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()
 | 
Download
Copy to clipboard