Completed Toast Notifications. Includes all options, working custom toast

This commit is contained in:
Kiooeht 2011-08-27 10:51:50 -07:00
parent 26f6b2a2f4
commit d9a742d90c
7 changed files with 342 additions and 76 deletions

View file

@ -28,6 +28,8 @@ CHANGELOG
* Customizable name alerts - Kiooeht [evacipatedBox] * Customizable name alerts - Kiooeht [evacipatedBox]
* Update bug reporter - Kiooeht [evacipatedBox] * Update bug reporter - Kiooeht [evacipatedBox]
* Explain why a chumhandle is invalid - Kiooeht [evacipatedBox] (Idea: Lexi [lexicalNuance]) * Explain why a chumhandle is invalid - Kiooeht [evacipatedBox] (Idea: Lexi [lexicalNuance])
* Netsplit notification in memos - Kiooeht [evacipatedBox]
* Toast Notifications - Kiooeht [evacipatedBox]
* Bug fixes * Bug fixes
* Don't delete random chum when blocking someone not on chumroll - Kiooeht [evacipatedBox] * Don't delete random chum when blocking someone not on chumroll - Kiooeht [evacipatedBox]
* Openning global userlist doesn't reset OP status of memo users - Kiooeht [evacipatedBox] * Openning global userlist doesn't reset OP status of memo users - Kiooeht [evacipatedBox]

View file

@ -18,6 +18,7 @@ Features
* Fully working Toasts * Fully working Toasts
* Auto download/install updates via Windows installer * Auto download/install updates via Windows installer
* Turn memo notifications on/off from right-click menu on memos (Idea: lostGash) * Turn memo notifications on/off from right-click menu on memos (Idea: lostGash)
* Gray out random encounter option when RE is offline
Bugs Bugs
---- ----

View file

@ -996,7 +996,7 @@ class PesterOptions(QtGui.QDialog):
self.tabs = QtGui.QButtonGroup(self) self.tabs = QtGui.QButtonGroup(self)
self.connect(self.tabs, QtCore.SIGNAL('buttonClicked(int)'), self.connect(self.tabs, QtCore.SIGNAL('buttonClicked(int)'),
self, QtCore.SLOT('changePage(int)')) self, QtCore.SLOT('changePage(int)'))
tabNames = ["Chum List", "Conversations", "Interface", "Sound", "Logging", "Idle/Updates", "Theme"] tabNames = ["Chum List", "Conversations", "Interface", "Sound", "Notifications", "Logging", "Idle/Updates", "Theme"]
if parent.advanced: tabNames.append("Advanced") if parent.advanced: tabNames.append("Advanced")
for t in tabNames: for t in tabNames:
button = QtGui.QPushButton(t) button = QtGui.QPushButton(t)
@ -1038,6 +1038,9 @@ class PesterOptions(QtGui.QDialog):
self.editMentions = QtGui.QPushButton("Edit Mentions", self) self.editMentions = QtGui.QPushButton("Edit Mentions", self)
self.connect(self.editMentions, QtCore.SIGNAL('clicked()'), self.connect(self.editMentions, QtCore.SIGNAL('clicked()'),
self, QtCore.SLOT('openMentions()')) self, QtCore.SLOT('openMentions()'))
self.editMentions2 = QtGui.QPushButton("Edit Mentions", self)
self.connect(self.editMentions2, QtCore.SIGNAL('clicked()'),
self, QtCore.SLOT('openMentions()'))
self.volume = QtGui.QSlider(QtCore.Qt.Horizontal, self) self.volume = QtGui.QSlider(QtCore.Qt.Horizontal, self)
self.volume.setMinimum(0) self.volume.setMinimum(0)
@ -1186,6 +1189,21 @@ class PesterOptions(QtGui.QDialog):
layout_type = QtGui.QHBoxLayout() layout_type = QtGui.QHBoxLayout()
layout_type.addWidget(self.notifyTypeLabel) layout_type.addWidget(self.notifyTypeLabel)
layout_type.addWidget(self.notifyOptions) layout_type.addWidget(self.notifyOptions)
self.notifySigninCheck = QtGui.QCheckBox("Chum signs in", self)
if self.config.notifyOptions() & self.config.SIGNIN:
self.notifySigninCheck.setChecked(True)
self.notifySignoutCheck = QtGui.QCheckBox("Chum signs out", self)
if self.config.notifyOptions() & self.config.SIGNOUT:
self.notifySignoutCheck.setChecked(True)
self.notifyNewMsgCheck = QtGui.QCheckBox("New messages", self)
if self.config.notifyOptions() & self.config.NEWMSG:
self.notifyNewMsgCheck.setChecked(True)
self.notifyNewConvoCheck = QtGui.QCheckBox("Only new conversations", self)
if self.config.notifyOptions() & self.config.NEWCONVO:
self.notifyNewConvoCheck.setChecked(True)
self.notifyMentionsCheck = QtGui.QCheckBox("Memo Mentions (initials)", self)
if self.config.notifyOptions() & self.config.INITIALS:
self.notifyMentionsCheck.setChecked(True)
self.notifyChange(self.notifycheck.checkState()) self.notifyChange(self.notifycheck.checkState())
if parent.advanced: if parent.advanced:
@ -1245,13 +1263,6 @@ class PesterOptions(QtGui.QDialog):
layout_interface.addLayout(layout_close) layout_interface.addLayout(layout_close)
layout_interface.addWidget(self.pesterBlink) layout_interface.addWidget(self.pesterBlink)
layout_interface.addWidget(self.memoBlink) layout_interface.addWidget(self.memoBlink)
layout_interface.addSpacing(16)
layout_interface.addWidget(QtGui.QLabel("NOT FULLY COMPLETE YET:"))
layout_interface.addWidget(self.notifycheck)
layout_indent = QtGui.QVBoxLayout()
layout_indent.addLayout(layout_type)
layout_indent.setContentsMargins(22,0,0,0)
layout_interface.addLayout(layout_indent)
self.pages.addWidget(widget) self.pages.addWidget(widget)
# Sound # Sound
@ -1276,6 +1287,26 @@ class PesterOptions(QtGui.QDialog):
layout_sound.addWidget(self.currentVol) layout_sound.addWidget(self.currentVol)
self.pages.addWidget(widget) self.pages.addWidget(widget)
# Notifications
widget = QtGui.QWidget()
layout_notify = QtGui.QVBoxLayout(widget)
layout_notify.setAlignment(QtCore.Qt.AlignTop)
layout_notify.addWidget(self.notifycheck)
layout_indent = QtGui.QVBoxLayout()
layout_indent.addLayout(layout_type)
layout_indent.setContentsMargins(22,0,0,0)
layout_indent.addWidget(self.notifySigninCheck)
layout_indent.addWidget(self.notifySignoutCheck)
layout_indent.addWidget(self.notifyNewMsgCheck)
layout_doubleindent = QtGui.QVBoxLayout()
layout_doubleindent.addWidget(self.notifyNewConvoCheck)
layout_doubleindent.setContentsMargins(22,0,0,0)
layout_indent.addLayout(layout_doubleindent)
layout_indent.addWidget(self.notifyMentionsCheck)
layout_indent.addWidget(self.editMentions2)
layout_notify.addLayout(layout_indent)
self.pages.addWidget(widget)
# Logging # Logging
widget = QtGui.QWidget() widget = QtGui.QWidget()
layout_logs = QtGui.QVBoxLayout(widget) layout_logs = QtGui.QVBoxLayout(widget)
@ -1335,9 +1366,19 @@ class PesterOptions(QtGui.QDialog):
if state == 0: if state == 0:
self.notifyTypeLabel.setEnabled(False) self.notifyTypeLabel.setEnabled(False)
self.notifyOptions.setEnabled(False) self.notifyOptions.setEnabled(False)
self.notifySigninCheck.setEnabled(False)
self.notifySignoutCheck.setEnabled(False)
self.notifyNewMsgCheck.setEnabled(False)
self.notifyNewConvoCheck.setEnabled(False)
self.notifyMentionsCheck.setEnabled(False)
else: else:
self.notifyTypeLabel.setEnabled(True) self.notifyTypeLabel.setEnabled(True)
self.notifyOptions.setEnabled(True) self.notifyOptions.setEnabled(True)
self.notifySigninCheck.setEnabled(True)
self.notifySignoutCheck.setEnabled(True)
self.notifyNewMsgCheck.setEnabled(True)
self.notifyNewConvoCheck.setEnabled(True)
self.notifyMentionsCheck.setEnabled(True)
@QtCore.pyqtSlot(int) @QtCore.pyqtSlot(int)
def soundChange(self, state): def soundChange(self, state):

View file

@ -328,6 +328,12 @@ class userConfig(object):
# Use for bit flag blink # Use for bit flag blink
self.PBLINK = 1 self.PBLINK = 1
self.MBLINK = 2 self.MBLINK = 2
# Use for bit flag notfications
self.SIGNIN = 1
self.SIGNOUT = 2
self.NEWMSG = 4
self.NEWCONVO = 8
self.INITIALS = 16
self.filename = _datadir+"pesterchum.js" self.filename = _datadir+"pesterchum.js"
fp = open(self.filename) fp = open(self.filename)
self.config = json.load(fp) self.config = json.load(fp)
@ -435,6 +441,8 @@ class userConfig(object):
return self.config.get('notify', True) return self.config.get('notify', True)
def notifyType(self): def notifyType(self):
return self.config.get('notifyType', "default") return self.config.get('notifyType', "default")
def notifyOptions(self):
return self.config.get('notifyOptions', self.SIGNIN | self.NEWMSG | self.NEWCONVO | self.INITIALS)
def addChum(self, chum): def addChum(self, chum):
if chum.handle not in self.chums(): if chum.handle not in self.chums():
fp = open(self.filename) # what if we have two clients open?? fp = open(self.filename) # what if we have two clients open??
@ -689,18 +697,20 @@ class chumListing(QtGui.QTreeWidgetItem):
def setMood(self, mood): def setMood(self, mood):
if hasattr(self.mainwindow, "chumList") and self.mainwindow.chumList.notify: if hasattr(self.mainwindow, "chumList") and self.mainwindow.chumList.notify:
#print "%s -> %s" % (self.chum.mood.name(), mood.name()) #print "%s -> %s" % (self.chum.mood.name(), mood.name())
if mood.name() == "offline" and self.chum.mood.name() != "offline": if self.mainwindow.config.notifyOptions() & self.mainwindow.config.SIGNOUT and \
mood.name() == "offline" and self.chum.mood.name() != "offline":
#print "OFFLINE NOTIFY: " + self.handle #print "OFFLINE NOTIFY: " + self.handle
uri = "file://" + os.path.abspath(os.path.curdir) + "/themes/enamel/distraught2.gif" uri = self.mainwindow.theme["toasts/icon/signout"]
n = self.mainwindow.tm.Toast(self.mainwindow.tm.appName, n = self.mainwindow.tm.Toast(self.mainwindow.tm.appName,
"%s is Offline" % (self.handle), uri) "%s is Offline" % (self.handle), uri)
#n.show() n.show()
elif mood.name() != "offline" and self.chum.mood.name() == "offline": elif self.mainwindow.config.notifyOptions() & self.mainwindow.config.SIGNIN and \
mood.name() != "offline" and self.chum.mood.name() == "offline":
#print "ONLINE NOTIFY: " + self.handle #print "ONLINE NOTIFY: " + self.handle
uri = "file://" + os.path.abspath(os.path.curdir) + "/themes/enamel/chummy2.gif" uri = self.mainwindow.theme["toasts/icon/signin"]
n = self.mainwindow.tm.Toast(self.mainwindow.tm.appName, n = self.mainwindow.tm.Toast(self.mainwindow.tm.appName,
"%s is Online" % (self.handle), uri) "%s is Online" % (self.handle), uri)
#n.show() n.show()
login = False login = False
logout = False logout = False
if mood.name() == "offline" and self.chum.mood.name() != "offline": if mood.name() == "offline" and self.chum.mood.name() != "offline":
@ -1596,11 +1606,12 @@ class PesterWindow(MovingWindow):
themeWarning.exec_() themeWarning.exec_()
self.theme = pesterTheme("pesterchum") self.theme = pesterTheme("pesterchum")
extraToasts = {'default': PesterToast}
if pytwmn.confExists():
extraToasts['twmn'] = pytwmn.Notification
self.tm = PesterToastMachine(self, lambda: self.theme["main/windowtitle"], on=self.config.notify(), self.tm = PesterToastMachine(self, lambda: self.theme["main/windowtitle"], on=self.config.notify(),
type=self.config.notifyType(), extras={'pester': PesterToast, 'twmn': pytwmn.Notification}) type=self.config.notifyType(), extras=extraToasts)
self.tm.run() self.tm.run()
t = self.tm.Toast(self.tm.appName, "!!---Started up ToastMachine---!!")
t.show()
self.chatlog = PesterLog(self.profile().handle, self) self.chatlog = PesterLog(self.profile().handle, self)
@ -1856,6 +1867,25 @@ class PesterWindow(MovingWindow):
#yeah suck on this #yeah suck on this
self.sendMessage.emit("PESTERCHUM:BLOCKED", handle) self.sendMessage.emit("PESTERCHUM:BLOCKED", handle)
return return
# notify
if self.config.notifyOptions() & self.config.NEWMSG:
if not self.convos.has_key(handle):
t = self.tm.Toast("New Conversation", "From: %s" % handle)
t.show()
elif not self.config.notifyOptions() & self.config.NEWCONVO:
if msg[:11] != "PESTERCHUM:":
t = self.tm.Toast("From: %s" % handle, re.sub("</?c(=.*?)?>", "", msg))
t.show()
else:
if msg == "PESTERCHUM:CEASE":
t = self.tm.Toast("Closed Conversation", handle)
t.show()
elif msg == "PESTERCHUM:BLOCK":
t = self.tm.Toast("Blocked", handle)
t.show()
elif msg == "PESTERCHUM:UNBLOCK":
t = self.tm.Toast("Unblocked", handle)
t.show()
if not self.convos.has_key(handle): if not self.convos.has_key(handle):
if msg == "PESTERCHUM:CEASE": # ignore cease after we hang up if msg == "PESTERCHUM:CEASE": # ignore cease after we hang up
return return
@ -1902,6 +1932,9 @@ class PesterWindow(MovingWindow):
m = m[m.find(":"):] m = m[m.find(":"):]
for search in self.userprofile.getMentions(): for search in self.userprofile.getMentions():
if re.search(search, m): if re.search(search, m):
if self.config.notifyOptions() & self.config.INITIALS:
t = self.tm.Toast(chan, re.sub("</?c(=.*?)?>", "", msg))
t.show()
self.namesound.play() self.namesound.play()
return return
if self.honk and re.search(r"\bhonk\b", convertTags(msg, "text"), re.I): if self.honk and re.search(r"\bhonk\b", convertTags(msg, "text"), re.I):
@ -2869,15 +2902,29 @@ class PesterWindow(MovingWindow):
# Taskbar blink # Taskbar blink
blinksetting = 0 blinksetting = 0
if self.optionmenu.pesterBlink.isChecked(): if self.optionmenu.pesterBlink.isChecked():
blinksetting = blinksetting | self.config.PBLINK blinksetting |= self.config.PBLINK
if self.optionmenu.memoBlink.isChecked(): if self.optionmenu.memoBlink.isChecked():
blinksetting = blinksetting | self.config.MBLINK blinksetting |= self.config.MBLINK
curblink = self.config.blink() curblink = self.config.blink()
if blinksetting != curblink: if blinksetting != curblink:
self.config.set('blink', blinksetting) self.config.set('blink', blinksetting)
# toast notifications # toast notifications
self.tm.setEnabled(self.optionmenu.notifycheck.isChecked()) self.tm.setEnabled(self.optionmenu.notifycheck.isChecked())
self.tm.setCurrentType(str(self.optionmenu.notifyOptions.currentText())) self.tm.setCurrentType(str(self.optionmenu.notifyOptions.currentText()))
notifysetting = 0
if self.optionmenu.notifySigninCheck.isChecked():
notifysetting |= self.config.SIGNIN
if self.optionmenu.notifySignoutCheck.isChecked():
notifysetting |= self.config.SIGNOUT
if self.optionmenu.notifyNewMsgCheck.isChecked():
notifysetting |= self.config.NEWMSG
if self.optionmenu.notifyNewConvoCheck.isChecked():
notifysetting |= self.config.NEWCONVO
if self.optionmenu.notifyMentionsCheck.isChecked():
notifysetting |= self.config.INITIALS
curnotify = self.config.notifyOptions()
if notifysetting != curnotify:
self.config.set('notifyOptions', notifysetting)
# advanced # advanced
## user mode ## user mode
if self.advanced: if self.advanced:

View file

@ -319,5 +319,18 @@
"voice": { "icon": "$path/voice.png" }, "voice": { "icon": "$path/voice.png" },
"founder": { "icon": "$path/founder.png" }, "founder": { "icon": "$path/founder.png" },
"admin": { "icon": "$path/admin.png" } "admin": { "icon": "$path/admin.png" }
},
"toasts":
{
"width": 210,
"height": 100,
"style": "background: white;",
"icon": { "signin": "$path/../enamel/chummy2.gif",
"signout": "$path/../enamel/distraught2.gif",
"style": "border: 2px solid black; border-width: 2px 0px 0px 2px;" },
"title": { "minimumheight": 50,
"style": "border: 2px solid black; border-width: 2px 2px 0px 0px; padding: 5px; font-weight:bold;"
},
"content": { "style": "background: black; color: white; padding: 5px;" }
} }
} }

View file

@ -349,5 +349,18 @@
"op": { "icon": "$path/op.png" }, "op": { "icon": "$path/op.png" },
"halfop": { "icon": "$path/halfop.png" }, "halfop": { "icon": "$path/halfop.png" },
"voice": { "icon": "$path/voice.png" } "voice": { "icon": "$path/voice.png" }
},
"toasts":
{
"width": 210,
"height": 100,
"style": "background: white;",
"icon": { "signin": "$path/../enamel/ecstatic2.gif",
"signout": "$path/../enamel/discontent2.gif",
"style": "border: 2px solid black; border-width: 2px 0px 0px 2px;" },
"title": { "minimumheight": 50,
"style": "border: 2px solid black; border-width: 2px 2px 0px 0px; padding: 5px; font-weight:bold;"
},
"content": { "style": "background: black; color: white; padding: 5px;" }
} }
} }

263
toast.py
View file

@ -1,6 +1,6 @@
import inspect import inspect
import threading import threading
import time import time, os
from PyQt4 import QtGui, QtCore from PyQt4 import QtGui, QtCore
try: try:
@ -9,12 +9,20 @@ except:
pynotify = None pynotify = None
class DefaultToast(object): class DefaultToast(object):
def __init__(self, title, msg, icon): def __init__(self, machine, title, msg, icon):
self.title = title self.machine = machine
self.msg = msg self.title = title
self.icon = icon self.msg = msg
self.icon = icon
def show(self): def show(self):
print self.title, self.msg, self.icon print self.title, self.msg, self.icon
self.done()
def done(self):
t = self.machine.toasts[0]
if t.title == self.title and t.msg == self.msg and t.icon == self.icon:
self.machine.toasts.pop(0)
self.machine.displaying = False
print "Done"
class ToastMachine(object): class ToastMachine(object):
class __Toast__(object): class __Toast__(object):
@ -23,6 +31,8 @@ class ToastMachine(object):
self.title = title self.title = title
self.msg = msg self.msg = msg
self.time = time self.time = time
if icon:
icon = os.path.abspath(icon)
self.icon = icon self.icon = icon
self.importance = importance self.importance = importance
if inspect.ismethod(self.title) or inspect.isfunction(self.title): if inspect.ismethod(self.title) or inspect.isfunction(self.title):
@ -59,10 +69,20 @@ class ToastMachine(object):
self.realShow() self.realShow()
def realShow(self): def realShow(self):
self.machine.displaying = True
t = None t = None
for (k,v) in self.machine.types.iteritems(): for (k,v) in self.machine.types.iteritems():
if self.machine.type == k: if self.machine.type == k:
t = v(self.title, self.msg, self.icon) args = inspect.getargspec(v.__init__).args
extras = {}
if 'parent' in args:
extras['parent'] = self.machine.parent
if 'time' in args:
extras['time'] = self.time
if k == "libnotify" or k == "twmn":
t = v(self.title, self.msg, self.icon, **extras)
else:
t = v(self.machine, self.title, self.msg, self.icon, **extras)
# Use libnotify's urgency setting # Use libnotify's urgency setting
if k == "libnotify": if k == "libnotify":
if self.importance < 0: if self.importance < 0:
@ -74,17 +94,13 @@ class ToastMachine(object):
break break
if not t: if not t:
if 'default' in self.machine.types: if 'default' in self.machine.types:
if 'parent' in inspect.getargspec(self.machine.types['default']).args: if 'parent' in inspect.getargspec(self.machine.types['default'].__init__).args:
t = self.machine.types['default'](self.title, self.msg, self.icon, self.machine.parent) t = self.machine.types['default'](self.machine, self.title, self.msg, self.icon, self.machine.parent)
else: else:
t = self.machine.types['default'](self.title, self.msg, self.icon) t = self.machine.types['default'](self.machine, self.title, self.msg, self.icon)
else: else:
t = DefaultToast(self.title, self.msg, self.icon) t = DefaultToast(self.title, self.msg, self.icon)
t.show() t.show()
print "SLEEPING"
#time.sleep(self.time/1000)
if self in self.machine.toasts:
self.machine.toasts.remove(self)
def __init__(self, parent, name, on=True, type="default", def __init__(self, parent, name, on=True, type="default",
types=({'default' : DefaultToast, types=({'default' : DefaultToast,
@ -99,13 +115,14 @@ class ToastMachine(object):
self.types = types self.types = types
self.type = "default" self.type = "default"
self.quit = False self.quit = False
self.displaying = False
self.setCurrentType(type) self.setCurrentType(type)
self.toasts = [] self.toasts = []
def Toast(self, title, msg, icon=""): def Toast(self, title, msg, icon="", time=3000):
return self.__Toast__(self, title, msg, time=0, icon=icon) return self.__Toast__(self, title, msg, time=time, icon=icon)
def setEnabled(self, on): def setEnabled(self, on):
self.on = (on is True) self.on = (on is True)
@ -127,7 +144,7 @@ class ToastMachine(object):
from libs import pytwmn from libs import pytwmn
try: try:
pytwmn.init() pytwmn.init()
except pytwmn.ERROR,e: except pytwmn.ERROR, e:
print "Problem initilizing pytwmn: " + str(e) print "Problem initilizing pytwmn: " + str(e)
return return
#self.type = type = "default" #self.type = type = "default"
@ -140,18 +157,9 @@ class ToastMachine(object):
return self.name return self.name
def showNext(self): def showNext(self):
high = filter(lambda x: x.importance < 0, self.toasts) if not self.displaying and self.toasts:
normal = filter(lambda x: x.importance == 0, self.toasts) self.toasts.sort(key=lambda x: x.importance)
low = filter(lambda x: x.importance > 0, self.toasts) self.toasts[0].realShow()
if high:
high.sort(key=lambda x: x.importance)
high[0].realShow()
elif normal:
normal[0].realShow()
elif low:
low.sort(key=lambda x: x.importance)
low[0].realShow()
def showAll(self): def showAll(self):
while self.toasts: while self.toasts:
@ -163,40 +171,180 @@ class ToastMachine(object):
self.showNext() self.showNext()
class PesterToast(QtGui.QFrame, DefaultToast): class PesterToast(QtGui.QWidget, DefaultToast):
def __init__(self, title, msg, icon, parent=None): def __init__(self, machine, title, msg, icon, time=3000, parent=None):
QtGui.QFrame.__init__(self, parent, QtGui.QWidget.__init__(self, parent)
(QtCore.Qt.CustomizeWindowHint |
QtCore.Qt.FramelessWindowHint)) self.machine = machine
#self.setAttribute(QtCore.Qt.WA_QuitOnClose, False) self.time = time
#self.setAttribute(QtCore.Qt.WA_TranslucentBackground, True)
#self.setAttribute(QtCore.Qt.WA_NoSystemBackground, True) self.setWindowFlags(QtCore.Qt.ToolTip)
self.setObjectName("toast")
self.setWindowTitle("toast") self.m_animation = QtCore.QParallelAnimationGroup()
#self.setFocusPolicy(QtCore.Qt.ClickFocus) anim = QtCore.QPropertyAnimation(self)
anim.setTargetObject(self)
self.m_animation.addAnimation(anim)
anim.setEasingCurve(QtCore.QEasingCurve.OutBounce)
anim.setDuration(1000)
self.connect(anim, QtCore.SIGNAL('finished()'),
self, QtCore.SLOT('reverseTrigger()'))
self.m_animation.setDirection(QtCore.QAnimationGroup.Forward)
self.title = QtGui.QLabel(title, self) self.title = QtGui.QLabel(title, self)
self.msg = QtGui.QLabel(msg, self) self.msg = QtGui.QLabel(msg, self)
self.content = msg
self.btn = QtGui.QPushButton("Push Me", self) if icon:
self.connect(self.btn, QtCore.SIGNAL('clicked()'), self.icon = QtGui.QLabel("")
self, QtCore.SLOT('close()')) self.icon.setPixmap(QtGui.QPixmap(icon).scaledToWidth(30))
else:
self.icon = QtGui.QLabel("")
self.icon.setPixmap(QtGui.QPixmap(30, 30))
self.icon.pixmap().fill(QtGui.QColor(0,0,0,0))
layout_0 = QtGui.QVBoxLayout() layout_0 = QtGui.QVBoxLayout()
layout_0.addWidget(self.title) layout_0.setMargin(0)
layout_0.addWidget(self.btn) layout_0.setContentsMargins(0, 0, 0, 0)
if self.icon:
layout_1 = QtGui.QGridLayout()
layout_1.addWidget(self.icon, 0,0, 1,1)
layout_1.addWidget(self.title, 0,1, 1,7)
layout_1.setAlignment(self.msg, QtCore.Qt.AlignTop)
layout_0.addLayout(layout_1)
else:
layout_0.addWidget(self.title)
layout_0.addWidget(self.msg) layout_0.addWidget(self.msg)
self.setMaximumWidth(self.parent().theme["toasts/width"])
self.msg.setMaximumWidth(self.parent().theme["toasts/width"])
self.title.setMinimumHeight(self.parent().theme["toasts/title/minimumheight"])
self.setLayout(layout_0) self.setLayout(layout_0)
print self.isWindow() self.setGeometry(0,0, self.parent().theme["toasts/width"], self.parent().theme["toasts/height"])
self.setStyleSheet(self.parent().theme["toasts/style"])
self.title.setStyleSheet(self.parent().theme["toasts/title/style"])
if self.icon:
self.icon.setStyleSheet(self.parent().theme["toasts/icon/style"])
self.msg.setStyleSheet(self.parent().theme["toasts/content/style"])
self.layout().setSpacing(0)
self.msg.setText(PesterToast.wrapText(self.msg.font(), str(self.msg.text()), self.parent().theme["toasts/width"], self.parent().theme["toasts/content/style"]))
anim.setStartValue(0)
anim.setEndValue(100)
self.connect(anim, QtCore.SIGNAL('valueChanged(QVariant)'),
self, QtCore.SLOT('updateBottomLeftAnimation(QVariant)'))
self.byebye = False
@QtCore.pyqtSlot()
def show(self): def show(self):
QtGui.QFrame.setVisible(self, True) self.m_animation.start()
print "SHOWING"
#~ themeWarning = QtGui.QMessageBox(self) @QtCore.pyqtSlot()
#~ themeWarning.setText("ASDFASD") def done(self):
#~ themeWarning.exec_() QtGui.QWidget.hide(self)
t = self.machine.toasts[0]
if t.title == str(self.title.text()) and \
t.msg == str(self.content):
self.machine.toasts.pop(0)
self.machine.displaying = False
if self.machine.on:
self.machine.showNext()
del self
@QtCore.pyqtSlot()
def reverseTrigger(self):
if self.time >= 0:
QtCore.QTimer.singleShot(self.time, self, QtCore.SLOT('reverseStart()'))
@QtCore.pyqtSlot()
def reverseStart(self):
if not self.byebye:
self.byebye = True
anim = self.m_animation.animationAt(0)
self.m_animation.setDirection(QtCore.QAnimationGroup.Backward)
anim.setEasingCurve(QtCore.QEasingCurve.InCubic)
self.disconnect(anim, QtCore.SIGNAL('finished()'),
self, QtCore.SLOT('reverseTrigger()'))
self.connect(anim, QtCore.SIGNAL('finished()'),
self, QtCore.SLOT('done()'))
self.m_animation.start()
@QtCore.pyqtSlot(QtCore.QVariant)
def updateBottomLeftAnimation(self, value):
p = QtGui.QApplication.desktop().availableGeometry(self).bottomRight()
val = float(self.height())/100
self.move(p.x()-self.width(), p.y() - (value.toInt()[0] * val) +1)
self.layout().setSpacing(0)
self.raise_()
QtGui.QWidget.show(self)
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.RightButton:
self.reverseStart()
elif event.button() == QtCore.Qt.LeftButton:
pass
@staticmethod
def wrapText(font, text, maxwidth, css=""):
ret = []
metric = QtGui.QFontMetrics(font)
if "padding" in css:
if css[css.find("padding")+7] != "-":
colon = css.find(":", css.find("padding"))
semicolon = css.find(";", css.find("padding"))
if semicolon < 0:
stuff = css[colon+1:]
else:
stuff = css[colon+1:semicolon]
stuff = stuff.replace("px", "").lstrip().rstrip()
stuff = stuff.split(" ")
if len(stuff) == 1:
maxwidth -= int(stuff[0])*2
elif len(stuff) == 2:
maxwidth -= int(stuff[1])*2
elif len(stuff) == 3:
maxwidth -= int(stuff[1])*2
elif len(stuff) == 4:
maxwidth -= int(stuff[1]) + int(stuff[3])
else:
if "padding-left" in css:
colon = css.find(":", css.find("padding-left"))
semicolon = css.find(";", css.find("padding-left"))
if semicolon < 0:
stuff = css[colon+1:]
else:
stuff = css[colon+1:semicolon]
stuff = stuff.replace("px", "").lstrip().rstrip()
if stuff.isdigit():
maxwidth -= int(stuff)
if "padding-right" in css:
colon = css.find(":", css.find("padding-right"))
semicolon = css.find(";", css.find("padding-right"))
if semicolon < 0:
stuff = css[colon+1:]
else:
stuff = css[colon+1:semicolon]
stuff = stuff.replace("px", "").lstrip().rstrip()
if stuff.isdigit():
maxwidth -= int(stuff)
if metric.width(text) < maxwidth:
return text
while metric.width(text) > maxwidth:
lastspace = text.find(" ")
curspace = lastspace
while metric.width(text, curspace) < maxwidth:
lastspace = curspace
curspace = text.find(" ", lastspace+1)
ret.append(text[:lastspace])
text = text[lastspace+1:]
ret.append(text)
return "\n".join(ret)
class PesterToastMachine(ToastMachine, QtCore.QObject): class PesterToastMachine(ToastMachine, QtCore.QObject):
def __init__(self, parent, name, on=True, type="default", def __init__(self, parent, name, on=True, type="default",
@ -229,9 +377,10 @@ class PesterToastMachine(ToastMachine, QtCore.QObject):
ToastMachine.showNext(self) ToastMachine.showNext(self)
def run(self): def run(self):
self.timer = QtCore.QTimer(self) pass
self.timer.setInterval(1000) #~ self.timer = QtCore.QTimer(self)
self.connect(self.timer, QtCore.SIGNAL('timeout()'), #~ self.timer.setInterval(1000)
self, QtCore.SLOT('showNext()')) #~ self.connect(self.timer, QtCore.SIGNAL('timeout()'),
if self.on: #~ self, QtCore.SLOT('showNext()'))
pass#self.timer.start() #~ if self.on:
#~ self.timer.start()