From aa62998bb58c64c2f3b6954248b553efbaaa4033 Mon Sep 17 00:00:00 2001 From: WasAlexHere Date: Sat, 24 Jun 2023 13:13:13 +0300 Subject: [PATCH] fixed issues and addedd additional features --- README.md | 10 +++--- app.py | 95 ++++++++++++++++++++++++++++++++++++++---------------- down.png | Bin 11778 -> 11778 bytes main.png | Bin 12868 -> 12868 bytes rest.png | Bin 0 -> 8693 bytes setup.py | 4 +-- up.png | Bin 11882 -> 11882 bytes 7 files changed, 73 insertions(+), 36 deletions(-) create mode 100644 rest.png diff --git a/README.md b/README.md index 2d72740..4b4ceaf 100644 --- a/README.md +++ b/README.md @@ -2,23 +2,21 @@ # StndApp – macOS Menubar App -Current release: [StndApp (Version 0.1.0)](https://github.com/WasAlexHere/stndApp/releases/tag/0.1.0) +Current release: [StndApp (Version 1.0.0)](https://github.com/WasAlexHere/stndApp/releases/tag/1.0.0) ![Screenshot](screenshots/screenshot.png) Menubar app to control stand/sit routine. It will inform you when you need to sit or stand during your work day. Just hit 'Start' button and pay attention to notifications. If you need to take a brake, just hit 'Pause' button and resume when you're ready. -Stand/Sit intervals are 45 and 15 minutes by default, but you can modify them by clicking Edit button. +Stand/Sit intervals are 30 and 30 minutes by default, but you can modify them by clicking 'Edit' button. +If you'ver reached the limit for standing work per a day, the app will tell you to take a rest and continue tomorrow. ## Features - Continuous alerts to stand up (for standing work) and sit down (for sitting work) during a work day. - Start/Pause/Stop functionality. - Displaying time for each routine with corresponding icon. - Modification of sit/stand intervals. - - -## Upcoming ideas - Allow to stand for 4 hours a day. -- Support dark mode. \ No newline at end of file +- Support dark mode. diff --git a/app.py b/app.py index b6f23ea..85c7d02 100644 --- a/app.py +++ b/app.py @@ -1,10 +1,12 @@ import rumps +from datetime import datetime main_icon = "main.png" up_icon = "up.png" down_icon = "down.png" trophy_icon = "trophy.png" edit_icon = "edit.png" +rest_icon = "rest.png" class LessThanAnHour(Exception): @@ -14,8 +16,9 @@ class LessThanAnHour(Exception): class StndApp(rumps.App): def __init__(self): self._seconds = 60 - self._stand_interval = 45 - self._sit_interval = 15 + self._stand_interval = 30 + self._sit_interval = 30 + self._stand_mins_amount = 0 self.config = { "stand": "Alright, alright, alright!", @@ -24,10 +27,15 @@ def __init__(self): "sit_message": f"Sit down for {self._sit_interval} minutes", "failed_value": "Unfortunately, you can only enter integer numbers!", "failed_less": "Unfortunately, you can only enter numbers less than 61!", + "rest": "Hold on, tiger!", + "rest_message": f"You've reached the limit for standing work (4 hours a day). \n" + f"(You're standing hours are {self._stand_mins_amount // 60}). \n" + f"Take a rest and continue tomorrow!", } + self.last_timer = {"last": "stand"} - super(StndApp, self).__init__("stndApp", icon=main_icon) + super(StndApp, self).__init__("stndApp", icon=main_icon, template=True) self.menu = ["Start", "Stop", "Edit", None] # rumps.debug_mode(True) @@ -35,27 +43,33 @@ def __init__(self): self.stand_timer = rumps.Timer(self.countdown_stand, 1) self.sit_timer = rumps.Timer(self.countdown_sit, 1) + self.check_date = rumps.Timer(self.date_validation, 4 * 3600) + self.check_date.start() + self.n = self._stand_interval * self._seconds @rumps.clicked("Start") def start_button(self, sender): - if sender.title == "Start": - if self.last_timer.get("last") == "stand": - self.stand_timer.start() - self.icon = up_icon - self.last_timer["last"] = "stand" + if self._stand_mins_amount / 60 < 4: + if sender.title == "Start": + if self.last_timer.get("last") == "stand": + self.stand_timer.start() + self.icon = up_icon + self.last_timer["last"] = "stand" + else: + self.sit_timer.start() + self.icon = down_icon + self.last_timer["last"] = "sit" + sender.title = "Pause" + self.menu["Edit"].hidden = True else: - self.sit_timer.start() - self.icon = down_icon - self.last_timer["last"] = "sit" - sender.title = "Pause" - self.menu["Edit"].hidden = True + sender.title = "Start" + if self.stand_timer.is_alive(): + self.stand_timer.stop() + if self.sit_timer.is_alive(): + self.sit_timer.stop() else: - sender.title = "Start" - if self.stand_timer.is_alive(): - self.stand_timer.stop() - if self.sit_timer.is_alive(): - self.sit_timer.stop() + self.rest_alert() @rumps.clicked("Stop") def stop_button(self, _): @@ -69,7 +83,7 @@ def stop_button(self, _): self.n = self._stand_interval * self._seconds self.last_timer["last"] = "stand" self.menu["Edit"].hidden = False - self.icon = "main.png" + self.icon = main_icon @rumps.clicked("Edit") def edit_button(self, _): @@ -82,6 +96,7 @@ def edit_button(self, _): if 0 < new < 61: self._stand_interval = new self._sit_interval = 60 - self._stand_interval + self.n = self._stand_interval * self._seconds self.successful_notification() else: self.failed_notification( @@ -114,6 +129,7 @@ def countdown_stand(self, _): "sit", down_icon, ) + self._stand_mins_amount += self._stand_interval if response.clicked == 2: self.stop_button(_) @@ -127,13 +143,17 @@ def countdown_sit(self, _): else: response = self.stand_alert() if response.clicked == 1: - self.change_interval( - self.sit_timer, - self.stand_timer, - self._stand_interval * self._seconds, - "stand", - up_icon, - ) + if self._stand_mins_amount / 60 < 4: + self.change_interval( + self.sit_timer, + self.stand_timer, + self._stand_interval * self._seconds, + "stand", + up_icon, + ) + else: + self.rest_alert() + self.stop_button(_) if response.clicked == 2: self.stop_button(_) @@ -144,7 +164,7 @@ def stand_alert(self): ok="Stand Up", dimensions=(0, 0), ) - window.icon = "trophy.png" + window.icon = trophy_icon window.add_button("Stop") return window.run() @@ -156,7 +176,7 @@ def sit_alert(self): ok="Sit Down", dimensions=(0, 0), ) - window.icon = "trophy.png" + window.icon = trophy_icon window.add_button("Stop") return window.run() @@ -194,6 +214,25 @@ def change_interval(self, func1, func2, num, action, new): self.last_timer["last"] = action self.icon = new + def date_validation(self, _): + current_date = datetime.now() + year = datetime.now().year + month = datetime.now().month + day = datetime.now().day + 1 + + if current_date >= datetime(year, month, day): + self._stand_mins_amount = 0 + + def rest_alert(self): + window = rumps.Window( + title=self.config["rest"], + message=self.config["rest_message"], + dimensions=(0, 0), + ) + window.icon = rest_icon + + return window.run() + if __name__ == "__main__": StndApp().run() diff --git a/down.png b/down.png index 3acf1756ff6d890083d50ffe5b0797ac9c9fe795..da2378e82d23a3216b0b4d8910ff3f38162da436 100644 GIT binary patch delta 34 pcmZpQX^PpP%fW4A5MpFxWoT+;YPs2hgNunJ!~2oQW;d=q$^fV(37r4{ delta 34 pcmZpQX^PpP%fW4E5n^a=Wo%$&V!YXcgNun}Q<(m)&2C(KlmV)93FZI* diff --git a/main.png b/main.png index 5d6fed0e423818e46494a8b4999a4b3199394a83..aecf69dc9d6ca18195f98174ceca1582a2032f26 100644 GIT binary patch delta 34 qcmX?-awKJgE(f=fL5Pu&m7$rHp}}Sg4lX7ZzJCjrZg%7Prw#zV_6rjL delta 34 qcmX?-awKJgE(f=vMTnufm9e3ffzf6Q4lX8^&4>N=Zg%7Prw#zZ8VgJS diff --git a/rest.png b/rest.png new file mode 100644 index 0000000000000000000000000000000000000000..d33a7e92567c664d3157eaea6d19c08e84713a20 GIT binary patch literal 8693 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 literal 0 HcmV?d00001 diff --git a/setup.py b/setup.py index 213f630..6f440f7 100644 --- a/setup.py +++ b/setup.py @@ -1,12 +1,12 @@ from setuptools import setup APP = ["app.py"] -DATA_FILES = ["main.png", "up.png", "down.png", "trophy.png", "edit.png"] +DATA_FILES = ["main.png", "up.png", "down.png", "trophy.png", "edit.png", "rest.png"] OPTIONS = { 'argv_emulation': True, 'iconfile': 'icon.icns', 'plist': { - 'CFBundleShortVersionString': '0.1.0', + 'CFBundleShortVersionString': '1.0.0', 'LSUIElement': True, }, 'packages': ['rumps'], diff --git a/up.png b/up.png index a4d03a1e6180fbbce0e16b9b62c23084f163c962..ab5ae05bf177586ea789d9622add720bf2d75c05 100644 GIT binary patch delta 31 mcmaDA^D1V8HV221L5Pu&m7(coV-8*>md2T->6=}+)Kmbdo(XgS delta 31 ncmaDA^D1V8HV22HMTnufm9gPwV-8*>mZp0@)i%3usi^<}t*Z(K