updated custom_timer; added kv for timer_screen.py, play sound for timer, json reading

This commit is contained in:
Arduinum628
2025-03-27 11:32:54 +03:00
parent dffb4f730b
commit 4e4a6530c2
11 changed files with 171 additions and 112 deletions

3
.gitignore vendored
View File

@@ -169,3 +169,6 @@ cython_debug/
# PyPI configuration file # PyPI configuration file
.pypirc .pypirc
# VSCODE
.vscode/

6
json/errors.json Normal file
View File

@@ -0,0 +1,6 @@
{
"no_time": "Не указано время таймера!",
"negative_time": "Время таймера не может быть отрицательным!",
"ss_mm_big": "Секунды и минуты не могут быть больше 59!",
"hh_big": "Часы не могут быть больше 23!"
}

5
json/timer.json Normal file
View File

@@ -0,0 +1,5 @@
{
"sound_timer": "alarm-beep.mp3",
"zero_time": "00:00:00",
"custom_time": "00:00:12"
}

View File

@@ -3,15 +3,26 @@ kivy.require('2.3.1')
from kivy.app import App from kivy.app import App
from kivy.uix.screenmanager import ScreenManager 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 from kawai_focus.screens.timer_screen import TimerScreen
Logger.setLevel(logging.DEBUG)
class KawaiFocusApp(App): class KawaiFocusApp(App):
"""Класс для создания приложения""" """Класс для создания приложения"""
title = 'Kawai.Focus' title = 'Kawai.Focus'
def build(self): def build(self):
# Загрузка kv файла
Builder.load_file('kv/timer_screen.kv')
screen_manager = ScreenManager() screen_manager = ScreenManager()
screen_manager.add_widget(TimerScreen(name='timers_screen')) screen_manager.add_widget(TimerScreen(name='timers_screen'))
return screen_manager return screen_manager

View File

@@ -1,116 +1,83 @@
from kivy.uix.screenmanager import Screen 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.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): 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): def __init__(self, **kwargs):
super(TimerScreen, self).__init__(**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.timer_generator = None
self.paused = False self.paused = False
self.remaining_time = None self.remaining_time = None
self.sound_stop_event = None
def start_timer(self, instance) -> None: def start_timer(self, instance) -> None:
"""Метод для запуска таймера"""
if self.paused: if self.paused:
self.paused = False self.paused = False
else: else:
# Инициализация генератора таймера # Инициализация генератора таймера
self.timer_generator = custom_timer('0h0m10s') # Установите необходимое время self.timer_generator = custom_timer(ss=12) # Установите необходимое время
self.remaining_time = next(self.timer_generator, '00:00:00') self.remaining_time = next(self.timer_generator, self.zero_time)
# Запуск обновления времени каждую секунду # Запуск обновления времени каждую секунду
Clock.schedule_interval(self.update_time, 1) Clock.schedule_interval(self.update_time, 1)
def pause_timer(self, instance) -> None: def pause_timer(self, instance) -> None:
"""Метод для паузы таймера"""
if not self.paused: if not self.paused:
self.paused = True self.paused = True
# Остановка обновления времени # Остановка обновления времени
Clock.unschedule(self.update_time) Clock.unschedule(self.update_time)
def stop_timer(self, instance) -> None: def stop_timer(self, instance) -> None:
"""Метод для остановки таймера"""
# Остановка обновления времени # Остановка обновления времени
Clock.unschedule(self.update_time) Clock.unschedule(self.update_time)
self.paused = False self.paused = False
self.remaining_time = '00:00:00' # TODO: временное решение, время будет задаваться в интерфейсе
self.time_label.text = self.remaining_time 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: def update_time(self, dt) -> None:
"""Метод для обновления времени на экране"""
if self.paused: if self.paused:
return return
try: # Получение следующего значения времени из генератора
# Получение следующего значения времени из генератора self.remaining_time = next(self.timer_generator, self.zero_time)
self.remaining_time = next(self.timer_generator, '00:00:00') self.ids.time_label.text = self.remaining_time
self.time_label.text = self.remaining_time
if self.remaining_time == self.zero_time:
if self.remaining_time == '00:00:00': # Остановка обновления времени
# Остановка обновления времени
Clock.unschedule(self.update_time)
except StopIteration:
# Остановка обновления времени при завершении генератора
Clock.unschedule(self.update_time) Clock.unschedule(self.update_time)
Clock.schedule_once(self.play_sound)

View File

@@ -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)

View File

View File

@@ -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')

View File

@@ -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}')

37
kv/timer_screen.kv Normal file
View File

@@ -0,0 +1,37 @@
#:kivy 2.3.1
<TimerScreen>:
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}

BIN
sounds/alarm-beep.mp3 Normal file

Binary file not shown.