From 80f107cb9d91730372999abdf67a85fef995be7f Mon Sep 17 00:00:00 2001 From: Salman Kabir Date: Thu, 23 Nov 2023 10:17:02 +0600 Subject: [PATCH 1/3] working on notification --- databases/rss_feed.db | Bin 12288 -> 12288 bytes views/test_noti.py | 296 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 296 insertions(+) create mode 100644 views/test_noti.py diff --git a/databases/rss_feed.db b/databases/rss_feed.db index b09ec96f403e96f23faa6f418eec325832753824..fb80c1f685130bf0a6d377ada2fcb7a3e1abad52 100644 GIT binary patch literal 12288 zcmeI1-EQMV6vv%*vt`eihv@Z z2q*%IfFhs>C<1ptAbqm7@xj(sCG{nB0xkk}9w*7U$2m)uPS*ENdV9kj4ELV*d$805 zpM%ff;Aq(Uws!)9lY{T~PF~dDzuRH&#STbc1{{XHpN4RBjQd%?zXO3kxm>>XG<{Mj z$2H-zMO;fKYfEu0HQh&CS#4gcZD{pB>mP0YwYl^7m-qg7_wU+2wbyu2ZHj;*pa>`e zihv@Z2q*$~OyIOu)wa_5cRYxIz*#gC@#R>`DEXq%n9XLp)BMV=E5gQwaFRxLGuJR}(`|m0h-vKdXZWz2zIyafOAoIbeIYLW$tZid1HFliuSkf`wE=&{ z132O`6N5b#`V&797)URD`XhJX)QxGBnKfW2M6gm^Xh-IDB(o}l^jN3MyIsR<>UvZ6 z%uG3s%>}tb8TGi+bR5o|l3@DVJ4og1+8Bsf zPDfLoa7ZFfb3%hFITn+~{`Ph$kPKVt=A-@qKBHmuB@Fp%3CEH2L;qJQeKCP378l%= zE5*KWN#^>b&|hRjP@9x)L~Vl7BvU43Wpbljq;Ix#liH@!aky^VtW!?^!;Px;GCj_R zTIi2rif{cWM0w)iPZ$jCDQ=Dixtr9<+2Klhrh2fY(C>ReCm~os1A!Qsp13M<>l;M2M&asTe?umC- z>XEq&S;!DnA%zObRC2FmAwX^fid-z6QG-+3w5ZJtvz*K9K~+0UTiIlPP7@q2gPUQ$ zk14Da-K{CPF(sL4A*{DsrbXL~b-8Klyu4V<*6&l;z=eVt?GDvV%k>Pq)O(|~s`f1X zcpqWq(C^EDSlxerSj?=;A$ttf-v&y|@*rZ+EYkIC@wLHry3#VH-L-`8I&#@p-ve*wCZ_cQ_yNGD`U$#Zx}Ya5rV8vm)TAaR{=%Qj%ry{MLMnr zY=inHdw>DE+j|XtfKdm}(!~N}2!>ryfNUMW|M%yQA7y{k=!ZeT37F7ijyTYikCaA3 z`4#|0QQnJvN9^mZDGqkmC*r$NIsT`^_sY?W@Ak#r=7T?#gO&J@nkVIlr|zrAfCoGzVC=pegJ<81{bDe948kCq)vtY??l!76 zBC_1rsGT%xHfpzS!A4e_@7+t4gO?9}-~Vm@4JgYDL_*aaR%C;-|c7T35vxZ&N!i*@>Lu}Qvkt5 zyrjwOiu0I#*={eF%cDhppK`7*4t_3Svmxq?yQFRM>*g?cLR1Zyv z{tcsx1e34DpX2o4UQ5C0)8~j~9_Ew>Gy-G7c;*B}KJXHPX4~2lGFkXS29`2V$iTHs z;mGzKjPb-$b;nnI&8RwZKDeW#2k9HZ=E)V}9_8Q#@p^F##tDj8@jfeanYlYhtGVC_ zP9sjH3Ci7X!>?3^g)*!w=Y~sVSWzy#BW(DNMN~vIfwkMMdjH~XOZg?8p0D^7joQbr zQ8cAMSPui7dt>03#33y_k79xY6b6iMHROdTEJR^d6mCS}MznCN*`}&%riC?A*FAkw zar^U~mU59={Y4P^Kz$2_WV#4Z0?6AqA&g~+w?Ub=hh<~9XH5O6LAZ3Y-$i8Ak+VidBi0F+8_tpjr%nCrkor!a=G=@_QzV^t&Aw5ntA zPT3JzKYI6-5CbEeP2_U^9V3pw^MEZxruGFYAA?4kzn>Ro4GEEb9J?b&4D6|Gxl5-~inK diff --git a/views/test_noti.py b/views/test_noti.py new file mode 100644 index 0000000..8c723cd --- /dev/null +++ b/views/test_noti.py @@ -0,0 +1,296 @@ +from tkinter import * +import tkinter as tk +from tkinter.ttk import * +import tkinter.ttk +from tkinter import Canvas, Frame, BOTH +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() \ No newline at end of file From 6976860e901945e94735534e91fcd13c9e307ebe Mon Sep 17 00:00:00 2001 From: Salman Kabir Date: Fri, 1 Dec 2023 01:19:09 +0600 Subject: [PATCH 2/3] Working on notification --- classes/notification.py | 315 ++++++++++++++++++++++++++++++++++++--- classes/notifications.py | 63 -------- requirements.txt | 4 +- views/test.py | 12 -- views/test2.py | 1 + 5 files changed, 298 insertions(+), 97 deletions(-) delete mode 100644 classes/notifications.py delete mode 100644 views/test.py diff --git a/classes/notification.py b/classes/notification.py index dce6610..8c723cd 100644 --- a/classes/notification.py +++ b/classes/notification.py @@ -1,19 +1,296 @@ -import subprocess -from plyer import notification -import config.settings as settings -from icecream import ic - -class Notification: - - def __init__(self, new_data = []): - self.new_data = new_data - pass - - def get_notification(rss_feed_count): - if rss_feed_count > 0: - title = f"{settings.APP_NAME}: New Update" - # Title and message for the notification - message = f"{rss_feed_count} new rss feed update" - # Sending the notification - notification.notify(title=title, message=message, timeout=10) # Timeout is in seconds - pass \ No newline at end of file +from tkinter import * +import tkinter as tk +from tkinter.ttk import * +import tkinter.ttk +from tkinter import Canvas, Frame, BOTH +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() \ No newline at end of file diff --git a/classes/notifications.py b/classes/notifications.py deleted file mode 100644 index 3733c1c..0000000 --- a/classes/notifications.py +++ /dev/null @@ -1,63 +0,0 @@ -import subprocess -from plyer import notification -import config.settings as settings -from icecream import ic -import sys -import os -import time -from playsound import playsound -from plyer import notification -from classes.notifier.test_notifications import LinuxNotificationHandler -import threading - - -class NotificationHandler: - driver = LinuxNotificationHandler - notification_sound = settings.NOTIFICATION_SOUND_PATH - icon = settings.FAVICON_PATH - - def __init__(self, app_window): - self.app_window = app_window - - def get_driver(self): - if sys.platform.startswith('linux'): - return LinuxNotificationHandler - else: - raise OSError('Please develop a Notification Handler like ( LinuxNotificationHandler ) in classes.notifier') - - def make_notification(self, new_data): - count = 0 - if new_data: - count = len(new_data) - - notification_details = dict( - title=f"Refreshed Feed : {count} new posts found!", - message='\n'.join(new_data), - app_name=settings.APP_NAME, - timeout=10, - icon=self.icon, - ) - return notification_details - - def show_notification(self, new_data): - driver = self.get_driver() - handler = driver() - - notification_details = self.make_notification(new_data) - - handler.show_notification(notification_details) - - notification_details = self.make_notification(new_data) - - # driver.notify(**notification_details) - - playsound(self.notification_sound) - - def handle_notifications(self, new_data): - self.show_notification(new_data) - notification_thread = threading.Thread(target=self.show_notification, args=(new_data,)) - notification_thread.start() - # notification.on_click(self.open_app_window) - - # def open_app_window(self, *args): - # self.app_window.deiconify() diff --git a/requirements.txt b/requirements.txt index 9bbce32..4ae4b96 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,6 +9,4 @@ sgmllib3k==1.0.0 tk==0.1.0 tksheet==6.2.9 icecream==2.1.3 -plyer==2.1.0 -playsound==1.3.0 -desktop-notifier==3.5.6 +pytweening==1.0.7 diff --git a/views/test.py b/views/test.py deleted file mode 100644 index 61ad62f..0000000 --- a/views/test.py +++ /dev/null @@ -1,12 +0,0 @@ -import asyncio -from desktop_notifier import DesktopNotifier - -notifier = DesktopNotifier() - -async def main(): - n = await notifier.send(title="Hello world!", message="Sent from Python") - await asyncio.sleep(5) # wait a bit before clearing notification - await notifier.clear(n) # removes the notification - await notifier.clear_all() # removes all notifications for this app - -asyncio.run(main()) \ No newline at end of file diff --git a/views/test2.py b/views/test2.py index 9dda064..cddf244 100644 --- a/views/test2.py +++ b/views/test2.py @@ -5,6 +5,7 @@ from tkinter import messagebox + class RssReaderApp: def __init__(self, master): self.master = master From f203bb2e04a24347014bc188fabfb0cf6307457a Mon Sep 17 00:00:00 2001 From: Salman Kabir Date: Fri, 1 Dec 2023 11:34:10 +0600 Subject: [PATCH 3/3] Working on notification --- classes/notification.py | 11 +++--- classes/notifier/test_notifications.py | 47 ------------------------- databases/rss_feed.db | Bin 12288 -> 12288 bytes views/view.py | 15 ++++---- 4 files changed, 15 insertions(+), 58 deletions(-) delete mode 100644 classes/notifier/test_notifications.py diff --git a/classes/notification.py b/classes/notification.py index 8c723cd..c85ca10 100644 --- a/classes/notification.py +++ b/classes/notification.py @@ -286,11 +286,12 @@ def builder(interior): 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") + # 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!") + # notification_manager.success("my succes message") + # notification_manager.warning("warning!") + # notification_manager.info("info!") root.mainloop() \ No newline at end of file diff --git a/classes/notifier/test_notifications.py b/classes/notifier/test_notifications.py deleted file mode 100644 index 096280a..0000000 --- a/classes/notifier/test_notifications.py +++ /dev/null @@ -1,47 +0,0 @@ -import os -import dbus -import dbus.mainloop.glib -import dbus.exceptions - - -dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) - - -class LinuxNotificationHandler: - def show_notification(self, notification_details): - bus = dbus.SessionBus() - notifications = bus.get_object("org.freedesktop.Notifications", "/org/freedesktop/Notifications") - interface = dbus.Interface(notifications, "org.freedesktop.Notifications") - - notification_id = str(os.getpid()) - - notification_data = {"app_pid": notification_id} - - interface.Notify( - notification_details.get("app_name"), - 0, - notification_details.get("icon"), - notification_details.get("title"), - notification_details.get("message"), - [], - notification_data, - 10000 - ) - notifications.connect_to_signal("NotificationClosed", self.onclick_function) - - # def get_clicked_event(self, onclick_function): - # bus = dbus.SessionBus() - # notifier = bus.get_object("org.freedesktop.Notifications", "/org/freedesktop/Notifications") - # notifier.connect_to_signal("ActionInvoked", onclick_function) - - def onclick_function(self, notification_id, action, *args): - # Assuming the notification_id contains the PID of the application - try: - pid = int(notification_id) # Extract PID from the notification ID (this could be a unique identifier) - os.system(f"wmctrl -ia {pid}") # Bring the window with the specified PID to the foreground - except ValueError: - print("Invalid PID") - - -# lnh = LinuxNotificationHandler() -# lnh.show_notification("Random Title", "Random details") diff --git a/databases/rss_feed.db b/databases/rss_feed.db index fb80c1f685130bf0a6d377ada2fcb7a3e1abad52..8d4dc8d0e9d46d711fd10550f7f54504672c14f2 100644 GIT binary patch literal 12288 zcmeHNU2oe)7Nwo2shtlOD1t7wF9Q^d*r;#JZ&FzltzyfL6WLKDr`ci`2!_LKJ>l+W1sd8hf=5pK~-S;v?O4g8hXw>bLP%~_l)|_I$_31 z5GP}r5kvV%+1OCNB1BO({-G$!HT+pzn|O15@d5vCRPNt%ix~GWf4q%PH}CwY> z{g(@J{uFF8ZF~9MjViB*&))J{`LMadYvs}J!AsQU+uMe+^JZt~&R=i; z`}SwIzQ6gOo1bp~3mfaJR-jg(R-jg(R-jg(R-jhkT?O(_w;Rg0`SU(YIFHC-JdHA* z5FnlK6({|eg>*#v86K`2h3RBO=lJOPWELk^!z`Pm4-O7yv)TP=(Q==~ZbHC?)M_?OqY^X! z+lQ!~^WDyI7=`JOoXyGMkY*>{3t|eDG9G2*ghxD~St;S}izFt@`0SN;o3=|lW zQosU}xtg!4s;!!yU8VGgMnjq9r>)b2vku|W02$y4-|ckH_DQdGvQHjO2iQ0`ri|~C zR`)a&v;OOgd;tZB1PBBb91t*)n(kUgQ)iB9W_H?JhIofO}?fl*$QkW*>K&;w9LdRoH)+1E(6H8F`Dkso)+@@~p zO|I)ETZ#8(qoMTkKbL!Hxgp1sEF6b_$7P9|>N<&3Zm6<5RJ%(8zNV_Wt257Uss?4% z#J6vR-jg(R-jg(R-jg(R^VL){{QO# z)ndLh7NGDI;H|9yXe-D%6>SAkJd~z_d?Qsg&C`8qI#!c$&#hV((+@?dua&su1^37i zN9!WSLUgUS;RPe?3Co@`tmg#GY-%>e*I{a!LFLUq!)o|R4vWrTPdRzq|4mhUrR!=D zu6mXgaEnnjaGOkPR-)#2vD^*wXV~`@*5CXv###ft8`2%oOnfPKG4`=A8`h}>BoZu< zV1cyILLil8L11&NN2q0)re;xI<@KO}wcE#ed?C&^ACU7Y(uzm%U=9yy%6)Q9u_h`F z2~no(mZLxDamup$mur^51w8x`9R&Kq?m+bAtQ6pqyl$aKq`6Jop^j~6W|h2ty(m1d z3JWfc8N3r8cJV?j73voITyl62vFVEHVVuP3tb<0LRz9nvZR2@C>wNm%$G71F+? zTRwMP(=Y;6v(+l;R8-4PzT4}E;|U%q&$yS0Z)O)otWbEG3I}8|oWMwWdAni6MpM7= zMdtly9WoBF>J&xe-aZ*-V>J6%idG-Jq_5W-mJAJLXjp`XA~aM&!!k6SiO{eN4I(gL zTo7U9R2dGlITP-liPfjSlKc7W(zbL{Q(0hX%=CO}S2v6NWCLk`md{TK9i=guq0=XP z3_nc8w+=`v;gsyMFq`j@Ac@E1kS2pz>?W9AXzC^*W@KL`;yR031OX8Q$_OALfCvBr f9}0Z2>j=wOpo2u0BT;>-tJE+Z+r+dBsuS>E56l-c literal 12288 zcmeI1-EQMV6vv%*vt`eihv@Z z2q*%IfFhs>C<1ptAbqm7@xj(sCG{nB0xkk}9w*7U$2m)uPS*ENdV9kj4ELV*d$805 zpM%ff;Aq(Uws!)9lY{T~PF~dDzuRH&#STbc1{{XHpN4RBjQd%?zXO3kxm>>XG<{Mj z$2H-zMO;fKYfEu0HQh&CS#4gcZD{pB>mP0YwYl^7m-qg7_wU+2wbyu2ZHj;*pa>`e zihv@Z2q*$~OyIOu)wa_5cRYxIz*#gC@#R>`DEXq%n9XLp)BMV=E5gQwaFRxLGuJR}(`|m0h-vKdXZWz2zIyafOAoIbeIYLW$tZid1HFliuSkf`wE=&{ z132O`6N5b#`V&797)URD`XhJX)QxGBnKfW2M6gm^Xh-IDB(o}l^jN3MyIsR<>UvZ6 z%uG3s%>}tb8TGi+bR5o|l3@DVJ4og1+8Bsf zPDfLoa7ZFfb3%hFITn+~{`Ph$kPKVt=A-@qKBHmuB@Fp%3CEH2L;qJQeKCP378l%= zE5*KWN#^>b&|hRjP@9x)L~Vl7BvU43Wpbljq;Ix#liH@!aky^VtW!?^!;Px;GCj_R zTIi2rif{cWM0w)iPZ$jCDQ=Dixtr9<+2Klhrh2fY(C>ReCm~os1A!Qsp13M<>l;M2M&asTe?umC- z>XEq&S;!DnA%zObRC2FmAwX^fid-z6QG-+3w5ZJtvz*K9K~+0UTiIlPP7@q2gPUQ$ zk14Da-K{CPF(sL4A*{DsrbXL~b-8Klyu4V<*6&l;z=eVt?GDvV%k>Pq)O(|~s`f1X zcpqWq(C^EDSlxerSj?=;A$ttf-v&y|@*rZ+EYkIC@wLHry3#VH-L-`8I&#@p-ve*wCZ_cQ>", self.on_select) self.schedule_refresh(link_input, interval)