This trick requires pytweening:
https://pypi.python.org/pypi/PyTweening
Install writing:
pip install pytweening
It shows a notification on one corner of the screen,and gradually the notification disappears using an easing function. By default, it uses a linear easing function.
The class Notification_Manager has the method create_notification, but it also has some convenient methods to create easily some kind of notifications: success, alert, info, warning.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 | # Author: Miguel Martinez Lopez
import pytweening
try:
from Tkinter import Toplevel, PhotoImage, Frame, Button, N, S, E, W, RIGHT, BOTH, X
except ImportError:
from tkinter import Toplevel, PhotoImage, Frame, Button, N, S, E, W, RIGHT, BOTH, X
SUCCESS_BACKGROUND = "#60a917"
WARNING_BACKGROUND = "#fa6800"
ALERT_BACKGROUND = "#ce352c"
INFO_BACKGROUND = "#59cde2"
class Notification(Toplevel):
def __init__(self, notification_manager, builder, index, x, y, h, v, padx, pady, background=None, on_hide=None):
Toplevel.__init__(self)
self._notification_manager = notification_manager
self.index = index
self.on_hide = on_hide
# Removes the native window boarder.
self.overrideredirect(True)
# Disables resizing of the widget.
self.resizable(False, False)
# Places window above all other windows in the window stack.
self.wm_attributes("-topmost", True)
notification_frame = Frame(self)
notification_frame.pack(expand=True, fill=BOTH, padx=padx, pady=pady)
top_row = Frame(notification_frame)
top_row.pack(fill=X)
if not hasattr(notification_manager, "_close_icon"):
notification_manager._close_icon = PhotoImage(data="R0lGODlhEAAQAPeQAIsAAI0AAI4AAI8AAJIAAJUAAJQCApkAAJoAAJ4AAJkJCaAAAKYAAKcAAKcCAKcDA6cGAKgAAKsAAKsCAKwAAK0AAK8AAK4CAK8DAqUJAKULAKwLALAAALEAALIAALMAALMDALQAALUAALYAALcEALoAALsAALsCALwAAL8AALkJAL4NAL8NAKoTAKwbAbEQALMVAL0QAL0RAKsREaodHbkQELMsALg2ALk3ALs+ALE2FbgpKbA1Nbc1Nb44N8AAAMIWAMsvAMUgDMcxAKVABb9NBbVJErFYEq1iMrtoMr5kP8BKAMFLAMxKANBBANFCANJFANFEB9JKAMFcANFZANZcANpfAMJUEMZVEc5hAM5pAMluBdRsANR8AM9YOrdERMpIQs1UVMR5WNt8X8VgYMdlZcxtYtx4YNF/btp9eraNf9qXXNCCZsyLeNSLd8SSecySf82kd9qqc9uBgdyBgd+EhN6JgtSIiNuJieGHhOGLg+GKhOKamty1ste4sNO+ueenp+inp+HHrebGrefKuOPTzejWzera1O7b1vLb2/bl4vTu7fbw7ffx7vnz8f///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAJAALAAAAAAQABAAAAjUACEJHEiwYEEABniQKfNFgQCDkATQwAMokEU+PQgUFDAjjR09e/LUmUNnh8aBCcCgUeRmzBkzie6EeQBAoAAMXuA8ciRGCaJHfXzUMCAQgYooWN48anTokR8dQk4sELggBhQrU9Q8evSHiJQgLCIIfMDCSZUjhbYuQkLFCRAMAiOQGGLE0CNBcZYmaRIDLqQFGF60eTRoSxc5jwjhACFWIAgMLtgUocJFy5orL0IQRHAiQgsbRZYswbEhBIiCCH6EiJAhAwQMKU5DjHCi9gnZEHMTDAgAOw==")
close_button = Button(top_row, image=notification_manager._close_icon, highlightthickness=0, borderwidth=0, command=self.close)
close_button.pack(side=RIGHT, anchor=E)
self.interior = Frame(notification_frame)
self.interior.pack(expand=True, fill=BOTH)
if builder:
builder(self.interior)
if background is not None:
top_row.config(background=background)
notification_frame.config(background=background)
self.config(background=background)
self.interior.config(background=background)
close_button.config(background=background)
self.place(x,y, h, v)
@property
def x(self):
return self._offset_x
@property
def y(self):
return self._offset_y
@property
def h(self):
return self._h
@property
def v(self):
return self._v
def place(self, x, y, h, v):
''' The windows overall position on the screen '''
self.wm_geometry("{h}{x}{v}{y}".format(x=x,y=y, h=h, v=v))
self._offset_x = x
self._offset_y = y
self._h = h
self._v = v
def start_animation(self, easing_function, ticks, duration, start_time=0):
self._tick = 0
self._total_ticks = float(ticks)
self._easing_function = easing_function
self._duration = duration
self._interval_time = int(duration * 1000 / self._total_ticks)
if start_time != 0:
self.after(int(start_time*1000), self._animate)
else:
self._animate()
def _animate(self):
t = self._tick / self._total_ticks
# This changes the alpha value (How transparent the window should be).
# It ranges from 0.0 (completely transparent) to 1.0 (completely opaque).
self.attributes("-alpha", self._easing_function(1-t))
self._tick += 1
if self._tick <= self._total_ticks:
self.after(self._interval_time, self._animate)
else:
self.after(self._interval_time, self.close)
def close(self):
self._notification_manager.delete(self)
class Notification_Manager(object):
def __init__(self, offset_x=12, offset_y=8, corner=N+E, background=None, spacing=5, ticks=15, easing_function=pytweening.linear, duration=3, start_time=3, padx=5, pady=5):
if corner == N+W:
self._h = "+"
self._v = "+"
elif corner == N+E:
self._h = "-"
self._v = "+"
elif corner == S+W:
self._h = "+"
self._v = "-"
elif corner == S+E:
self._h = "-"
self._v = "-"
else:
raise ValueError("Not a valid corner value: %s"%corner)
self._list_of_notifications = []
self._offset_x = offset_x
self._offset_y = offset_y
self._padx = padx
self._pady = pady
self._corner = corner
self._background = background
self._ticks = ticks
self._duration = duration
self._easing_function = easing_function
self._spacing = spacing
self._start_time = start_time
@property
def corner(self):
return self._corner
@property
def background(self):
return self._background
@property
def duration(self):
return self._duration
@property
def spacing(self):
return self._spacing
@property
def ticks(self):
return self._ticks
def create_notification(self, builder, start_time=None, duration=None, easing_function=None,ticks=None, background=None, padx=None, pady=None, on_hide=None):
if ticks is None:
ticks = self._ticks
if builder is None:
builder = self._builder
notification.on_hide = on_hide
if duration is None:
duration = self._duration
if easing_function is None:
easing_function = self._easing_function
if background is None:
background = self._background
if padx is None:
padx = self._padx
if pady is None:
pady = self._pady
if start_time is None:
start_time = self._start_time
if len(self._list_of_notifications) == 0:
x = self._offset_x
y = self._offset_y
index = 0
else:
last_notification = self._list_of_notifications[-1]
last_notification.update_idletasks()
x = self._offset_x
y = last_notification.y + last_notification.winfo_height() + self._spacing
index = len(self._list_of_notifications)
notification = Notification(self, builder, index, x, y, self._h, self._v, padx, pady, background, on_hide)
self._list_of_notifications.append(notification)
notification.start_animation(easing_function=easing_function, ticks=ticks, duration=duration, start_time=start_time)
def simple_notification(self, text, foreground, background, font=None, width=None, anchor=None, justify=None, wraplength=None, start_time=None, duration=None, easing_function=None,ticks=None, padx=None, pady=None, on_hide=None):
builder = self.create_builder(text, foreground, background, font=font, width=width, anchor=anchor, justify=justify, wraplength=wraplength)
self.create_notification(builder, background= background, start_time=start_time, duration=duration, easing_function=easing_function, ticks=ticks, padx=padx, pady=pady, on_hide=on_hide)
def success(self, text, font=None, width=None, anchor=None, justify=None, wraplength=None, start_time=None, duration=None, easing_function=None,ticks=None, padx=None, pady=None, on_hide=None):
self.simple_notification(text, "white", SUCCESS_BACKGROUND, font=font, width=width, anchor=anchor, justify=justify, wraplength=wraplength, start_time=start_time, duration=duration, easing_function=easing_function, ticks=ticks, padx=padx, pady=pady, on_hide=on_hide)
def warning(self, text, font=None, width=None, anchor=None, justify=None, wraplength=None, start_time=None, duration=None, easing_function=None,ticks=None, padx=None, pady=None, on_hide=None):
self.simple_notification(text, "white", WARNING_BACKGROUND, font=font, width=width, anchor=anchor, justify=justify, wraplength=wraplength, start_time=start_time, duration=duration, easing_function=easing_function, ticks=ticks, padx=padx, pady=pady, on_hide=on_hide)
def alert(self, text, font=None, width=None, anchor=None, justify=None, wraplength=None, start_time=None, duration=None, easing_function=None,ticks=None, padx=None, pady=None, on_hide=None):
self.simple_notification(text, "white", ALERT_BACKGROUND, font=font, width=width, anchor=anchor, justify=justify, wraplength=wraplength, start_time=start_time, duration=duration, easing_function=easing_function, ticks=ticks, padx=padx, pady=pady, on_hide=on_hide)
def info(self, text, font=None, width=None, anchor=None, justify=None, wraplength=None, start_time=None, duration=None, easing_function=None,ticks=None, padx=None, pady=None, on_hide=None):
self.simple_notification(text, "white", INFO_BACKGROUND, font=font, width=width, anchor=anchor, justify=justify, wraplength=wraplength, start_time=start_time, duration=duration, easing_function=easing_function, ticks=ticks, padx=padx, pady=pady, on_hide=on_hide)
def create_builder(self, text, foreground, background, font=None, width=None, anchor=None, justify=None, wraplength=None):
kwargs = dict(text=text, fg=foreground, background=background)
if font:
kwargs["font"] = font
if anchor:
kwargs["anchor"] = anchor
if justify:
kwargs["justify"] = justify
if width:
kwargs["width"] = width
if wraplength:
kwargs["wraplength"] = wraplength
def builder(interior):
Label(interior, **kwargs).pack()
return builder
def delete(self, notification):
index = notification.index
height = notification.winfo_height()
self._list_of_notifications.pop(index)
notification.destroy()
x = self._offset_x
for i in range(index, len(self._list_of_notifications)):
_notification = self._list_of_notifications[i]
y = _notification.y - height - self._spacing
_notification.index = i
_notification.place(x, y, h=self._h, v=self._v)
if notification.on_hide:
notification.on_hide()
if __name__ == "__main__":
try:
from Tkinter import Tk, Label
except ImportError:
from tkinter import Tk, Label
root = Tk()
notification_manager = Notification_Manager(background="white")
def create_notification(start_time, text):
def notify():
def builder(interior):
Label(interior, text=text, background="white").pack()
notification_manager.create_notification(builder=builder)
root.after(start_time, notify)
create_notification(100, "this is a label")
create_notification(2500, "this is another label")
create_notification(5000, "this is the third label")
notification_manager.success("my succes message")
notification_manager.warning("warning!")
root.mainloop()
|