import subprocess import gi from timer import Timer gi.require_version('Gtk', '4.0') gi.require_version('Adw', '1') from gi.repository import GLib, Gio, Gtk, Adw class Application(Adw.Application): def __init__(self) -> None: super().__init__(application_id='de.p1st.dndbuster') self.window: AppWindow | None = None def do_activate(self) -> None: # Windows are associated with the application. # When the last one is closed the application shuts down. self.window = AppWindow(application=self) self.window.present() action = Gio.SimpleAction.new('restart-timer', None) action.connect('activate', self.on_notification_button_restart) self.add_action(action) def send_notification_timeout(self) -> None: notification = Gio.Notification() notification.set_title('Timeout') notification.set_body('The time is over and your do-not-disturb mode just got busted!') notification.set_priority(Gio.NotificationPriority.URGENT) notification.add_button(label='Restart', detailed_action='app.restart-timer') self.send_notification(id=f'{self.get_application_id()}.{Timer.current_time()}', notification=notification) # https://0pointer.de/public/sound-naming-spec.html sound_name = 'alarm-clock-elapsed' subprocess.run(['canberra-gtk-play', '-i', sound_name]) def on_notification_button_restart(self, _action: Gio.SimpleAction, _other: None): self.window.restart() class AppWindow(Gtk.ApplicationWindow): def __init__(self, application: Application) -> None: super().__init__(application=application, title='DnDBuster') self.app = application self.set_default_size(400, 200) self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.box.set_spacing(10) self.box.set_margin_top(10) self.box.set_margin_bottom(10) self.box.set_margin_start(10) self.box.set_margin_end(10) self.set_child(self.box) self.button_start_restart = Gtk.Button(label='Start') self.button_start_restart.connect('clicked', self.on_button_start_restart_clicked) self.box.append(self.button_start_restart) self.button_pause_resume = Gtk.Button(label='Pause', visible=False) self.button_pause_resume.connect('clicked', self.on_button_pause_resume_clicked) self.box.append(self.button_pause_resume) self.progress_bar = Gtk.ProgressBar(visible=False) self.box.append(self.progress_bar) self.timer = Timer() def on_button_start_restart_clicked(self, _widget) -> None: if not self.timer.is_started(): self.start() else: self.restart() def on_button_pause_resume_clicked(self, _widget) -> None: if self.timer.is_paused(): self.timer.resume() self.regularly_update_progress_bar() else: self.timer.pause() self.update_pause_resume_label() def start(self): self.reset_start_timer() self.button_start_restart.set_label('Restart') self.button_pause_resume.set_visible(True) def restart(self): self.reset_start_timer() self.update_pause_resume_label() def update_pause_resume_label(self) -> None: if self.timer.is_paused(): self.button_pause_resume.set_label('Resume') else: self.button_pause_resume.set_label('Pause') def reset_start_timer(self) -> None: self.timer.reset() self.timer.start() self.progress_bar.set_visible(True) self.regularly_update_progress_bar() def regularly_update_progress_bar(self) -> None: # Start regularly calling update_progress() GLib.timeout_add_seconds(1, self.update_progress_bar) def update_progress_bar(self) -> bool: total_min = 25 total_sec = total_min * 60 delta_sec = self.timer.read() progress = delta_sec / total_sec self.progress_bar.set_fraction(progress) if progress >= 1: self.app.send_notification_timeout() # Stop regularly calling update_progress() return False # Weather to continue regularly calling update_progress() return not self.timer.is_paused()