Welcome, guest | Sign In | My Account | Store | Cart
# 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()

Diff to Previous Revision

--- revision 1 2017-04-01 19:18:42
+++ revision 2 2017-04-01 19:27:59
@@ -21,13 +21,13 @@
         
self.index = index
         
self.on_hide = on_hide
 
-        ''' Removes the native window boarder. '''
+        # Removes the native window boarder.
         
self.overrideredirect(True)
 
-        ''' Disables resizing of the widget.  '''
+        # Disables resizing of the widget.
         
self.resizable(False, False)
 
-        ''' Places window above all other windows in the window stack. '''
+        # Places window above all other windows in the window stack.
         
self.wm_attributes("-topmost", True)
 
         notification_frame
= Frame(self)

History