diff --git a/kawai_focus/main.py b/kawai_focus/main.py index fd29d68..a95fe02 100644 --- a/kawai_focus/main.py +++ b/kawai_focus/main.py @@ -2,6 +2,8 @@ import kivy kivy.require('2.3.1') from kivy.app import App +from kivy.uix.screenmanager import ScreenManager +from kawai_focus.screens.timer_screen import TimerScreen class KawaiFocusApp(App): @@ -10,7 +12,9 @@ class KawaiFocusApp(App): title = 'Kawai.Focus' def build(self): - pass + screen_manager = ScreenManager() + screen_manager.add_widget(TimerScreen(name='timers_screen')) + return screen_manager def main() -> None: diff --git a/kawai_focus/screens/__init__.py b/kawai_focus/screens/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/kawai_focus/screens/timer_screen.py b/kawai_focus/screens/timer_screen.py new file mode 100644 index 0000000..9d9a633 --- /dev/null +++ b/kawai_focus/screens/timer_screen.py @@ -0,0 +1,116 @@ +from kivy.uix.screenmanager import Screen +from kivy.uix.floatlayout import FloatLayout +from kivy.uix.button import Button +from kivy.uix.label import Label +from kivy.clock import Clock + +from kawai_focus.utils import custom_timer + + +class TimerScreen(Screen): + """Экран таймера""" + + def __init__(self, **kwargs): + super(TimerScreen, self).__init__(**kwargs) + + self.layout = FloatLayout() + + # Добавляем кнопку "Старт" + self.start_button = Button( + text='Старт', + size_hint=(None, None), + height=40, + width=100, + pos_hint={ + 'center_x': 0.3, + 'center_y': 0.5 + } + ) + self.start_button.bind(on_release=self.start_timer) + self.layout.add_widget(self.start_button) + + # Добавляем кнопку "Пауза" + self.pause_button = Button( + text='Пауза', + size_hint=(None, None), + height=40, + width=100, + pos_hint={ + 'center_x': 0.5, + 'center_y': 0.5 + } + ) + self.pause_button.bind(on_release=self.pause_timer) + self.layout.add_widget(self.pause_button) + + # Добавляем кнопку "Стоп" + self.stop_button = Button( + text='Стоп', + size_hint=(None, None), + height=40, + width=100, + pos_hint={ + 'center_x': 0.7, + 'center_y': 0.5 + } + ) + self.stop_button.bind(on_release=self.stop_timer) + self.layout.add_widget(self.stop_button) + + # Добавляем Label для отображения времени + self.time_label = Label( + text='00:00:10', + size_hint=(None, None), + pos_hint={ + 'center_x': 0.5, + 'center_y': 0.6 + } + ) + self.layout.add_widget(self.time_label) + + self.add_widget(self.layout) + + # Переменные для управления таймером + self.timer_generator = None + self.paused = False + self.remaining_time = None + + def start_timer(self, instance) -> None: + if self.paused: + self.paused = False + else: + # Инициализация генератора таймера + self.timer_generator = custom_timer('0h0m10s') # Установите необходимое время + self.remaining_time = next(self.timer_generator, '00:00:00') + + # Запуск обновления времени каждую секунду + Clock.schedule_interval(self.update_time, 1) + + def pause_timer(self, instance) -> None: + if not self.paused: + self.paused = True + # Остановка обновления времени + Clock.unschedule(self.update_time) + + def stop_timer(self, instance) -> None: + # Остановка обновления времени + Clock.unschedule(self.update_time) + self.paused = False + self.remaining_time = '00:00:00' + self.time_label.text = self.remaining_time + + def update_time(self, dt) -> None: + if self.paused: + return + + try: + # Получение следующего значения времени из генератора + self.remaining_time = next(self.timer_generator, '00:00:00') + self.time_label.text = self.remaining_time + + if self.remaining_time == '00:00:00': + # Остановка обновления времени + Clock.unschedule(self.update_time) + except StopIteration: + # Остановка обновления времени при завершении генератора + Clock.unschedule(self.update_time) diff --git a/kawai_focus/utils.py b/kawai_focus/utils.py new file mode 100644 index 0000000..9b10282 --- /dev/null +++ b/kawai_focus/utils.py @@ -0,0 +1,36 @@ +import time +import re +from typing import Generator +import logging + +from kivy.logger import Logger + + +Logger.setLevel(logging.DEBUG) + + +def custom_timer(timer_str: str) -> Generator[str, None, None]: + """Функция отсчитывает время, установленное для таймера в формате 'XhYmZs'.""" + + try: + match = re.match(r'(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?', timer_str) + if not match: + raise ValueError('Неверный формат времени, используйте "XhYmZs"!') + + hours, minutes, seconds = match.groups() + total_seconds = (int(hours or 0) * 3600) + (int(minutes or 0) * 60) + (int(seconds or 0)) + + if total_seconds == 0: + raise ValueError('Не указано время таймера!') + + for remaining in range(total_seconds, -1, -1): + yield f"{remaining // 3600:02d}:{(remaining % 3600) // 60:02d}:{remaining % 60:02d}" + except ValueError as err: + Logger.error(f'{err.__class__.__name__}: {err}') + + +if __name__ == '__main__': + # Пример использования + + for time_now in custom_timer('0h1m3s'): + print(time_now) diff --git a/poetry.lock b/poetry.lock index c3ea599..c87ff41 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,14 +1,15 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. [[package]] name = "certifi" -version = "2024.12.14" +version = "2025.1.31" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["main"] files = [ - {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, - {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, + {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, + {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, ] [[package]] @@ -17,6 +18,7 @@ version = "3.4.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, @@ -118,6 +120,7 @@ version = "0.21.2" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, @@ -129,6 +132,7 @@ version = "1.2.0" description = "Infer file type and MIME type of any file/buffer. No external dependencies." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25"}, {file = "filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb"}, @@ -140,6 +144,7 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -154,6 +159,7 @@ version = "2.3.1" description = "An open-source Python framework for developing GUI apps that work cross-platform, including desktop, mobile and embedded platforms." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "Kivy-2.3.1-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:ace93c166c9400f9435cfd3bd179b5ef9fdd40d69ee8171a6b8beba08c402d09"}, {file = "Kivy-2.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d6215762510b463b0461d173f8a0b22e449beb12ba79cf151e18aa1d3d12a40"}, @@ -194,14 +200,14 @@ pypiwin32 = {version = "*", markers = "sys_platform == \"win32\""} requests = "*" [package.extras] -angle = ["kivy-deps.angle (>=0.4.0,<0.5.0)"] +angle = ["kivy-deps.angle (>=0.4.0,<0.5.0) ; sys_platform == \"win32\""] base = ["pillow (>=9.5.0,<11)"] -dev = ["flake8", "kivy-deps.glew-dev (>=0.3.1,<0.4.0)", "kivy-deps.gstreamer-dev (>=0.3.3,<0.4.0)", "kivy-deps.sdl2-dev (>=0.8.0,<0.9.0)", "pre-commit", "pyinstaller", "pytest (>=3.6)", "pytest-asyncio (!=0.11.0)", "pytest-benchmark", "pytest-cov", "pytest-timeout", "responses", "sphinx (>=6.2.1,<6.3.0)", "sphinxcontrib-jquery (>=4.1,<5.0)"] -full = ["ffpyplayer", "kivy-deps.gstreamer (>=0.3.3,<0.4.0)", "pillow (>=9.5.0,<11)"] -glew = ["kivy-deps.glew (>=0.3.1,<0.4.0)"] -gstreamer = ["kivy-deps.gstreamer (>=0.3.3,<0.4.0)"] -media = ["ffpyplayer", "kivy-deps.gstreamer (>=0.3.3,<0.4.0)"] -sdl2 = ["kivy-deps.sdl2 (>=0.8.0,<0.9.0)"] +dev = ["flake8", "kivy-deps.glew-dev (>=0.3.1,<0.4.0) ; sys_platform == \"win32\"", "kivy-deps.gstreamer-dev (>=0.3.3,<0.4.0) ; sys_platform == \"win32\"", "kivy-deps.sdl2-dev (>=0.8.0,<0.9.0) ; sys_platform == \"win32\"", "pre-commit", "pyinstaller", "pytest (>=3.6)", "pytest-asyncio (!=0.11.0)", "pytest-benchmark", "pytest-cov", "pytest-timeout", "responses", "sphinx (>=6.2.1,<6.3.0)", "sphinxcontrib-jquery (>=4.1,<5.0)"] +full = ["ffpyplayer ; sys_platform == \"linux\" or sys_platform == \"darwin\"", "kivy-deps.gstreamer (>=0.3.3,<0.4.0) ; sys_platform == \"win32\"", "pillow (>=9.5.0,<11)"] +glew = ["kivy-deps.glew (>=0.3.1,<0.4.0) ; sys_platform == \"win32\""] +gstreamer = ["kivy-deps.gstreamer (>=0.3.3,<0.4.0) ; sys_platform == \"win32\""] +media = ["ffpyplayer ; sys_platform == \"linux\" or sys_platform == \"darwin\"", "kivy-deps.gstreamer (>=0.3.3,<0.4.0) ; sys_platform == \"win32\""] +sdl2 = ["kivy-deps.sdl2 (>=0.8.0,<0.9.0) ; sys_platform == \"win32\""] tuio = ["oscpy"] [[package]] @@ -210,6 +216,8 @@ version = "0.4.0" description = "Repackaged binary dependency of Kivy." optional = false python-versions = "*" +groups = ["main"] +markers = "sys_platform == \"win32\"" files = [ {file = "kivy_deps.angle-0.4.0-cp310-cp310-win32.whl", hash = "sha256:7873a551e488afa5044c4949a4aa42c4a4c4290469f0a6dd861e6b95283c9638"}, {file = "kivy_deps.angle-0.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:71f2f01a3a7bbe1d4790e2a64e64a0ea8ae154418462ea407799ed66898b2c1f"}, @@ -232,6 +240,8 @@ version = "0.3.1" description = "Repackaged binary dependency of Kivy." optional = false python-versions = "*" +groups = ["main"] +markers = "sys_platform == \"win32\"" files = [ {file = "kivy_deps.glew-0.3.1-cp310-cp310-win32.whl", hash = "sha256:8f4b3ed15acb62474909b6d41661ffb4da9eb502bb5684301fb2da668f288a58"}, {file = "kivy_deps.glew-0.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:aef2d2a93f129d8425c75234e7f6cc0a34b59a4aee67f6d2cd7a5fdfa9915b53"}, @@ -254,6 +264,8 @@ version = "0.8.0" description = "Repackaged binary dependency of Kivy." optional = false python-versions = "*" +groups = ["main"] +markers = "sys_platform == \"win32\"" files = [ {file = "kivy_deps.sdl2-0.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:5af0a3b318a6ec9e0f0c1d476a4af4b2d0cbcce4dbfd89bc4681c33bcd6b3bcd"}, {file = "kivy_deps.sdl2-0.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ae3735480841ec9a57c0fb26e8647adee474a3d746147e3d75a1fc177c0fbc01"}, @@ -269,6 +281,7 @@ version = "0.1.5" description = "" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "Kivy Garden-0.1.5.tar.gz", hash = "sha256:2b8377378e87501d5d271f33d94f0e44c089884572c64f89c9d609b1f86a2748"}, {file = "Kivy_Garden-0.1.5-py3-none-any.whl", hash = "sha256:ef50f44b96358cf10ac5665f27a4751bb34ef54051c54b93af891f80afe42929"}, @@ -283,6 +296,7 @@ version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, @@ -297,6 +311,8 @@ version = "223" description = "" optional = false python-versions = "*" +groups = ["main"] +markers = "sys_platform == \"win32\"" files = [ {file = "pypiwin32-223-py3-none-any.whl", hash = "sha256:67adf399debc1d5d14dffc1ab5acacb800da569754fafdc576b2a039485aa775"}, {file = "pypiwin32-223.tar.gz", hash = "sha256:71be40c1fbd28594214ecaecb58e7aa8b708eabfa0125c8a109ebd51edbd776a"}, @@ -311,6 +327,8 @@ version = "308" description = "Python for Window Extensions" optional = false python-versions = "*" +groups = ["main"] +markers = "sys_platform == \"win32\"" files = [ {file = "pywin32-308-cp310-cp310-win32.whl", hash = "sha256:796ff4426437896550d2981b9c2ac0ffd75238ad9ea2d3bfa67a1abd546d262e"}, {file = "pywin32-308-cp310-cp310-win_amd64.whl", hash = "sha256:4fc888c59b3c0bef905ce7eb7e2106a07712015ea1c8234b703a088d46110e8e"}, @@ -338,6 +356,7 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -359,18 +378,19 @@ version = "2.3.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [metadata] -lock-version = "2.0" -python-versions = "3.12.0" -content-hash = "fa04d76cbe32d3f29d742181d10a0fdf23ea36170ad3754458c915644270e846" +lock-version = "2.1" +python-versions = ">=3.12" +content-hash = "3fdbb9768a88f02f36e5874e93a09f51e4bbbd5c91a28267ab3651c3ac56af0e" diff --git a/pyproject.toml b/pyproject.toml index 2522df4..e2d30b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,19 +1,20 @@ -[tool.poetry] +[project] name = "kawai-focus" version = "0.1.0" description = "Kawai.Focus - приложение для фокусировки внимания на основе таймера Pomodoro." -authors = ["Arduinum "] -license = "MIT" +authors = [ + {name = "Arduinum",email = "message.chaos628@gmail.com"} +] +license = {text = "MIT"} readme = "README.md" -packages = [{include = "kawai_focus"}] - -[tool.poetry.dependencies] -python = "3.12.0" -kivy = "^2.3.1" +requires-python = ">=3.12" +dependencies = [ + "kivy (>=2.3.1,<3.0.0)", +] [build-system] -requires = ["poetry-core"] +requires = ["poetry-core>=2.0.0,<3.0.0"] build-backend = "poetry.core.masonry.api" [tool.poetry.scripts]