diff --git a/.gitignore b/.gitignore index 15201ac..199fea6 100644 --- a/.gitignore +++ b/.gitignore @@ -169,3 +169,6 @@ cython_debug/ # PyPI configuration file .pypirc + +# VSCODE +.vscode/ \ No newline at end of file diff --git a/json/errors.json b/json/errors.json new file mode 100644 index 0000000..a3a7afc --- /dev/null +++ b/json/errors.json @@ -0,0 +1,6 @@ +{ + "no_time": "Не указано время таймера!", + "negative_time": "Время таймера не может быть отрицательным!", + "ss_mm_big": "Секунды и минуты не могут быть больше 59!", + "hh_big": "Часы не могут быть больше 23!" +} \ No newline at end of file diff --git a/json/timer.json b/json/timer.json new file mode 100644 index 0000000..be37c13 --- /dev/null +++ b/json/timer.json @@ -0,0 +1,5 @@ +{ + "sound_timer": "alarm-beep.mp3", + "zero_time": "00:00:00", + "custom_time": "00:00:12" +} \ No newline at end of file diff --git a/kawai_focus/main.py b/kawai_focus/main.py index a95fe02..e71d8ae 100644 --- a/kawai_focus/main.py +++ b/kawai_focus/main.py @@ -3,15 +3,26 @@ kivy.require('2.3.1') from kivy.app import App from kivy.uix.screenmanager import ScreenManager +from kivy.lang import Builder +from kivy.logger import Logger + +import logging + from kawai_focus.screens.timer_screen import TimerScreen +Logger.setLevel(logging.DEBUG) + + class KawaiFocusApp(App): """Класс для создания приложения""" title = 'Kawai.Focus' def build(self): + # Загрузка kv файла + Builder.load_file('kv/timer_screen.kv') + screen_manager = ScreenManager() screen_manager.add_widget(TimerScreen(name='timers_screen')) return screen_manager diff --git a/kawai_focus/screens/timer_screen.py b/kawai_focus/screens/timer_screen.py index 9d9a633..f8b1390 100644 --- a/kawai_focus/screens/timer_screen.py +++ b/kawai_focus/screens/timer_screen.py @@ -1,116 +1,83 @@ 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 kivy.core.audio import SoundLoader -from kawai_focus.utils import custom_timer + +from kawai_focus.utils.utils import custom_timer +from kawai_focus.utils.data_json import read_json_timer class TimerScreen(Screen): """Экран таймера""" + sound_timer_name = read_json_timer.get_text('sound_timer') + path_file = f'sounds/{sound_timer_name}' + sound = SoundLoader.load(path_file) + 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.zero_time = read_json_timer.get_text('zero_time') self.timer_generator = None self.paused = False self.remaining_time = None + self.sound_stop_event = 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') + self.timer_generator = custom_timer(ss=12) # Установите необходимое время + self.remaining_time = next(self.timer_generator, self.zero_time) # Запуск обновления времени каждую секунду 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 + # TODO: временное решение, время будет задаваться в интерфейсе + self.remaining_time = read_json_timer.get_text('custom_time') + self.ids.time_label.text = self.remaining_time + + self.sound.stop() + + if self.sound_stop_event: + Clock.unschedule(self.sound_stop_event) + self.sound_stop_event = None + + def play_sound(self, dt) -> None: + """Метод для воспроизведения звука""" + + self.sound.play() + # Планируем остановку звука через 20 секунд + self.sound_stop_event = Clock.schedule_once(lambda dt: self.sound.stop(), 20) 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: - # Остановка обновления времени при завершении генератора + # Получение следующего значения времени из генератора + self.remaining_time = next(self.timer_generator, self.zero_time) + self.ids.time_label.text = self.remaining_time + + if self.remaining_time == self.zero_time: + # Остановка обновления времени Clock.unschedule(self.update_time) + Clock.schedule_once(self.play_sound) diff --git a/kawai_focus/utils.py b/kawai_focus/utils.py deleted file mode 100644 index 9b10282..0000000 --- a/kawai_focus/utils.py +++ /dev/null @@ -1,36 +0,0 @@ -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/kawai_focus/utils/__init__.py b/kawai_focus/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/kawai_focus/utils/data_json.py b/kawai_focus/utils/data_json.py new file mode 100644 index 0000000..a8acf8c --- /dev/null +++ b/kawai_focus/utils/data_json.py @@ -0,0 +1,36 @@ +import json + +from kawai_focus.main import Logger + + +class ReadJson: + """Класс для чтения json файла""" + + def __init__(self, file_name: str): + self.file_name = file_name + self.json_data = self.read_json_as_dict() + + def read_json_as_dict(self) -> dict: + """Метод для чтения json файла как dict""" + + try: + file_path = f'json/{self.file_name}' + with open(file=file_path, mode='r') as file: + return json.load(file) + except (FileNotFoundError, json.decoder.JSONDecodeError) as err: + Logger.error(f'Logger: {err.__class__.__name__}: {err}') + + def get_text(self, name: str) -> str: + """Метод для возврата текста из файла json по ключу name""" + + text = self.json_data.get(name) + + if text: + if isinstance(text, list): + text = ''.join(text) + return text + return text + + +read_json_err = ReadJson(file_name='errors.json') +read_json_timer = ReadJson(file_name='timer.json') diff --git a/kawai_focus/utils/utils.py b/kawai_focus/utils/utils.py new file mode 100644 index 0000000..b51aa18 --- /dev/null +++ b/kawai_focus/utils/utils.py @@ -0,0 +1,30 @@ +from typing import Generator + +from kawai_focus.main import Logger +from kawai_focus.utils.data_json import read_json_err + + +def custom_timer(hh: int=0, mm: int=0, ss: int=0) -> Generator[str, None, None]: + """Функция отсчитывает время, установленное для таймера в формате + 'hh:mm:ss'. Возвращает генератор, который возвращает текущее время + в формате 'hh:mm:ss'.""" + + try: + if ss == 0 and mm == 0 and hh == 0: + raise ValueError(read_json_err.get_text('no_time')) + + if ss < 0 or mm < 0 or hh < 0: + raise ValueError(read_json_err.get_text('negative_time')) + + if ss > 59 or mm > 59: + raise ValueError(read_json_err.get_text('ss_mm_big')) + + if hh > 23: + raise ValueError(read_json_err.get_text('hh_big')) + + total_seconds = (int(hh or 0) * 3600) + (int(mm or 0) * 60) + (int(ss or 0)) + + for remaining in range(total_seconds, -1, -1): + yield f"{remaining // 3600:02d}:{(remaining % 3600) // 60:02d}:{remaining % 60:02d}" + except (TypeError, ValueError) as err: + Logger.error(f'Logger: {err.__class__.__name__}: {err}') diff --git a/kv/timer_screen.kv b/kv/timer_screen.kv new file mode 100644 index 0000000..55d64f1 --- /dev/null +++ b/kv/timer_screen.kv @@ -0,0 +1,37 @@ +#:kivy 2.3.1 + +: + FloatLayout: + # Кнопка "Старт" + Button: + text: 'Старт' + size_hint: None, None + height: 40 + width: 100 + pos_hint: {'center_x': 0.3, 'center_y': 0.5} + on_release: root.start_timer(self) + + # Кнопка "Пауза" + Button: + text: 'Пауза' + size_hint: None, None + height: 40 + width: 100 + pos_hint: {'center_x': 0.5, 'center_y': 0.5} + on_release: root.pause_timer(self) + + # Кнопка "Стоп" + Button: + text: 'Стоп' + size_hint: None, None + height: 40 + width: 100 + pos_hint: {'center_x': 0.7, 'center_y': 0.5} + on_release: root.stop_timer(self) + + # Label для отображения времени + Label: + id: time_label + text: '00:00:12' + size_hint: None, None + pos_hint: {'center_x': 0.5, 'center_y': 0.6} \ No newline at end of file diff --git a/sounds/alarm-beep.mp3 b/sounds/alarm-beep.mp3 new file mode 100644 index 0000000..b9bfeba Binary files /dev/null and b/sounds/alarm-beep.mp3 differ