This commit is contained in:
Stephen Dranger 2011-02-04 15:17:27 -06:00
parent 4b965814b9
commit 79e4de5710
26 changed files with 1269 additions and 699 deletions

5
TODO
View file

@ -1,5 +1,7 @@
Features: Features:
* Chat rooms/browser * Chat rooms/browser
* PESTERLOG: in convo window
* help button on quirks menu?
* tab recombining gives wrong window icon * tab recombining gives wrong window icon
* help menu -- about and forum * help menu -- about and forum
* profile switch should say current profile * profile switch should say current profile
@ -13,6 +15,7 @@ Features:
* tab right-click menu on tabbed convos * tab right-click menu on tabbed convos
* comment history (up button) * comment history (up button)
* page up/down scrolling * page up/down scrolling
* get rid of border on chat window?
* ctrl-tab should prefer new convos * ctrl-tab should prefer new convos
* More complex quirks: random, spelling, by-sound * More complex quirks: random, spelling, by-sound
* Implement TC options * Implement TC options
@ -28,7 +31,7 @@ Features:
* Theme checking * Theme checking
* don't clear new message when clicking away from tab? * don't clear new message when clicking away from tab?
* Spy mode * Spy mode
* Animated
* put code into separate files * put code into separate files
* hide offline chums * hide offline chums
* chum list groups * chum list groups

438
convo.py Normal file
View file

@ -0,0 +1,438 @@
from string import Template
import re
from PyQt4 import QtGui, QtCore
from dataobjs import PesterProfile, Mood
from generic import PesterIcon
from parsetools import escapeBrackets, convertTags
class PesterTabWindow(QtGui.QFrame):
def __init__(self, mainwindow, parent=None):
QtGui.QFrame.__init__(self, parent)
self.setFocusPolicy(QtCore.Qt.ClickFocus)
self.mainwindow = mainwindow
self.resize(*self.mainwindow.theme["convo/size"])
self.setStyleSheet(self.mainwindow.theme["convo/style"])
self.tabs = QtGui.QTabBar(self)
self.tabs.setTabsClosable(True)
self.connect(self.tabs, QtCore.SIGNAL('currentChanged(int)'),
self, QtCore.SLOT('changeTab(int)'))
self.connect(self.tabs, QtCore.SIGNAL('tabCloseRequested(int)'),
self, QtCore.SLOT('tabClose(int)'))
self.tabs.setShape(self.mainwindow.theme["convo/tabs/tabstyle"])
self.tabs.setStyleSheet("QTabBar::tab{ %s } QTabBar::tab:selected { %s }" % (self.mainwindow.theme["convo/tabs/style"], self.mainwindow.theme["convo/tabs/selectedstyle"]))
self.layout = QtGui.QVBoxLayout()
self.layout.setContentsMargins(0,0,0,0)
self.layout.addWidget(self.tabs)
self.setLayout(self.layout)
self.convos = {}
self.tabIndices = {}
self.currentConvo = None
self.changedTab = False
self.softclose = False
# get default tab color i guess
self.defaultTabTextColor = self.getTabTextColor()
def getTabTextColor(self):
# ugly, ugly hack
self.changedTab = True
i = self.tabs.addTab(".")
c = self.tabs.tabTextColor(i)
self.tabs.removeTab(i)
self.changedTab = False
return c
def addChat(self, convo):
self.convos[convo.chum.handle] = convo
# either addTab or setCurrentIndex will trigger changed()
newindex = self.tabs.addTab(convo.chum.handle)
self.tabIndices[convo.chum.handle] = newindex
self.tabs.setCurrentIndex(newindex)
self.tabs.setTabIcon(newindex, convo.chum.mood.icon(self.mainwindow.theme))
def showChat(self, handle):
tabi = self.tabIndices[handle]
if self.tabs.currentIndex() == tabi:
self.activateWindow()
self.raise_()
self.convos[handle].raiseChat()
else:
self.tabs.setCurrentIndex(tabi)
def convoHasFocus(self, convo):
if ((self.hasFocus() or self.tabs.hasFocus()) and
self.tabs.tabText(self.tabs.currentIndex()) == convo.chum.handle):
return True
def keyPressEvent(self, event):
keypress = event.key()
mods = event.modifiers()
if ((mods & QtCore.Qt.ControlModifier) and
keypress == QtCore.Qt.Key_Tab):
nexti = (self.tabIndices[self.currentConvo.chum.handle] + 1) % self.tabs.count()
self.tabs.setCurrentIndex(nexti)
def closeSoft(self):
self.softclose = True
self.close()
def updateBlocked(self, handle):
i = self.tabIndices[handle]
icon = QtGui.QIcon(self.mainwindow.theme["main/chums/moods/blocked/icon"])
self.tabs.setTabIcon(i, icon)
if self.tabs.currentIndex() == i:
self.setWindowIcon(icon)
def updateMood(self, handle, mood, unblocked=False):
i = self.tabIndices[handle]
if handle in self.mainwindow.config.getBlocklist() and not unblocked:
icon = QtGui.QIcon(self.mainwindow.theme["main/chums/moods/blocked/icon"])
else:
icon = mood.icon(self.mainwindow.theme)
self.tabs.setTabIcon(i, icon)
if self.tabs.currentIndex() == i:
self.setWindowIcon(icon)
def closeEvent(self, event):
if not self.softclose:
while self.tabs.count() > 0:
self.tabClose(0)
self.windowClosed.emit()
def focusInEvent(self, event):
# make sure we're not switching tabs!
i = self.tabs.tabAt(self.mapFromGlobal(QtGui.QCursor.pos()))
if i == -1:
i = self.tabs.currentIndex()
handle = unicode(self.tabs.tabText(i))
self.clearNewMessage(handle)
def convoHasFocus(self, handle):
i = self.tabIndices[handle]
if (self.tabs.currentIndex() == i and
(self.hasFocus() or self.tabs.hasFocus())):
return True
else:
return False
def notifyNewMessage(self, handle):
i = self.tabIndices[handle]
self.tabs.setTabTextColor(i, QtGui.QColor(self.mainwindow.theme["convo/tabs/newmsgcolor"]))
convo = self.convos[handle]
def func():
convo.showChat()
self.mainwindow.waitingMessages.addMessage(handle, func)
# set system tray
def clearNewMessage(self, handle):
try:
i = self.tabIndices[handle]
self.tabs.setTabTextColor(i, self.defaultTabTextColor)
except KeyError:
pass
self.mainwindow.waitingMessages.messageAnswered(handle)
def changeTheme(self, theme):
self.resize(*theme["convo/size"])
self.setStyleSheet(theme["convo/style"])
self.tabs.setShape(theme["convo/tabs/tabstyle"])
self.tabs.setStyleSheet("QTabBar::tabs{ %s }" % (theme["convo/tabs/style"]))
for c in self.convos.values():
tabi = self.tabIndices[c.chum.handle]
self.tabs.setTabIcon(tabi, c.chum.mood.icon(theme))
currenttabi = self.tabs.currentIndex()
if currenttabi >= 0:
currentHandle = unicode(self.tabs.tabText(self.tabs.currentIndex()))
self.setWindowIcon(self.convos[currentHandle].chum.mood.icon(theme))
self.defaultTabTextColor = self.getTabTextColor()
@QtCore.pyqtSlot(int)
def tabClose(self, i):
handle = unicode(self.tabs.tabText(i))
self.mainwindow.waitingMessages.messageAnswered(handle)
convo = self.convos[handle]
del self.convos[handle]
del self.tabIndices[handle]
self.tabs.removeTab(i)
for (h, j) in self.tabIndices.iteritems():
if j > i:
self.tabIndices[h] = j-1
self.layout.removeWidget(convo)
convo.close()
if self.tabs.count() == 0:
self.close()
return
if self.currentConvo == convo:
currenti = self.tabs.currentIndex()
currenth = unicode(self.tabs.tabText(currenti))
self.currentConvo = self.convos[currenth]
self.currentConvo.raiseChat()
@QtCore.pyqtSlot(int)
def changeTab(self, i):
if i < 0:
return
if self.changedTab:
self.changedTab = False
return
handle = unicode(self.tabs.tabText(i))
convo = self.convos[handle]
if self.currentConvo:
self.layout.removeWidget(self.currentConvo)
self.currentConvo = convo
self.layout.addWidget(convo)
self.setWindowIcon(convo.chum.mood.icon(self.mainwindow.theme))
self.setWindowTitle(convo.chum.handle)
self.activateWindow()
self.raise_()
convo.raiseChat()
windowClosed = QtCore.pyqtSignal()
class PesterText(QtGui.QTextEdit):
def __init__(self, theme, parent=None):
QtGui.QTextEdit.__init__(self, parent)
self.setStyleSheet(theme["convo/textarea/style"])
self.setReadOnly(True)
self.setMouseTracking(True)
def addMessage(self, text, chum):
color = chum.colorhtml()
systemColor = QtGui.QColor(self.parent().mainwindow.theme["convo/systemMsgColor"])
initials = chum.initials()
msg = unicode(text)
parent = self.parent()
window = parent.mainwindow
me = window.profile()
if msg == "PESTERCHUM:BEGIN":
parent.setChumOpen(True)
msg = chum.pestermsg(me, systemColor, window.theme["convo/text/beganpester"])
window.chatlog.log(chum.handle, convertTags(msg, "bbcode"))
self.append(convertTags(msg))
elif msg == "PESTERCHUM:CEASE":
parent.setChumOpen(False)
msg = chum.pestermsg(me, systemColor, window.theme["convo/text/ceasepester"])
window.chatlog.log(chum.handle, convertTags(msg, "bbcode"))
self.append(convertTags(msg))
elif msg == "PESTERCHUM:BLOCK":
msg = chum.pestermsg(me, systemColor, window.theme['convo/text/blocked'])
window.chatlog.log(chum.handle, convertTags(msg, "bbcode"))
self.append(convertTags(msg))
elif msg == "PESTERCHUM:UNBLOCK":
msg = chum.pestermsg(me, systemColor, window.theme['convo/text/unblocked'])
window.chatlog.log(chum.handle, convertTags(msg, "bbcode"))
self.append(convertTags(msg))
elif msg[0:3] == "/me" or msg[0:13] == "PESTERCHUM:ME":
if msg[0:3] == "/me":
start = 3
else:
start = 13
space = msg.find(" ")
msg = chum.memsg(systemColor, msg[start:space], msg[space:])
if chum is me:
window.chatlog.log(parent.chum.handle, convertTags(msg, "bbcode"))
else:
window.chatlog.log(chum.handle, convertTags(msg, "bbcode"))
self.append(convertTags(msg))
else:
if not parent.chumopen and chum is not me:
beginmsg = chum.pestermsg(me, systemColor, window.theme["convo/text/beganpester"])
parent.setChumOpen(True)
window.chatlog.log(chum.handle, convertTags(beginmsg, "bbcode"))
self.append(convertTags(beginmsg))
msg = "<c=%s>%s: %s</c>" % (color, initials, msg)
msg = escapeBrackets(msg)
self.append(convertTags(msg))
if chum is me:
window.chatlog.log(parent.chum.handle, convertTags(msg, "bbcode"))
else:
window.chatlog.log(chum.handle, convertTags(msg, "bbcode"))
def changeTheme(self, theme):
self.setStyleSheet(theme["convo/textarea/style"])
sb = self.verticalScrollBar()
sb.setMaximum(sb.maximum()+1000) # ugly hack but whatcha gonna do
sb.setValue(sb.maximum())
def focusInEvent(self, event):
self.parent().clearNewMessage()
QtGui.QTextEdit.focusInEvent(self, event)
def mousePressEvent(self, event):
url = self.anchorAt(event.pos())
if url != "":
QtGui.QDesktopServices.openUrl(QtCore.QUrl(url, QtCore.QUrl.TolerantMode))
QtGui.QTextEdit.mousePressEvent(self, event)
def mouseMoveEvent(self, event):
QtGui.QTextEdit.mouseMoveEvent(self, event)
if self.anchorAt(event.pos()):
if self.viewport().cursor().shape != QtCore.Qt.PointingHandCursor:
self.viewport().setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
else:
self.viewport().setCursor(QtGui.QCursor(QtCore.Qt.IBeamCursor))
class PesterInput(QtGui.QLineEdit):
def __init__(self, theme, parent=None):
QtGui.QLineEdit.__init__(self, parent)
self.setStyleSheet(theme["convo/input/style"])
def changeTheme(self, theme):
self.setStyleSheet(theme["convo/input/style"])
def focusInEvent(self, event):
self.parent().clearNewMessage()
QtGui.QLineEdit.focusInEvent(self, event)
class PesterConvo(QtGui.QFrame):
def __init__(self, chum, initiated, mainwindow, parent=None):
QtGui.QFrame.__init__(self, parent)
self.setFocusPolicy(QtCore.Qt.ClickFocus)
self.chum = chum
self.mainwindow = mainwindow
convo = self.mainwindow.theme["convo"]
self.resize(*convo["size"])
self.setStyleSheet(convo["style"])
self.setWindowIcon(chum.mood.icon(self.mainwindow.theme))
self.setWindowTitle(chum.handle)
t = Template(self.mainwindow.theme["convo/chumlabel/text"])
self.chumLabel = QtGui.QLabel(t.safe_substitute(handle=chum.handle), self)
self.chumLabel.setStyleSheet(self.mainwindow.theme["convo/chumlabel/style"])
self.chumLabel.setAlignment(self.aligndict["h"][self.mainwindow.theme["convo/chumlabel/align/h"]] | self.aligndict["v"][self.mainwindow.theme["convo/chumlabel/align/v"]])
self.chumLabel.setMaximumHeight(self.mainwindow.theme["convo/chumlabel/maxheight"])
self.chumLabel.setMinimumHeight(self.mainwindow.theme["convo/chumlabel/minheight"])
self.chumLabel.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Expanding))
self.textArea = PesterText(self.mainwindow.theme, self)
self.textInput = PesterInput(self.mainwindow.theme, self)
self.textInput.setFocus()
self.connect(self.textInput, QtCore.SIGNAL('returnPressed()'),
self, QtCore.SLOT('sentMessage()'))
self.layout = QtGui.QVBoxLayout()
self.layout.addWidget(self.chumLabel)
self.layout.addWidget(self.textArea)
self.layout.addWidget(self.textInput)
self.layout.setSpacing(0)
margins = self.mainwindow.theme["convo/margins"]
self.layout.setContentsMargins(margins["left"], margins["top"],
margins["right"], margins["bottom"])
self.setLayout(self.layout)
self.chumopen = False
if parent:
parent.addChat(self)
if initiated:
msg = self.mainwindow.profile().pestermsg(self.chum, QtGui.QColor(self.mainwindow.theme["convo/systemMsgColor"]), self.mainwindow.theme["convo/text/beganpester"])
self.setChumOpen(True)
self.textArea.append(convertTags(msg))
self.mainwindow.chatlog.log(self.chum.handle, convertTags(msg, "bbcode"))
self.newmessage = False
def updateMood(self, mood, unblocked=False):
if mood.name() == "offline" and self.chumopen == True and not unblocked:
msg = self.chum.pestermsg(self.mainwindow.profile(), QtGui.QColor(self.mainwindow.theme["convo/systemMsgColor"]), self.mainwindow.theme["convo/text/ceasepester"])
self.textArea.append(convertTags(msg))
self.mainwindow.chatlog.log(self.chum.handle, convertTags(msg, "bbcode"))
self.chumopen = False
if self.parent():
self.parent().updateMood(self.chum.handle, mood, unblocked)
else:
if self.chum.blocked(self.mainwindow.config) and not unblocked:
self.setWindowIcon(QtGui.QIcon(self.mainwindow.theme["main/chums/moods/blocked/icon"]))
else:
self.setWindowIcon(mood.icon(self.mainwindow.theme))
# print mood update?
def updateBlocked(self):
if self.parent():
self.parent().updateBlocked(self.chum.handle)
else:
self.setWindowIcon(QtGui.QIcon(self.mainwindow.theme["main/chums/moods/blocked/icon"]))
def updateColor(self, color):
self.chum.color = color
def addMessage(self, text, me=True):
if me:
chum = self.mainwindow.profile()
else:
chum = self.chum
self.notifyNewMessage()
self.textArea.addMessage(text, chum)
def notifyNewMessage(self):
# first see if this conversation HASS the focus
if not (self.hasFocus() or self.textArea.hasFocus() or
self.textInput.hasFocus() or
(self.parent() and self.parent().convoHasFocus(self.chum.handle))):
# ok if it has a tabconvo parent, send that the notify.
if self.parent():
self.parent().notifyNewMessage(self.chum.handle)
# if not change the window title and update system tray
else:
self.newmessage = True
self.setWindowTitle(self.chum.handle+"*")
def func():
self.showChat()
self.mainwindow.waitingMessages.addMessage(self.chum.handle, func)
def clearNewMessage(self):
if self.parent():
self.parent().clearNewMessage(self.chum.handle)
elif self.newmessage:
self.newmessage = False
self.setWindowTitle(self.chum.handle)
self.mainwindow.waitingMessages.messageAnswered(self.chum.handle)
# reset system tray
def focusInEvent(self, event):
self.clearNewMessage()
self.textInput.setFocus()
def raiseChat(self):
self.activateWindow()
self.raise_()
self.textInput.setFocus()
def showChat(self):
if self.parent():
self.parent().showChat(self.chum.handle)
self.raiseChat()
def closeEvent(self, event):
self.mainwindow.waitingMessages.messageAnswered(self.chum.handle)
self.windowClosed.emit(self.chum.handle)
def setChumOpen(self, o):
self.chumopen = o
def changeTheme(self, theme):
self.resize(*theme["convo/size"])
self.setStyleSheet(theme["convo/style"])
margins = theme["convo/margins"]
self.layout.setContentsMargins(margins["left"], margins["top"],
margins["right"], margins["bottom"])
self.setWindowIcon(self.chum.mood.icon(theme))
t = Template(self.mainwindow.theme["convo/chumlabel/text"])
self.chumLabel.setText(t.safe_substitute(handle=self.chum.handle))
self.chumLabel.setStyleSheet(theme["convo/chumlabel/style"])
self.chumLabel.setAlignment(self.aligndict["h"][self.mainwindow.theme["convo/chumlabel/align/h"]] | self.aligndict["v"][self.mainwindow.theme["convo/chumlabel/align/v"]])
self.chumLabel.setMaximumHeight(self.mainwindow.theme["convo/chumlabel/maxheight"])
self.chumLabel.setMinimumHeight(self.mainwindow.theme["convo/chumlabel/minheight"])
self.chumLabel.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Expanding))
self.textArea.changeTheme(theme)
self.textInput.changeTheme(theme)
@QtCore.pyqtSlot()
def sentMessage(self):
text = self.textInput.text()
if text == "":
return
# deal with quirks here
qtext = self.mainwindow.userprofile.quirks.apply(unicode(text))
text = QtCore.QString(qtext)
self.textInput.setText("")
self.addMessage(text, True)
# if ceased, rebegin
if not self.chumopen:
self.mainwindow.newConvoStarted.emit(QtCore.QString(self.chum.handle), True)
# convert color tags
text = convertTags(unicode(text), "ctag")
self.messageSent.emit(text, self.chum)
messageSent = QtCore.pyqtSignal(QtCore.QString, PesterProfile)
windowClosed = QtCore.pyqtSignal(QtCore.QString)
aligndict = {"h": {"center": QtCore.Qt.AlignHCenter,
"left": QtCore.Qt.AlignLeft,
"right": QtCore.Qt.AlignRight },
"v": {"center": QtCore.Qt.AlignVCenter,
"top": QtCore.Qt.AlignTop,
"bottom": QtCore.Qt.AlignBottom } }

BIN
convo.pyc Normal file

Binary file not shown.

View file

@ -6,9 +6,10 @@ from generic import PesterIcon
class Mood(object): class Mood(object):
moods = ["chummy", "rancorous", "offline", "pleasant", "distraught", moods = ["chummy", "rancorous", "offline", "pleasant", "distraught",
"unruly", "smooth", "ecstatic", "relaxed", "discontent", "pranky", "smooth", "ecstatic", "relaxed", "discontent",
"devious", "sleek", "detestful", "mirthful", "manipulative", "devious", "sleek", "detestful", "mirthful", "manipulative",
"vigorous", "perky", "acceptant", "protective"] "vigorous", "perky", "acceptant", "protective", "mystified",
"amazed", "insolent", "bemused" ]
def __init__(self, mood): def __init__(self, mood):
if type(mood) is int: if type(mood) is int:
self.mood = mood self.mood = mood
@ -66,14 +67,23 @@ class pesterQuirks(object):
def plainList(self): def plainList(self):
return [q.quirk for q in self.quirklist] return [q.quirk for q in self.quirklist]
def apply(self, string): def apply(self, string):
# don't quirk /me commands
if string[0:3] == "/me":
space = string.find(" ")
cmd = string[0:space]
string = string[space:]
else:
cmd = ""
presuffix = [q for q in self.quirklist if presuffix = [q for q in self.quirklist if
q.type=='prefix' or q.type=='suffix'] q.type=='prefix' or q.type=='suffix']
replace = [q for q in self.quirklist if replace = [q for q in self.quirklist if
q.type=='replace' or q.type=='regexp'] q.type=='replace' or q.type=='regexp']
for r in replace: for r in replace:
string = r.apply(string) string = r.apply(string)
for ps in presuffix: if not cmd:
string = ps.apply(string) for ps in presuffix:
string = ps.apply(string)
string = cmd+string
return string return string
def __iter__(self): def __iter__(self):

BIN
dataobjs.pyc Normal file

Binary file not shown.

View file

@ -1 +1 @@
{"macruralAlchemist": {"color": "#700000", "handle": "macruralAlchemist", "mood": "offline"}, "agogPorphyry": {"color": "#522d80", "handle": "agogPorphyry", "mood": "offline"}, "fireSwallow": {"color": "#80bb9a", "handle": "fireSwallow", "mood": "offline"}, "aquaMarinist": {"color": "#00caca", "handle": "aquaMarinist", "mood": "offline"}, "nitroZealist": {"color": "#ff3737", "handle": "nitroZealist", "mood": "offline"}, "superGhost": {"color": "#800564", "handle": "superGhost", "mood": "offline"}, "tentacleTherapist": {"color": "#cc66ff", "handle": "tentacleTherapist", "mood": "offline"}, "aquaticMarinist": {"color": "#00caca", "handle": "aquaticMarinist", "mood": "offline"}, "marineAquist": {"color": "#00caca", "handle": "marineAquist", "mood": "offline"}, "captainCaveman": {"color": "#7c414e", "handle": "captainCaveman", "mood": "offline"}, "gamblingGenocider": {"color": "#00ff00", "handle": "gamblingGenocider", "mood": "offline"}, "mechanicalSpectacle": {"color": "#0000ff", "handle": "mechanicalSpectacle", "mood": "offline"}, "absoluteTranquility": {"color": "#000033", "handle": "absoluteTranquility", "mood": "offline"}, "centaursTesticle": {"color": "#000056", "handle": "centaursTesticle", "mood": "offline"}, "gardenGnostic": {"color": "#00ff00", "handle": "gardenGnostic", "mood": "offline"}, "unknownTraveler": {"color": "#006666", "handle": "unknownTraveler", "mood": "offline"}, "schlagzeugGator": {"color": "#61821f", "handle": "schlagzeugGator", "mood": "offline"}} {"macruralAlchemist": {"color": "#700000", "handle": "macruralAlchemist", "mood": "offline"}, "agogPorphyry": {"color": "#522d80", "handle": "agogPorphyry", "mood": "offline"}, "fireSwallow": {"color": "#80bb9a", "handle": "fireSwallow", "mood": "offline"}, "aquaMarinist": {"color": "#00caca", "handle": "aquaMarinist", "mood": "offline"}, "nitroZealist": {"color": "#ff3737", "handle": "nitroZealist", "mood": "offline"}, "superGhost": {"color": "#800564", "handle": "superGhost", "mood": "offline"}, "tentacleTherapist": {"color": "#cc66ff", "handle": "tentacleTherapist", "mood": "offline"}, "gardenGnostic": {"color": "#00ff00", "handle": "gardenGnostic", "mood": "offline"}, "aquaticMarinist": {"color": "#00caca", "handle": "aquaticMarinist", "mood": "offline"}, "captainCaveman": {"color": "#7c414e", "handle": "captainCaveman", "mood": "offline"}, "greenZephyr": {"color": "#00ca40", "handle": "greenZephyr", "mood": "offline"}, "pesterClient394": {"color": "#ff3737", "handle": "pesterClient394", "mood": "offline"}, "gamblingGenocider": {"color": "#00ff00", "handle": "gamblingGenocider", "mood": "offline"}, "mechanicalSpectacle": {"color": "#0000ff", "handle": "mechanicalSpectacle", "mood": "offline"}, "elegantDiversion": {"color": "#12b40d", "handle": "elegantDiversion", "mood": "offline"}, "absoluteTranquility": {"color": "#000033", "handle": "absoluteTranquility", "mood": "offline"}, "centaursTesticle": {"color": "#000056", "handle": "centaursTesticle", "mood": "offline"}, "schlagzeugGator": {"color": "#61821f", "handle": "schlagzeugGator", "mood": "offline"}, "unknownTraveler": {"color": "#006666", "handle": "unknownTraveler", "mood": "offline"}, "remoteBloodbath": {"color": "#c70000", "handle": "remoteBloodbath", "mood": "offline"}, "marineAquist": {"color": "#00caca", "handle": "marineAquist", "mood": "offline"}}

125
memos.py Normal file
View file

@ -0,0 +1,125 @@
from string import Template
import re
from PyQt4 import QtGui, QtCore
from dataobjs import PesterProfile, Mood
from generic import PesterIcon
from convo import PesterConvo, PesterInput, PesterText
class MemoText(PesterText):
def __init__(self, theme, parent=None):
QtGui.QTextEdit.__init__(self, parent)
self.setStyleSheet(theme["memos/textarea/style"])
self.setReadOnly(True)
self.setMouseTracking(True)
def addMessage(self, text, chum):
pass
def changeTheme(self):
pass
class MemoInput(PesterInput):
def __init__(self, theme, parent=None):
QtGui.QLineEdit.__init__(self, parent)
self.setStyleSheet(theme["memos/input/style"])
def changeTheme(self, theme):
self.setStyleSheet(theme["memos/input/style"])
class PesterMemo(PesterConvo):
def __init__(self, channel, mainwindow, parent=None):
QtGui.QFrame.__init__(self, parent)
self.channel = channel
self.mainwindow = mainwindow
self.setWindowTitle(channel)
self.channelLabel = QtGui.QLabel(self)
self.channelLabel.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Expanding))
self.textArea = MemoText(self.mainwindow.theme, self)
self.textInput = MemoInput(self.mainwindow.theme, self)
self.textInput.setFocus()
self.userlist = QtGui.QListWidget(self)
self.userlist.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Expanding))
self.timeslider = QtGui.QSlider(QtCore.Qt.Vertical, self)
self.timeinput = QtGui.QLineEdit(self)
self.initTheme(self.mainwindow.theme)
# connect
layout_0 = QtGui.QVBoxLayout()
layout_0.addWidget(self.channelLabel)
layout_0.addWidget(self.textArea)
layout_0.addWidget(self.textInput)
layout_1 = QtGui.QGridLayout()
layout_1.addWidget(self.timeslider, 0, 1, QtCore.Qt.AlignHCenter)
layout_1.addWidget(self.timeinput, 1, 0, 1, 3)
self.layout = QtGui.QHBoxLayout()
self.setLayout(self.layout)
self.layout.addLayout(layout_0)
self.layout.addWidget(self.userlist)
self.layout.addLayout(layout_1)
self.layout.setSpacing(0)
margins = self.mainwindow.theme["memos/margins"]
self.layout.setContentsMargins(margins["left"], margins["top"],
margins["right"], margins["bottom"])
#if parent:
# parent.addChat(self)
self.newmessage = False
def updateMood(self):
pass
def updateBlocked(self):
pass
def updateColor(self):
pass
def addMessage(self):
pass
def notifyNewMessage(self):
pass
def clearNewMessage(self):
pass
def initTheme(self, theme):
memo = theme["memos"]
self.resize(*memo["size"])
self.setStyleSheet(memo["style"])
self.setWindowIcon(PesterIcon(theme["memos/memoicon"]))
t = Template(theme["memos/label/text"])
self.channelLabel.setText(t.safe_substitute(channel=self.channel))
self.channelLabel.setStyleSheet(theme["memos/label/style"])
self.channelLabel.setAlignment(self.aligndict["h"][theme["memos/label/align/h"]] | self.aligndict["v"][theme["memos/label/align/v"]])
self.channelLabel.setMaximumHeight(theme["memos/label/maxheight"])
self.channelLabel.setMinimumHeight(theme["memos/label/minheight"])
self.userlist.setStyleSheet(theme["main/chums/style"])
self.userlist.setFixedWidth(theme["memos/userlist/width"])
self.timeinput.setFixedWidth(theme["memos/time/text/width"])
self.timeinput.setStyleSheet(theme["memos/time/text/style"])
slidercss = "QSlider { %s } QSlider::groove { %s } QSlider::handle { %s }" % (theme["memos/time/slider/style"], theme["memos/time/slider/groove"], theme["memos/time/slider/handle"])
self.timeslider.setStyleSheet(slidercss)
def changeTheme(self, theme):
self.initTheme(theme)
self.textArea.changeTheme(theme)
self.textInput.changeTheme(theme)
def sentMessage(self):
pass
def closeEvent(self, event):
self.mainwindow.waitingMessages.messageAnswered(self.channel)
# self.windowClosed.emit(self.chum.handle)
# messageSent - signal -> sendMessage -> sendMessage(Memo)
# windowClosed - signal -> closeMemo
# self.textInput
# self.textArea

BIN
memos.pyc Normal file

Binary file not shown.

View file

@ -376,7 +376,6 @@ class PesterUserlist(QtGui.QDialog):
self.userarea.setStyleSheet(theme["main/chums/style"]) self.userarea.setStyleSheet(theme["main/chums/style"])
self.addChumAction.setText(theme["main/menus/rclickchumlist/addchum"]) self.addChumAction.setText(theme["main/menus/rclickchumlist/addchum"])
for item in [self.userarea.item(i) for i in range(0, self.userarea.count())]: for item in [self.userarea.item(i) for i in range(0, self.userarea.count())]:
print item.text()
item.setTextColor(QtGui.QColor(theme["main/chums/userlistcolor"])) item.setTextColor(QtGui.QColor(theme["main/chums/userlistcolor"]))
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
@ -388,3 +387,66 @@ class PesterUserlist(QtGui.QDialog):
addChum = QtCore.pyqtSignal(QtCore.QString) addChum = QtCore.pyqtSignal(QtCore.QString)
class PesterMemoList(QtGui.QDialog):
def __init__(self, parent):
QtGui.QDialog.__init__(self, parent)
self.setModal(False)
self.theme = parent.theme
self.mainwindow = parent
self.setStyleSheet(self.theme["main/defaultwindow/style"])
self.resize(200, 300)
self.label = QtGui.QLabel("MEMOS")
self.channelarea = RightClickList(self)
self.channelarea.setStyleSheet(self.theme["main/chums/style"])
self.channelarea.optionsMenu = QtGui.QMenu(self)
self.connect(self.channelarea,
QtCore.SIGNAL('itemActivated(QListWidgetItem *)'),
self, QtCore.SLOT('joinActivatedMemo(QListWidgetItem *)'))
self.orjoinlabel = QtGui.QLabel("OR MAKE A NEW MEMO:")
self.newmemo = QtGui.QLineEdit(self)
self.cancel = QtGui.QPushButton("CANCEL", self)
self.connect(self.cancel, QtCore.SIGNAL('clicked()'),
self, QtCore.SLOT('reject()'))
self.join = QtGui.QPushButton("JOIN", self)
self.join.setDefault(True)
self.connect(self.join, QtCore.SIGNAL('clicked()'),
self, QtCore.SLOT('accept()'))
layout_ok = QtGui.QHBoxLayout()
layout_ok.addWidget(self.cancel)
layout_ok.addWidget(self.join)
layout_0 = QtGui.QVBoxLayout()
layout_0.addWidget(self.label)
layout_0.addWidget(self.channelarea)
layout_0.addWidget(self.orjoinlabel)
layout_0.addWidget(self.newmemo)
layout_0.addLayout(layout_ok)
self.setLayout(layout_0)
def newmemoname(self):
return self.newmemo.text()
def selectedmemo(self):
return self.channelarea.currentItem()
def updateChannels(self, channels):
for c in channels:
item = QtGui.QListWidgetItem(c[1:])
item.setTextColor(QtGui.QColor(self.theme["main/chums/userlistcolor"]))
item.setIcon(QtGui.QIcon(self.theme["memos/memoicon"]))
self.channelarea.addItem(item)
def updateTheme(self, theme):
self.theme = theme
self.setStyleSheet(theme["main/defaultwindow/style"])
for item in [self.userarea.item(i) for i in range(0, self.channelarea.count())]:
item.setTextColor(QtGui.QColor(theme["main/chums/userlistcolor"]))
item.setIcon(QtGui.QIcon(theme["memos/memoicon"]))
@QtCore.pyqtSlot(QtGui.QListWidgetItem)
def joinActivatedMemo(self, item):
self.channelarea.setCurrentItem(item)
self.accept()

BIN
menus.pyc Normal file

Binary file not shown.

View file

@ -35,6 +35,9 @@ def names(cli, *channels):
if len(msglist) > 0: if len(msglist) > 0:
cli.send("NAMES %s" % (",".join(msglist))) cli.send("NAMES %s" % (",".join(msglist)))
def channel_list(cli):
cli.send("LIST")
def msgrandom(cli, choices, dest, user=None): def msgrandom(cli, choices, dest, user=None):
o = "%s: " % user if user else "" o = "%s: " % user if user else ""
o += random.choice(choices) o += random.choice(choices)

Binary file not shown.

89
parsetools.py Normal file
View file

@ -0,0 +1,89 @@
import re
from PyQt4 import QtGui
_ctag_begin = re.compile(r'<c=(.*?)>')
_ctag_rgb = re.compile(r'\d+,\d+,\d+')
_urlre = re.compile(r"(?i)(http://[^\s<]+)")
def convertTags(string, format="html"):
if format not in ["html", "bbcode", "ctag"]:
raise ValueError("Color format not recognized")
def colorrepfunc(matchobj):
color = matchobj.group(1)
if _ctag_rgb.match(color) is not None:
if format=='ctag':
return "<c=%s,%s,%s>"
try:
qc = QtGui.QColor(*[int(c) for c in color.split(",")])
except ValueError:
qc = QtGui.QColor("black")
else:
qc = QtGui.QColor(color)
if not qc.isValid():
qc = QtGui.QColor("black")
if format == "html":
return '<span style="color:%s">' % (qc.name())
elif format == "bbcode":
return '[color=%s]' % (qc.name())
elif format == "ctag":
(r,g,b,a) = qc.getRgb()
return '<c=%s,%s,%s>' % (r,g,b)
string = _ctag_begin.sub(colorrepfunc, string)
endtag = {"html": "</span>", "bbcode": "[/color]", "ctag": "</c>"}
string = string.replace("</c>", endtag[format])
def urlrep(matchobj):
if format=="html":
return "<a href='%s'>%s</a>" % (matchobj.group(1).replace("&amp;", "&"), matchobj.group(1))
elif format=="bbcode":
return "[url]%s[/url]" % (matchobj.group(1).replace("&amp;", "&"))
elif format=="ctag":
return matchobj.group(1)
string = _urlre.sub(urlrep, string)
return string
def escapeBrackets(string):
class beginTag(object):
def __init__(self, tag):
self.tag = tag
class endTag(object):
pass
newlist = []
begintagpos = [(m.start(), m.end()) for m in _ctag_begin.finditer(string)]
lasti = 0
for (s, e) in begintagpos:
newlist.append(string[lasti:s])
newlist.append(beginTag(string[s:e]))
lasti = e
if lasti < len(string):
newlist.append(string[lasti:])
tmp = []
for o in newlist:
if type(o) is not beginTag:
l = o.split("</c>")
tmp.append(l[0])
l = l[1:]
for item in l:
tmp.append(endTag())
tmp.append(item)
else:
tmp.append(o)
btlen = 0
etlen = 0
retval = ""
newlist = tmp
for o in newlist:
if type(o) is beginTag:
retval += o.tag.replace("&", "&amp;")
btlen +=1
elif type(o) is endTag:
if etlen >= btlen:
continue
else:
retval += "</c>"
etlen += 1
else:
retval += o.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
if btlen > etlen:
for i in range(0, btlen-etlen):
retval += "</c>"
return retval

BIN
parsetools.pyc Normal file

Binary file not shown.

View file

@ -1 +1 @@
{"tabs": true, "chums": ["aquaMarinist", "marineAquist", "unknownTraveler", "tentacleTherapist", "macruralAlchemist", "vaginalEngineer", "mechanicalSpectacle", "carcinoGeneticist", "schlagzeugGator", "gamblingGenocider", "gardenGnostic", "superGhost", "centaursTesticle", "arachnidsGrip", "fireSwallow", "grimAuxiliatrix"], "defaultprofile": "ghostDunk", "block": []} {"tabs": false, "chums": ["aquaMarinist", "marineAquist", "unknownTraveler", "tentacleTherapist", "macruralAlchemist", "vaginalEngineer", "mechanicalSpectacle", "carcinoGeneticist", "schlagzeugGator", "gamblingGenocider", "gardenGnostic", "superGhost", "centaursTesticle", "arachnidsGrip", "fireSwallow", "grimAuxiliatrix", "remoteBloodbath", "nitroZealist", "greenZephyr"], "defaultprofile": "testProfile", "block": []}

View file

@ -13,95 +13,16 @@ import re
from PyQt4 import QtGui, QtCore from PyQt4 import QtGui, QtCore
import pygame import pygame
from pestermenus import PesterChooseQuirks, PesterChooseTheme, \ from menus import PesterChooseQuirks, PesterChooseTheme, \
PesterChooseProfile, PesterOptions, PesterUserlist PesterChooseProfile, PesterOptions, PesterUserlist, PesterMemoList
from pesterdata import PesterProfile, Mood, pesterQuirk, pesterQuirks from dataobjs import PesterProfile, Mood, pesterQuirk, pesterQuirks
from generic import PesterIcon, RightClickList, MultiTextDialog from generic import PesterIcon, RightClickList, MultiTextDialog
from convo import PesterTabWindow, PesterText, PesterInput, PesterConvo
from parsetools import convertTags
from memos import PesterMemo
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
_ctag_begin = re.compile(r'<c=(.*?)>')
_ctag_rgb = re.compile(r'\d+,\d+,\d+')
_urlre = re.compile(r"(?i)(http://[^\s<]+)")
def convertTags(string, format="html"):
if format not in ["html", "bbcode", "ctag"]:
raise ValueError("Color format not recognized")
def colorrepfunc(matchobj):
color = matchobj.group(1)
if _ctag_rgb.match(color) is not None:
if format=='ctag':
return "<c=%s,%s,%s>"
try:
qc = QtGui.QColor(*[int(c) for c in color.split(",")])
except ValueError:
qc = QtGui.QColor("black")
else:
qc = QtGui.QColor(color)
if not qc.isValid():
qc = QtGui.QColor("black")
if format == "html":
return '<span style="color:%s">' % (qc.name())
elif format == "bbcode":
return '[color=%s]' % (qc.name())
elif format == "ctag":
(r,g,b,a) = qc.getRgb()
return '<c=%s,%s,%s>' % (r,g,b)
string = _ctag_begin.sub(colorrepfunc, string)
endtag = {"html": "</span>", "bbcode": "[/color]", "ctag": "</c>"}
string = string.replace("</c>", endtag[format])
urlrep = {"html": r"<a href='\1'>\1</a>",
"bbcode": r"[url]\1[/url]",
"ctag": r"\1" }
string = _urlre.sub(urlrep[format], string)
return string
def escapeBrackets(string):
class beginTag(object):
def __init__(self, tag):
self.tag = tag
class endTag(object):
pass
newlist = []
begintagpos = [(m.start(), m.end()) for m in _ctag_begin.finditer(string)]
lasti = 0
for (s, e) in begintagpos:
newlist.append(string[lasti:s])
newlist.append(beginTag(string[s:e]))
lasti = e
if lasti < len(string):
newlist.append(string[lasti:])
tmp = []
for o in newlist:
if type(o) is not beginTag:
l = o.split("</c>")
tmp.append(l[0])
l = l[1:]
for item in l:
tmp.append(endTag())
tmp.append(item)
else:
tmp.append(o)
btlen = 0
etlen = 0
retval = ""
newlist = tmp
for o in newlist:
if type(o) is beginTag:
retval += o.tag.replace("&", "&amp;")
btlen +=1
elif type(o) is endTag:
if etlen >= btlen:
continue
else:
retval += "</c>"
etlen += 1
else:
retval += o.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
if btlen > etlen:
for i in range(0, btlen-etlen):
retval += "</c>"
return retval
class waitingMessageHolder(object): class waitingMessageHolder(object):
def __init__(self, mainwindow, **msgfuncs): def __init__(self, mainwindow, **msgfuncs):
@ -140,7 +61,7 @@ class PesterLog(object):
self.convos = {} self.convos = {}
def log(self, handle, msg): def log(self, handle, msg):
if not self.convos.has_key(handle): if not self.convos.has_key(handle):
time = datetime.now().strftime("%Y-%m-%d.%H:%M") time = datetime.now().strftime("%Y-%m-%d.%H.%M.txt")
if not os.path.exists("logs/%s/%s" % (self.handle, handle)): if not os.path.exists("logs/%s/%s" % (self.handle, handle)):
os.mkdir("logs/%s/%s" % (self.handle, handle)) os.mkdir("logs/%s/%s" % (self.handle, handle))
fp = open("logs/%s/%s/%s.%s" % (self.handle, handle, handle, time), 'a') fp = open("logs/%s/%s/%s.%s" % (self.handle, handle, handle, time), 'a')
@ -652,422 +573,6 @@ class MovingWindow(QtGui.QFrame):
self.update() self.update()
self.moving = None self.moving = None
class PesterTabWindow(QtGui.QFrame):
def __init__(self, mainwindow, parent=None):
QtGui.QFrame.__init__(self, parent)
self.setFocusPolicy(QtCore.Qt.ClickFocus)
self.mainwindow = mainwindow
self.resize(*self.mainwindow.theme["convo/size"])
self.setStyleSheet(self.mainwindow.theme["convo/style"])
self.tabs = QtGui.QTabBar(self)
self.tabs.setTabsClosable(True)
self.connect(self.tabs, QtCore.SIGNAL('currentChanged(int)'),
self, QtCore.SLOT('changeTab(int)'))
self.connect(self.tabs, QtCore.SIGNAL('tabCloseRequested(int)'),
self, QtCore.SLOT('tabClose(int)'))
self.tabs.setShape(self.mainwindow.theme["convo/tabs/tabstyle"])
self.tabs.setStyleSheet("QTabBar::tab{ %s } QTabBar::tab:selected { %s }" % (self.mainwindow.theme["convo/tabs/style"], self.mainwindow.theme["convo/tabs/selectedstyle"]))
self.layout = QtGui.QVBoxLayout()
self.layout.setContentsMargins(0,0,0,0)
self.layout.addWidget(self.tabs)
self.setLayout(self.layout)
self.convos = {}
self.tabIndices = {}
self.currentConvo = None
self.changedTab = False
self.softclose = False
# get default tab color i guess
self.defaultTabTextColor = self.getTabTextColor()
def getTabTextColor(self):
# ugly, ugly hack
self.changedTab = True
i = self.tabs.addTab(".")
c = self.tabs.tabTextColor(i)
self.tabs.removeTab(i)
self.changedTab = False
return c
def addChat(self, convo):
self.convos[convo.chum.handle] = convo
# either addTab or setCurrentIndex will trigger changed()
newindex = self.tabs.addTab(convo.chum.handle)
self.tabIndices[convo.chum.handle] = newindex
self.tabs.setCurrentIndex(newindex)
self.tabs.setTabIcon(newindex, convo.chum.mood.icon(self.mainwindow.theme))
def showChat(self, handle):
tabi = self.tabIndices[handle]
if self.tabs.currentIndex() == tabi:
self.activateWindow()
self.raise_()
self.convos[handle].raiseChat()
else:
self.tabs.setCurrentIndex(tabi)
def convoHasFocus(self, convo):
if ((self.hasFocus() or self.tabs.hasFocus()) and
self.tabs.tabText(self.tabs.currentIndex()) == convo.chum.handle):
return True
def keyPressEvent(self, event):
keypress = event.key()
mods = event.modifiers()
if ((mods & QtCore.Qt.ControlModifier) and
keypress == QtCore.Qt.Key_Tab):
nexti = (self.tabIndices[self.currentConvo.chum.handle] + 1) % self.tabs.count()
self.tabs.setCurrentIndex(nexti)
def closeSoft(self):
self.softclose = True
self.close()
def updateBlocked(self, handle):
i = self.tabIndices[handle]
icon = QtGui.QIcon(self.mainwindow.theme["main/chums/moods/blocked/icon"])
self.tabs.setTabIcon(i, icon)
if self.tabs.currentIndex() == i:
self.setWindowIcon(icon)
def updateMood(self, handle, mood, unblocked=False):
i = self.tabIndices[handle]
if handle in self.mainwindow.config.getBlocklist() and not unblocked:
icon = QtGui.QIcon(self.mainwindow.theme["main/chums/moods/blocked/icon"])
else:
icon = mood.icon(self.mainwindow.theme)
self.tabs.setTabIcon(i, icon)
if self.tabs.currentIndex() == i:
self.setWindowIcon(icon)
def closeEvent(self, event):
if not self.softclose:
while self.tabs.count() > 0:
self.tabClose(0)
self.windowClosed.emit()
def focusInEvent(self, event):
# make sure we're not switching tabs!
i = self.tabs.tabAt(self.mapFromGlobal(QtGui.QCursor.pos()))
if i == -1:
i = self.tabs.currentIndex()
handle = unicode(self.tabs.tabText(i))
self.clearNewMessage(handle)
def convoHasFocus(self, handle):
i = self.tabIndices[handle]
if (self.tabs.currentIndex() == i and
(self.hasFocus() or self.tabs.hasFocus())):
return True
else:
return False
def notifyNewMessage(self, handle):
i = self.tabIndices[handle]
self.tabs.setTabTextColor(i, QtGui.QColor(self.mainwindow.theme["convo/tabs/newmsgcolor"]))
convo = self.convos[handle]
def func():
convo.showChat()
self.mainwindow.waitingMessages.addMessage(handle, func)
# set system tray
def clearNewMessage(self, handle):
try:
i = self.tabIndices[handle]
except KeyError:
pass
self.tabs.setTabTextColor(i, self.defaultTabTextColor)
self.mainwindow.waitingMessages.messageAnswered(handle)
def changeTheme(self, theme):
self.resize(*theme["convo/size"])
self.setStyleSheet(theme["convo/style"])
self.tabs.setShape(theme["convo/tabs/tabstyle"])
self.tabs.setStyleSheet("QTabBar::tabs{ %s }" % (theme["convo/tabs/style"]))
for c in self.convos.values():
tabi = self.tabIndices[c.chum.handle]
self.tabs.setTabIcon(tabi, c.chum.mood.icon(theme))
currenttabi = self.tabs.currentIndex()
if currenttabi >= 0:
currentHandle = unicode(self.tabs.tabText(self.tabs.currentIndex()))
self.setWindowIcon(self.convos[currentHandle].chum.mood.icon(theme))
self.defaultTabTextColor = self.getTabTextColor()
@QtCore.pyqtSlot(int)
def tabClose(self, i):
handle = unicode(self.tabs.tabText(i))
self.mainwindow.waitingMessages.messageAnswered(handle)
convo = self.convos[handle]
del self.convos[handle]
del self.tabIndices[handle]
self.tabs.removeTab(i)
for (h, j) in self.tabIndices.iteritems():
if j > i:
self.tabIndices[h] = j-1
self.layout.removeWidget(convo)
convo.close()
if self.tabs.count() == 0:
self.close()
return
if self.currentConvo == convo:
currenti = self.tabs.currentIndex()
currenth = unicode(self.tabs.tabText(currenti))
self.currentConvo = self.convos[currenth]
self.currentConvo.raiseChat()
@QtCore.pyqtSlot(int)
def changeTab(self, i):
if i < 0:
return
if self.changedTab:
self.changedTab = False
return
handle = unicode(self.tabs.tabText(i))
convo = self.convos[handle]
if self.currentConvo:
self.layout.removeWidget(self.currentConvo)
self.currentConvo = convo
self.layout.addWidget(convo)
self.setWindowIcon(convo.chum.mood.icon(self.mainwindow.theme))
self.setWindowTitle(convo.chum.handle)
self.activateWindow()
self.raise_()
convo.raiseChat()
windowClosed = QtCore.pyqtSignal()
class PesterText(QtGui.QTextEdit):
def __init__(self, theme, parent=None):
QtGui.QTextEdit.__init__(self, parent)
self.setStyleSheet(theme["convo/textarea/style"])
self.setReadOnly(True)
def addMessage(self, text, chum):
color = chum.colorhtml()
systemColor = QtGui.QColor(self.parent().mainwindow.theme["convo/systemMsgColor"])
initials = chum.initials()
msg = unicode(text)
parent = self.parent()
window = parent.mainwindow
me = window.profile()
if msg == "PESTERCHUM:BEGIN":
parent.setChumOpen(True)
msg = chum.pestermsg(me, systemColor, window.theme["convo/text/beganpester"])
window.chatlog.log(chum.handle, convertTags(msg, "bbcode"))
self.append(convertTags(msg))
elif msg == "PESTERCHUM:CEASE":
parent.setChumOpen(False)
msg = chum.pestermsg(me, systemColor, window.theme["convo/text/ceasepester"])
window.chatlog.log(chum.handle, convertTags(msg, "bbcode"))
self.append(convertTags(msg))
elif msg == "PESTERCHUM:BLOCK":
msg = chum.pestermsg(me, systemColor, window.theme['convo/text/blocked'])
window.chatlog.log(chum.handle, convertTags(msg, "bbcode"))
self.append(convertTags(msg))
elif msg == "PESTERCHUM:UNBLOCK":
msg = chum.pestermsg(me, systemColor, window.theme['convo/text/unblocked'])
window.chatlog.log(chum.handle, convertTags(msg, "bbcode"))
self.append(convertTags(msg))
elif msg[0:3] == "/me" or msg[0:13] == "PESTERCHUM:ME":
if msg[0:3] == "/me":
start = 3
else:
start = 13
space = msg.find(" ")
msg = chum.memsg(systemColor, msg[start:space], msg[space:])
if chum is me:
window.chatlog.log(parent.chum.handle, convertTags(msg, "bbcode"))
else:
window.chatlog.log(chum.handle, convertTags(msg, "bbcode"))
self.append(convertTags(msg))
else:
if not parent.chumopen and chum is not me:
beginmsg = chum.pestermsg(me, systemColor, window.theme["convo/text/beganpester"])
parent.setChumOpen(True)
window.chatlog.log(chum.handle, convertTags(beginmsg, "bbcode"))
self.append(convertTags(beginmsg))
msg = "<c=%s>%s: %s</c>" % (color, initials, msg)
msg = escapeBrackets(msg)
self.append(convertTags(msg))
if chum is me:
window.chatlog.log(parent.chum.handle, convertTags(msg, "bbcode"))
else:
window.chatlog.log(chum.handle, convertTags(msg, "bbcode"))
def changeTheme(self, theme):
self.setStyleSheet(theme["convo/textarea/style"])
sb = self.verticalScrollBar()
sb.setMaximum(sb.maximum()+1000) # ugly hack but whatcha gonna do
sb.setValue(sb.maximum())
def focusInEvent(self, event):
self.parent().clearNewMessage()
QtGui.QTextEdit.focusInEvent(self, event)
def mousePressEvent(self, event):
url = self.anchorAt(event.pos())
if url != "":
QtGui.QDesktopServices.openUrl(QtCore.QUrl(url, QtCore.QUrl.TolerantMode))
QtGui.QTextEdit.mousePressEvent(self, event)
class PesterInput(QtGui.QLineEdit):
def __init__(self, theme, parent=None):
QtGui.QLineEdit.__init__(self, parent)
self.setStyleSheet(theme["convo/input/style"])
def changeTheme(self, theme):
self.setStyleSheet(theme["convo/input/style"])
def focusInEvent(self, event):
self.parent().clearNewMessage()
QtGui.QLineEdit.focusInEvent(self, event)
class PesterConvo(QtGui.QFrame):
def __init__(self, chum, initiated, mainwindow, parent=None):
QtGui.QFrame.__init__(self, parent)
self.setFocusPolicy(QtCore.Qt.ClickFocus)
self.chum = chum
self.mainwindow = mainwindow
convo = self.mainwindow.theme["convo"]
self.resize(*convo["size"])
self.setStyleSheet(convo["style"])
self.setWindowIcon(chum.mood.icon(self.mainwindow.theme))
self.setWindowTitle(chum.handle)
t = Template(self.mainwindow.theme["convo/chumlabel/text"])
self.chumLabel = QtGui.QLabel(t.safe_substitute(handle=chum.handle), self)
self.chumLabel.setStyleSheet(self.mainwindow.theme["convo/chumlabel/style"])
self.chumLabel.setAlignment(self.aligndict["h"][self.mainwindow.theme["convo/chumlabel/align/h"]] | self.aligndict["v"][self.mainwindow.theme["convo/chumlabel/align/v"]])
self.chumLabel.setMaximumHeight(self.mainwindow.theme["convo/chumlabel/maxheight"])
self.chumLabel.setMinimumHeight(self.mainwindow.theme["convo/chumlabel/minheight"])
self.chumLabel.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Expanding))
self.textArea = PesterText(self.mainwindow.theme, self)
self.textInput = PesterInput(self.mainwindow.theme, self)
self.textInput.setFocus()
self.connect(self.textInput, QtCore.SIGNAL('returnPressed()'),
self, QtCore.SLOT('sentMessage()'))
self.layout = QtGui.QVBoxLayout()
self.layout.addWidget(self.chumLabel)
self.layout.addWidget(self.textArea)
self.layout.addWidget(self.textInput)
self.layout.setSpacing(0)
self.layout.setMargin(0)
self.setLayout(self.layout)
self.chumopen = False
if parent:
parent.addChat(self)
if initiated:
msg = self.mainwindow.profile().pestermsg(self.chum, QtGui.QColor(self.mainwindow.theme["convo/systemMsgColor"]), self.mainwindow.theme["convo/text/beganpester"])
self.setChumOpen(True)
self.textArea.append(convertTags(msg))
self.mainwindow.chatlog.log(self.chum.handle, convertTags(msg, "bbcode"))
self.newmessage = False
def updateMood(self, mood, unblocked=False):
if mood.name() == "offline" and self.chumopen == True and not unblocked:
msg = self.chum.pestermsg(self.mainwindow.profile(), QtGui.QColor(self.mainwindow.theme["convo/systemMsgColor"]), self.mainwindow.theme["convo/text/ceasepester"])
self.textArea.append(convertTags(msg))
self.mainwindow.chatlog.log(self.chum.handle, convertTags(msg, "bbcode"))
self.chumopen = False
if self.parent():
self.parent().updateMood(self.chum.handle, mood, unblocked)
else:
if self.chum.blocked(self.mainwindow.config) and not unblocked:
self.setWindowIcon(QtGui.QIcon(self.mainwindow.theme["main/chums/moods/blocked/icon"]))
else:
self.setWindowIcon(mood.icon(self.mainwindow.theme))
# print mood update?
def updateBlocked(self):
if self.parent():
self.parent().updateBlocked(self.chum.handle)
else:
self.setWindowIcon(QtGui.QIcon(self.mainwindow.theme["main/chums/moods/blocked/icon"]))
def updateColor(self, color):
self.chum.color = color
def addMessage(self, text, me=True):
if me:
chum = self.mainwindow.profile()
else:
chum = self.chum
self.notifyNewMessage()
self.textArea.addMessage(text, chum)
def notifyNewMessage(self):
# first see if this conversation HASS the focus
if not (self.hasFocus() or self.textArea.hasFocus() or
self.textInput.hasFocus() or
(self.parent() and self.parent().convoHasFocus(self.chum.handle))):
# ok if it has a tabconvo parent, send that the notify.
if self.parent():
self.parent().notifyNewMessage(self.chum.handle)
# if not change the window title and update system tray
else:
self.newmessage = True
self.setWindowTitle(self.chum.handle+"*")
def func():
self.showChat()
self.mainwindow.waitingMessages.addMessage(self.chum.handle, func)
def clearNewMessage(self):
if self.parent():
self.parent().clearNewMessage(self.chum.handle)
elif self.newmessage:
self.newmessage = False
self.setWindowTitle(self.chum.handle)
self.mainwindow.waitingMessages.messageAnswered(self.chum.handle)
# reset system tray
def focusInEvent(self, event):
self.clearNewMessage()
self.textInput.setFocus()
def raiseChat(self):
self.activateWindow()
self.raise_()
self.textInput.setFocus()
def showChat(self):
if self.parent():
self.parent().showChat(self.chum.handle)
self.raiseChat()
def closeEvent(self, event):
self.mainwindow.waitingMessages.messageAnswered(self.chum.handle)
self.windowClosed.emit(self.chum.handle)
def setChumOpen(self, o):
self.chumopen = o
def changeTheme(self, theme):
self.resize(*theme["convo/size"])
self.setStyleSheet(theme["convo/style"])
self.setWindowIcon(self.chum.mood.icon(theme))
t = Template(self.mainwindow.theme["convo/chumlabel/text"])
self.chumLabel.setText(t.safe_substitute(handle=self.chum.handle))
self.chumLabel.setStyleSheet(theme["convo/chumlabel/style"])
self.chumLabel.setAlignment(self.aligndict["h"][self.mainwindow.theme["convo/chumlabel/align/h"]] | self.aligndict["v"][self.mainwindow.theme["convo/chumlabel/align/v"]])
self.chumLabel.setMaximumHeight(self.mainwindow.theme["convo/chumlabel/maxheight"])
self.chumLabel.setMinimumHeight(self.mainwindow.theme["convo/chumlabel/minheight"])
self.chumLabel.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Expanding))
self.textArea.changeTheme(theme)
self.textInput.changeTheme(theme)
@QtCore.pyqtSlot()
def sentMessage(self):
text = self.textInput.text()
if text == "":
return
# deal with quirks here
qtext = self.mainwindow.userprofile.quirks.apply(unicode(text))
text = QtCore.QString(qtext)
self.textInput.setText("")
self.addMessage(text, True)
# if ceased, rebegin
if not self.chumopen:
self.mainwindow.newConvoStarted.emit(QtCore.QString(self.chum.handle), True)
# convert color tags
text = convertTags(unicode(text), "ctag")
self.messageSent.emit(text, self.chum)
messageSent = QtCore.pyqtSignal(QtCore.QString, PesterProfile)
windowClosed = QtCore.pyqtSignal(QtCore.QString)
aligndict = {"h": {"center": QtCore.Qt.AlignHCenter,
"left": QtCore.Qt.AlignLeft,
"right": QtCore.Qt.AlignRight },
"v": {"center": QtCore.Qt.AlignVCenter,
"top": QtCore.Qt.AlignTop,
"bottom": QtCore.Qt.AlignBottom } }
class PesterWindow(MovingWindow): class PesterWindow(MovingWindow):
def __init__(self, parent=None): def __init__(self, parent=None):
@ -1076,6 +581,7 @@ class PesterWindow(MovingWindow):
QtCore.Qt.FramelessWindowHint)) QtCore.Qt.FramelessWindowHint))
self.convos = {} self.convos = {}
self.memos = {}
self.tabconvo = None self.tabconvo = None
self.setObjectName("main") self.setObjectName("main")
@ -1103,11 +609,16 @@ class PesterWindow(MovingWindow):
self.userlistaction = userlistaction self.userlistaction = userlistaction
self.connect(userlistaction, QtCore.SIGNAL('triggered()'), self.connect(userlistaction, QtCore.SIGNAL('triggered()'),
self, QtCore.SLOT('showAllUsers()')) self, QtCore.SLOT('showAllUsers()'))
memoaction = QtGui.QAction(self.theme["main/menus/client/memos"], self)
self.memoaction = memoaction
self.connect(memoaction, QtCore.SIGNAL('triggered()'),
self, QtCore.SLOT('showMemos()'))
self.menu = QtGui.QMenuBar(self) self.menu = QtGui.QMenuBar(self)
filemenu = self.menu.addMenu(self.theme["main/menus/client/_name"]) filemenu = self.menu.addMenu(self.theme["main/menus/client/_name"])
self.filemenu = filemenu self.filemenu = filemenu
filemenu.addAction(opts) filemenu.addAction(opts)
filemenu.addAction(memoaction)
filemenu.addAction(userlistaction) filemenu.addAction(userlistaction)
filemenu.addAction(exitaction) filemenu.addAction(exitaction)
@ -1268,6 +779,27 @@ class PesterWindow(MovingWindow):
self.connect(self.tabconvo, QtCore.SIGNAL('windowClosed()'), self.connect(self.tabconvo, QtCore.SIGNAL('windowClosed()'),
self, QtCore.SLOT('tabsClosed()')) self, QtCore.SLOT('tabsClosed()'))
def newMemo(self, channel):
if self.memos.has_key(channel):
# load memo
return
else:
# do slider dialog then set
if self.config.tabs():
# create new tabbed memo window
pass
else:
self.memoWindow = PesterMemo(channel, self, None)
# connect signals
# chat client send memo open
self.memoWindow.show()
def addChum(self, chum):
self.chumList.addChum(chum)
self.config.addChum(chum)
self.moodRequest.emit(chum)
def changeProfile(self, collision=None): def changeProfile(self, collision=None):
if not hasattr(self, 'chooseprofile'): if not hasattr(self, 'chooseprofile'):
self.chooseprofile = None self.chooseprofile = None
@ -1348,13 +880,7 @@ class PesterWindow(MovingWindow):
if not pygame.mixer: if not pygame.mixer:
self.alarm = NoneSound() self.alarm = NoneSound()
else: else:
self.alarm = pygame.mixer.Sound(theme["main/sounds/alertsound"] self.alarm = pygame.mixer.Sound(theme["main/sounds/alertsound"])
)
def addChum(self, chum):
self.chumList.addChum(chum)
self.config.addChum(chum)
self.moodRequest.emit(chum)
def changeTheme(self, theme): def changeTheme(self, theme):
self.theme = theme self.theme = theme
@ -1393,6 +919,7 @@ class PesterWindow(MovingWindow):
else: else:
self.waitingMessages.answerMessage() self.waitingMessages.answerMessage()
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def blockSelectedChum(self): def blockSelectedChum(self):
curChumListing = self.chumList.currentItem() curChumListing = self.chumList.currentItem()
@ -1539,6 +1066,39 @@ class PesterWindow(MovingWindow):
self.unblockedChum.emit(handle) self.unblockedChum.emit(handle)
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def showMemos(self):
if not hasattr(self, 'memochooser'):
self.memochooser = None
if self.memochooser:
return
self.memochooser = PesterMemoList(self)
self.connect(self.memochooser, QtCore.SIGNAL('accepted()'),
self, QtCore.SLOT('joinSelectedMemo()'))
self.connect(self.memochooser, QtCore.SIGNAL('rejected()'),
self, QtCore.SLOT('memoChooserClose()'))
self.requestChannelList.emit()
self.memochooser.show()
@QtCore.pyqtSlot()
def joinSelectedMemo(self):
newmemo = self.memochooser.newmemoname()
selectedmemo = self.memochooser.selectedmemo()
if newmemo:
self.newMemo('#'+newmemo)
else:
self.newMemo('#'+selectedmemo.text())
self.memochooser = None
@QtCore.pyqtSlot()
def memoChooserClose(self):
self.memochooser = None
@QtCore.pyqtSlot()
def memoChooserClose(self):
self.memochooser = None
@QtCore.pyqtSlot(PesterList)
def updateChannelList(self, channels):
if hasattr(self, 'memochooser') and self.memochooser:
self.memochooser.updateChannels(channels)
@QtCore.pyqtSlot()
def showAllUsers(self): def showAllUsers(self):
if not hasattr(self, 'allusers'): if not hasattr(self, 'allusers'):
self.allusers = None self.allusers = None
@ -1561,6 +1121,7 @@ class PesterWindow(MovingWindow):
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def userListClose(self): def userListClose(self):
self.allusers = None self.allusers = None
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def openQuirks(self): def openQuirks(self):
if not hasattr(self, 'quirkmenu'): if not hasattr(self, 'quirkmenu'):
@ -1705,6 +1266,8 @@ class PesterWindow(MovingWindow):
return return
self.colorDialog = QtGui.QColorDialog(self) self.colorDialog = QtGui.QColorDialog(self)
color = self.colorDialog.getColor(initial=self.profile().color) color = self.colorDialog.getColor(initial=self.profile().color)
if not color.isValid():
color = self.profile().color
self.mychumcolor.setStyleSheet("background: %s" % color.name()) self.mychumcolor.setStyleSheet("background: %s" % color.name())
self.userprofile.setColor(color) self.userprofile.setColor(color)
self.mycolorUpdated.emit() self.mycolorUpdated.emit()
@ -1753,6 +1316,7 @@ class PesterWindow(MovingWindow):
moodRequest = QtCore.pyqtSignal(PesterProfile) moodRequest = QtCore.pyqtSignal(PesterProfile)
moodsRequest = QtCore.pyqtSignal(PesterList) moodsRequest = QtCore.pyqtSignal(PesterList)
moodUpdated = QtCore.pyqtSignal() moodUpdated = QtCore.pyqtSignal()
requestChannelList = QtCore.pyqtSignal()
requestNames = QtCore.pyqtSignal(QtCore.QString) requestNames = QtCore.pyqtSignal(QtCore.QString)
namesUpdated = QtCore.pyqtSignal() namesUpdated = QtCore.pyqtSignal()
userPresentSignal = QtCore.pyqtSignal(QtCore.QString,QtCore.QString,QtCore.QString) userPresentSignal = QtCore.pyqtSignal(QtCore.QString,QtCore.QString,QtCore.QString)
@ -1820,6 +1384,9 @@ class PesterIRC(QtCore.QObject):
def requestNames(self, channel): def requestNames(self, channel):
c = unicode(channel) c = unicode(channel)
helpers.names(self.cli, c) helpers.names(self.cli, c)
@QtCore.pyqtSlot()
def requestChannelList(self):
helpers.channel_list(self.cli)
def updateIRC(self): def updateIRC(self):
self.conn.next() self.conn.next()
@ -1828,6 +1395,7 @@ class PesterIRC(QtCore.QObject):
colorUpdated = QtCore.pyqtSignal(QtCore.QString, QtGui.QColor) colorUpdated = QtCore.pyqtSignal(QtCore.QString, QtGui.QColor)
messageReceived = QtCore.pyqtSignal(QtCore.QString, QtCore.QString) messageReceived = QtCore.pyqtSignal(QtCore.QString, QtCore.QString)
namesReceived = QtCore.pyqtSignal(QtCore.QString, PesterList) namesReceived = QtCore.pyqtSignal(QtCore.QString, PesterList)
channelListReceived = QtCore.pyqtSignal(PesterList)
nickCollision = QtCore.pyqtSignal(QtCore.QString, QtCore.QString) nickCollision = QtCore.pyqtSignal(QtCore.QString, QtCore.QString)
userPresentUpdate = QtCore.pyqtSignal(QtCore.QString, QtCore.QString, userPresentUpdate = QtCore.pyqtSignal(QtCore.QString, QtCore.QString,
QtCore.QString) QtCore.QString)
@ -1919,6 +1487,19 @@ class PesterHandler(DefaultCommandHandler):
del self.channelnames[channel] del self.channelnames[channel]
self.parent.namesReceived.emit(channel, pl) self.parent.namesReceived.emit(channel, pl)
def liststart(self, server, handle, *info):
self.channel_list = []
info = list(info)
self.channel_field = info.index("Channel") # dunno if this is protocol
def list(self, server, handle, *info):
channel = info[self.channel_field]
if channel not in self.channel_list and channel != "#pesterchum":
self.channel_list.append(channel)
def listend(self, server, handle, msg):
pl = PesterList(self.channel_list)
self.parent.channelListReceived.emit(pl)
self.channel_list = []
def getMood(self, *chums): def getMood(self, *chums):
chumglub = "GETMOOD " chumglub = "GETMOOD "
for c in chums: for c in chums:
@ -2020,6 +1601,10 @@ def main():
QtCore.SIGNAL('requestNames(QString)'), QtCore.SIGNAL('requestNames(QString)'),
irc, irc,
QtCore.SLOT('requestNames(QString)')) QtCore.SLOT('requestNames(QString)'))
irc.connect(widget,
QtCore.SIGNAL('requestChannelList()'),
irc,
QtCore.SLOT('requestChannelList()'))
# IRC --> Main window # IRC --> Main window
irc.connect(irc, irc.connect(irc,
@ -2046,6 +1631,10 @@ def main():
QtCore.SIGNAL('userPresentUpdate(QString, QString, QString)'), QtCore.SIGNAL('userPresentUpdate(QString, QString, QString)'),
widget, widget,
QtCore.SLOT('userPresentUpdate(QString, QString, QString)')) QtCore.SLOT('userPresentUpdate(QString, QString, QString)'))
irc.connect(irc,
QtCore.SIGNAL('channelListReceived(PyQt_PyObject)'),
widget,
QtCore.SLOT('updateChannelList(PyQt_PyObject)'))
ircapp = IRCThread(irc) ircapp = IRCThread(irc)
ircapp.start() ircapp.start()

Binary file not shown.

Binary file not shown.

View file

@ -1 +1 @@
{"color": "#aa00ff", "theme": "trollian", "quirks": [], "handle": "testProfile"} {"color": "#aa00ff", "theme": "pesterchum", "quirks": [], "handle": "testProfile"}

174
setup-py2exe.py Normal file
View file

@ -0,0 +1,174 @@
# ======================================================#
# File automagically generated by GUI2Exe version 0.3
# Andrea Gavana, 01 April 2007
# ======================================================#
# Let's start with some default (for me) imports...
from distutils.core import setup
import py2exe
import glob
import os
import zlib
import shutil
# Remove the build folder
shutil.rmtree("build", ignore_errors=True)
# do the same for dist folder
shutil.rmtree("dist", ignore_errors=True)
MANIFEST_TEMPLATE = """
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="5.0.0.0"
processorArchitecture="x86"
name="%(prog)s"
type="win32"
/>
<description>%(prog)s</description>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel
level="asInvoker"
uiAccess="false">
</requestedExecutionLevel>
</requestedPrivileges>
</security>
</trustInfo>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.VC90.CRT"
version="9.0.21022.8"
processorArchitecture="x86"
publicKeyToken="1fc8b3b9a1e18e3b">
</assemblyIdentity>
</dependentAssembly>
</dependency>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="X86"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</assembly>
"""
class Target(object):
""" A simple class that holds information on our executable file. """
def __init__(self, **kw):
""" Default class constructor. Update as you need. """
self.__dict__.update(kw)
# Ok, let's explain why I am doing that.
# Often, data_files, excludes and dll_excludes (but also resources)
# can be very long list of things, and this will clutter too much
# the setup call at the end of this file. So, I put all the big lists
# here and I wrap them using the textwrap module.
data_files = []
includes = []
excludes = ['_gtkagg', '_tkagg', 'bsddb', 'curses', 'email', 'pywin.debugger',
'pywin.debugger.dbgcon', 'pywin.dialogs', 'tcl',
'Tkconstants', 'Tkinter']
packages = []
dll_excludes = ['libgdk-win32-2.0-0.dll', 'libgobject-2.0-0.dll', 'tcl84.dll',
'tk84.dll',
'MSVCP90.dll', 'mswsock.dll', 'powrprof.dll']
icon_resources = []
bitmap_resources = []
other_resources = []
other_resources = [(24, 1, MANIFEST_TEMPLATE % dict(prog="MyAppName"))]
# This is a place where the user custom code may go. You can do almost
# whatever you want, even modify the data_files, includes and friends
# here as long as they have the same variable name that the setup call
# below is expecting.
#
# The following will copy the MSVC run time dll's
# (msvcm90.dll, msvcp90.dll and msvcr90.dll) and
# the Microsoft.VC90.CRT.manifest which I keep in the
# "Py26MSdlls" folder to the dist folder
#
# depending on wx widgets you use, you might need to add
# gdiplus.dll to the above collection
py26MSdll = glob.glob(r"c:\Dev\Py26MSdlls-9.0.21022.8\msvc\*.*")
# install the MSVC 9 runtime dll's into the application folder
data_files += [("", py26MSdll),]
# I found on some systems one has to put them into sub-folders.
##data_files += [("Microsoft.VC90.CRT", py26MSdll),
## ("lib\Microsoft.VC90.CRT", py26MSdll)]
# Ok, now we are going to build our target class.
# I chose this building strategy as it works perfectly for me :-D
GUI2Exe_Target_1 = Target(
# what to build
script = "test.py",
icon_resources = icon_resources,
bitmap_resources = bitmap_resources,
other_resources = other_resources,
dest_base = "test",
version = "0.1",
company_name = "No Company",
copyright = "No Copyrights",
name = "Py2Exe Sample File"
)
# That's serious now: we have all (or almost all) the options py2exe
# supports. I put them all even if some of them are usually defaulted
# and not used. Some of them I didn't even know about.
setup(
data_files = data_files,
options = {"py2exe": {"compressed": 2,
"optimize": 2,
"includes": includes,
"excludes": excludes,
"packages": packages,
"dll_excludes": dll_excludes,
"bundle_files": 2,
"dist_dir": "dist",
"xref": False,
"skip_archive": False,
"ascii": False,
"custom_boot_script": '',
}
},
zipfile = "lib\library.zip",
console = [],
windows = [GUI2Exe_Target_1]
)
# This is a place where any post-compile code may go.
# You can add as much code as you want, which can be used, for example,
# to clean up your folders or to do some particular post-compilation
# actions.
# And we are done. That's a setup script :-D

32
setup.py Normal file
View file

@ -0,0 +1,32 @@
from cx_Freeze import setup, Executable
import sys
import os
import shutil
if sys.platform == "win32":
base = "Win32GUI"
else:
base = "Console"
setup(
name = "P3ST3RCHUM",
version = "3.14",
description = "P3ST3RCHUM",
executables = [Executable("pesterchum.py",
base=base,
icon="pesterchum.ico",
compress=True,
)])
if sys.platform == "win32":
os.rename("build/exe.win32-2.6", "build/pesterchum")
shutil.copytree("themes", "build/pesterchum/themes")
shutil.copytree("imageformats", "build/pesterchum/imageformats")
shutil.copy("pesterchum.js", "build/pesterchum/")
shutil.copy("C:/Dev/Py26MSdlls-9.0.21022.8/msvc/msvcm90.dll", "build/pesterchum")
shutil.copy("C:/Dev/Py26MSdlls-9.0.21022.8/msvc/msvcp90.dll", "build/pesterchum")
shutil.copy("C:/Dev/Py26MSdlls-9.0.21022.8/msvc/msvcr90.dll", "build/pesterchum")
shutil.copy("C:/Dev/Py26MSdlls-9.0.21022.8/msvc/x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375.manifest",
"build/pesterchum")
os.mkdir("build/pesterchum/profiles")
os.mkdir("build/pesterchum/logs")

BIN
themes/pesterchum/memo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

View file

@ -17,6 +17,7 @@
"sounds": { "alertsound": "$path/alarm.wav" }, "sounds": { "alertsound": "$path/alarm.wav" },
"menus": {"client": {"_name": "CLIENT", "menus": {"client": {"_name": "CLIENT",
"options": "OPTIONS", "options": "OPTIONS",
"memos": "MEMOS",
"userlist": "USERLIST", "userlist": "USERLIST",
"exit": "EXIT"}, "exit": "EXIT"},
"profile": {"_name": "PROFILE", "profile": {"_name": "PROFILE",
@ -183,6 +184,7 @@
}, },
"convo": "convo":
{"style": "background: #fdb302; border:2px solid yellow; font-family: 'Courier'", {"style": "background: #fdb302; border:2px solid yellow; font-family: 'Courier'",
"margins": {"top": 0, "bottom": 0, "left": 0, "right": 0 },
"size": [295, 191], "size": [295, 191],
"chumlabel": { "style": "background: rgb(196, 138, 0); color: white; border:0px;", "chumlabel": { "style": "background: rgb(196, 138, 0); color: white; border:0px;",
"align": { "h": "center", "v": "center" }, "align": { "h": "center", "v": "center" },
@ -209,6 +211,25 @@
"unblocked": "unblocked" "unblocked": "unblocked"
}, },
"systemMsgColor": "#646464" "systemMsgColor": "#646464"
},
"memos":
{"memoicon": "$path/memo.png",
"style": "background: #fdb302; font-family:'Courier';font:bold;selection-background-color:#919191; ",
"size": [600,300],
"label": { "text": "$channel",
"style": "background: rgb(196, 138, 0); color: white; border:0px;",
"align": { "h": "center", "v": "center" },
"minheight": 30,
"maxheight": 50
},
"input": { "style": "background: white; border:2px solid #c48a00;margin-top:5px;" },
"textarea": { "style": "background: white; font:bold; border:2px solid #c48a00;text-align:center;" },
"margins": {"top": 0, "bottom": 0, "left": 0, "right": 0 },
"userlist": { "width": 150 },
"time": { "text": { "width": 75, "style": "" },
"slider": { "style": "",
"groove": "",
"handle": "" }
}
} }
} }

BIN
themes/trollian/memo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

View file

@ -10,13 +10,14 @@
"loc": [621, 8]}, "loc": [621, 8]},
"menubar": { "style": "font-family: 'Arial'; font-size: 11px; color: rgba(0,0,0,0);" }, "menubar": { "style": "font-family: 'Arial'; font-size: 11px; color: rgba(0,0,0,0);" },
"menu" : { "style": "font-family: 'Arial'; font-size: 11px; background-color: #c2c2c2; border:1px solid #545454;", "menu" : { "style": "font-family: 'Arial'; font-size: 11px; background-color: #c2c2c2; border:1px solid #545454;",
"selected": "background-color: #545454", "selected": "background-color: #545454",
"menuitem": "margin-right:14px;", "menuitem": "margin-right:14px;",
"loc": [14,90] "loc": [14,90]
}, },
"sounds": { "alertsound": "$path/alarm.wav" }, "sounds": { "alertsound": "$path/alarm.wav" },
"menus": {"client": {"_name": "Trollian", "menus": {"client": {"_name": "Trollian",
"options": "Options", "options": "Options",
"memos": "Memos",
"userlist": "Fresh Targets", "userlist": "Fresh Targets",
"exit": "Abscond"}, "exit": "Abscond"},
"profile": {"_name": "View", "profile": {"_name": "View",
@ -31,56 +32,56 @@
"addchum": "Add Chump", "addchum": "Add Chump",
"unblockchum": "Mercy"} "unblockchum": "Mercy"}
}, },
"chums": { "style": "border: 0px; background-color: white; padding: 5px; font-family: 'Arial';selection-background-color:rgb(200,200,200); ", "chums": { "style": "font-size: 12px; background: white; border:2px solid #c2c2c2; padding: 5px; font-family: 'Arial';selection-background-color:rgb(200,200,200); ",
"loc": [476, 90], "loc": [475, 89],
"size": [175, 361], "size": [175, 361],
"userlistcolor": "black", "userlistcolor": "black",
"moods": { "moods": {
"chummy": { "icon": "$path/chummy.png", "color": "#63ea00" }, "chummy": { "icon": "$path/chummy.png", "color": "#63ea00" },
"rancorous": { "icon": "$path/rancorous.png", "color": "#7f7f7f" }, "rancorous": { "icon": "$path/rancorous.png", "color": "#7f7f7f" },
"offline": { "icon": "$path/offline.png", "color": "black"}, "offline": { "icon": "$path/offline.png", "color": "black"},
"pleasant": { "icon": "$path/pleasant.png", "color": "#d69df8" }, "pleasant": { "icon": "$path/pleasant.png", "color": "#d69df8" },
"distraught": { "icon": "$path/distraught.png", "color": "#706eba" }, "distraught": { "icon": "$path/distraught.png", "color": "#706eba" },
"unruly": { "icon": "$path/unruly.png", "color": "blue" }, "unruly": { "icon": "$path/unruly.png", "color": "blue" },
"smooth": { "icon": "$path/smooth.png", "color": "red" }, "smooth": { "icon": "$path/smooth.png", "color": "red" },
"ecstatic": { "icon": "$path/ecstatic.png", "color": "#99004d" }, "ecstatic": { "icon": "$path/ecstatic.png", "color": "#99004d" },
"relaxed": { "icon": "$path/relaxed.png", "color": "#078446" }, "relaxed": { "icon": "$path/relaxed.png", "color": "#078446" },
"discontent": { "icon": "$path/discontent.png", "color": "#a75403" }, "discontent": { "icon": "$path/discontent.png", "color": "#a75403" },
"devious": { "icon": "$path/devious.png", "color": "#008282" }, "devious": { "icon": "$path/devious.png", "color": "#008282" },
"sleek": { "icon": "$path/sleek.png", "color": "#a1a100" }, "sleek": { "icon": "$path/sleek.png", "color": "#a1a100" },
"detestful": { "icon": "$path/detestful.png", "color": "#6a006a" }, "detestful": { "icon": "$path/detestful.png", "color": "#6a006a" },
"mirthful": { "icon": "$path/mirthful.png", "color": "#450077" }, "mirthful": { "icon": "$path/mirthful.png", "color": "#450077" },
"manipulative": { "icon": "$path/manipulative.png", "color": "#004182" }, "manipulative": { "icon": "$path/manipulative.png", "color": "#004182" },
"vigorous": { "icon": "$path/vigorous.png", "color": "#0021cb" }, "vigorous": { "icon": "$path/vigorous.png", "color": "#0021cb" },
"perky": { "icon": "$path/perky.png", "color": "#406600" }, "perky": { "icon": "$path/perky.png", "color": "#406600" },
"acceptant": { "icon": "$path/acceptant.png", "color": "#a10000" }, "acceptant": { "icon": "$path/acceptant.png", "color": "#a10000" },
"protective": { "icon": "$path/protective.png", "color": "white" }, "protective": { "icon": "$path/protective.png", "color": "white" },
"blocked": { "icon": "$path/blocked.png", "color": "black" } "blocked": { "icon": "$path/blocked.png", "color": "black" }
} }
}, },
"trollslum": { "trollslum": {
"style": "background: rgb(190, 19, 4); font-family: 'Arial'", "style": "background: rgb(190, 19, 4); font-family: 'Arial'",
@ -107,142 +108,143 @@
"text": "" "text": ""
}, },
"pester": { "style": "background: rgba(0,0,0,0); border:0px; color: rgba(0,0,0,0);", "pester": { "style": "background: rgba(0,0,0,0); border:0px; color: rgba(0,0,0,0);",
"loc": [0,0], "loc": [0,0],
"size": [0, 0], "size": [0, 0],
"text": "" "text": ""
}, },
"block": { "style": "background: rgba(0,0,0,0); border:0px; color: rgba(0,0,0,0);", "block": { "style": "background: rgba(0,0,0,0); border:0px; color: rgba(0,0,0,0);",
"loc": [1500,202], "loc": [1500,202],
"size": [71, 22], "size": [71, 22],
"text": "" "text": ""
}, },
"defaultmood": 7, "defaultmood": 7,
"moodlabel": { "style": "", "moodlabel": { "style": "",
"loc": [0, 0], "loc": [0, 0],
"text": "" "text": ""
}, },
"moods": [ "moods": [
{ "style": "border:0px;", { "style": "border:0px;",
"selected": "background-image:url($path/moodcheck1.png); border:0px;", "selected": "background-image:url($path/moodcheck1.png); border:0px;",
"loc": [25, 141], "loc": [25, 141],
"size": [20, 270], "size": [20, 270],
"text": "", "text": "",
"icon": "", "icon": "",
"mood": 17 "mood": 17
}, },
{ "style": "border:0px;", { "style": "border:0px;",
"selected": "background-image:url($path/moodcheck2.png); border:0px;", "selected": "background-image:url($path/moodcheck2.png); border:0px;",
"loc": [60, 141], "loc": [60, 141],
"size": [20, 270], "size": [20, 270],
"text": "", "text": "",
"icon": "", "icon": "",
"mood": 9 "mood": 9
}, },
{ "style": "border:0px;", { "style": "border:0px;",
"selected": "background-image:url($path/moodcheck3.png); border:0px;", "selected": "background-image:url($path/moodcheck3.png); border:0px;",
"loc": [95, 141], "loc": [95, 141],
"size": [20, 270], "size": [20, 270],
"text": "", "text": "",
"icon": "", "icon": "",
"mood": 11 "mood": 11
}, },
{ "style": "border:0px;", { "style": "border:0px;",
"selected": "background-image:url($path/moodcheck4.png); border:0px;", "selected": "background-image:url($path/moodcheck4.png); border:0px;",
"loc": [130, 141], "loc": [130, 141],
"size": [20, 270], "size": [20, 270],
"text": "", "text": "",
"icon": "", "icon": "",
"mood": 1 "mood": 1
}, },
{ "style": "border:0px;", { "style": "border:0px;",
"selected": "background-image:url($path/moodcheck5.png); border:0px;", "selected": "background-image:url($path/moodcheck5.png); border:0px;",
"loc": [165, 141], "loc": [165, 141],
"size": [20, 270], "size": [20, 270],
"text": "", "text": "",
"icon": "", "icon": "",
"mood": 16 "mood": 16
}, },
{ "style": "border:0px;", { "style": "border:0px;",
"selected": "background-image:url($path/moodcheck6.png); border:0px;", "selected": "background-image:url($path/moodcheck6.png); border:0px;",
"loc": [200, 141], "loc": [200, 141],
"size": [20, 270], "size": [20, 270],
"text": "", "text": "",
"icon": "", "icon": "",
"mood": 8 "mood": 8
}, },
{ "style": "border:0px;", { "style": "border:0px;",
"selected": "background-image:url($path/moodcheck7.png); border:0px;", "selected": "background-image:url($path/moodcheck7.png); border:0px;",
"loc": [235, 141], "loc": [235, 141],
"size": [20, 270], "size": [20, 270],
"text": "", "text": "",
"icon": "", "icon": "",
"mood": 10 "mood": 10
}, },
{ "style": "border:0px;", { "style": "border:0px;",
"selected": "background-image:url($path/moodcheck8.png); border:0px;", "selected": "background-image:url($path/moodcheck8.png); border:0px;",
"loc": [270, 141], "loc": [270, 141],
"size": [20, 270], "size": [20, 270],
"text": "", "text": "",
"icon": "", "icon": "",
"mood": 14 "mood": 14
}, },
{ "style": "border:0px;", { "style": "border:0px;",
"selected": "background-image:url($path/moodcheck9.png); border:0px;", "selected": "background-image:url($path/moodcheck9.png); border:0px;",
"loc": [305, 141], "loc": [305, 141],
"size": [20, 270], "size": [20, 270],
"text": "", "text": "",
"icon": "", "icon": "",
"mood": 15 "mood": 15
}, },
{ "style": "border:0px;", { "style": "border:0px;",
"selected": "background-image:url($path/moodcheck10.png); border:0px;", "selected": "background-image:url($path/moodcheck10.png); border:0px;",
"loc": [340, 141], "loc": [340, 141],
"size": [20, 270], "size": [20, 270],
"text": "", "text": "",
"icon": "", "icon": "",
"mood": 13 "mood": 13
}, },
{ "style": "border:0px;", { "style": "border:0px;",
"selected": "background-image:url($path/moodcheck11.png); border:0px;", "selected": "background-image:url($path/moodcheck11.png); border:0px;",
"loc": [375, 141], "loc": [375, 141],
"size": [20, 270], "size": [20, 270],
"text": "", "text": "",
"icon": "", "icon": "",
"mood": 12 "mood": 12
}, },
{ "style": "border:0px;", { "style": "border:0px;",
"selected": "background-image:url($path/moodcheck12.png); border:0px;", "selected": "background-image:url($path/moodcheck12.png); border:0px;",
"loc": [410, 141], "loc": [410, 141],
"size": [20, 270], "size": [20, 270],
"text": "", "text": "",
"icon": "", "icon": "",
"mood": 7 "mood": 7
}, },
{ "style": "border:0px;color: rgba(0, 0, 0, 0%);", { "style": "border:0px;color: rgba(0, 0, 0, 0%);",
"selected": "border:0px; color: rgba(0, 0, 0, 0%);", "selected": "border:0px; color: rgba(0, 0, 0, 0%);",
"loc": [12, 117], "loc": [12, 117],
"size": [435, 18], "size": [435, 18],
"text": "", "text": "",
"icon": "", "icon": "",
"mood": 2 "mood": 2
} }
] ]
}, },
"convo": "convo":
{"style": "background: rgb(190, 19, 4); font-family: 'Arial';", {"style": "background: rgb(190, 19, 4); font-family: 'Arial';",
"size": [308, 194], "margins": {"top": 22, "bottom": 9, "left": 10, "right": 4 },
"chumlabel": { "style": "background: rgb(255, 38, 18); color: white;", "size": [400, 250],
"align": { "h": "center", "v": "center" }, "chumlabel": { "style": "background: rgb(255, 38, 18); color: white; padding: 2px; border:1px solid #c2c2c2;",
"minheight": 30, "align": { "h": "left", "v": "center" },
"maxheight": 50, "minheight": 18,
"maxheight": 18,
"text" : "trolling: $handle" "text" : "trolling: $handle"
}, },
"textarea": { "textarea": {
"style": "background: white; border:0px;" "style": "background: white; border:2px solid #c2c2c2; font-size: 12px; margin-top: 4px;"
}, },
"input": { "input": {
"style": "background: white; border:0px solid #c48a00;margin-top:5px;" "style": "background: white;margin-top:5px; border:1px solid #c2c2c2; margin-right: 54px; font-size: 12px;"
}, },
"tabs": { "tabs": {
"style": "", "style": "",
@ -257,6 +259,10 @@
"unblocked": "mercifully forgave" "unblocked": "mercifully forgave"
}, },
"systemMsgColor": "#646464" "systemMsgColor": "#646464"
},
"memos":
{"memoicon": "$path/memo.png"
} }
} }

18
tmp.py Normal file
View file

@ -0,0 +1,18 @@
class PesterMemo(PesterConvo):
def __init__()
def updateMood: pass
def updateBlocked
def updateColor: pass
def addMessage
def notifyNewMessage
def clearNewMessage
def changeTheme
slot
def sentMessage
messageSent - signal -> sendMessage -> sendMessage(Memo)
windowClosed - signal -> closeMemo
self.textInput
self.textArea