feat: added new disign for timer_screen; deleted back and delete_timer fuctions; added id for navigation panel; added new backend for sounds ffpyplayer, converted alarm-beep.mp3 into CBR mp3

This commit is contained in:
Arduinum628
2025-11-13 11:59:44 +03:00
parent 7795bc6455
commit b748708751
7 changed files with 258 additions and 136 deletions

View File

@@ -7,6 +7,7 @@ from kivy.metrics import dp
from kivy.uix.behaviors import ButtonBehavior
from kivy.logger import Logger
from kivy.properties import StringProperty
from kivy import Config
import logging
@@ -19,10 +20,13 @@ from kivymd.uix.navigationrail import MDNavigationRailItem
from kawai_focus.menu_app import MenuApp
from kawai_focus.screens.timers_screen import TimersScreen
from kawai_focus.screens.timer_screen import TimerScreen
Logger.setLevel(logging.DEBUG)
Config.set('kivy', 'audio', 'ffpyplayer') # вместо audio_sdl2
class TrailingPressedIconButton(
ButtonBehavior, RotateBehavior, MDListItemTrailingIcon
@@ -49,9 +53,11 @@ class KawaiFocusApp(MDApp, MenuApp):
self.theme_cls.theme_style = 'Dark'
# Загрузка kv файла
Builder.load_file('kv/timers_screen.kv')
Builder.load_file('kv/timer_screen.kv')
self.screen_manager = MDScreenManager()
self.screen_manager.add_widget(TimersScreen(name='timers_screen'))
self.screen_manager.add_widget(TimerScreen(name='timer_screen'))
return self.screen_manager

View File

@@ -1,137 +1,183 @@
from kivy.uix.screenmanager import Screen
from kivymd.uix.screen import MDScreen
from kivy.clock import Clock
from kivy.core.audio import SoundLoader
from os.path import isfile
from kawai_focus.utils.utils import data_json, custom_timer, calculate_time
from kawai_focus.database.cruds import del_timer, list_timers
from kawai_focus.main import Logger
class TimerScreen(Screen):
class TimerScreen(MDScreen):
"""Экран таймера"""
sound_timer_name = data_json.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)
super().__init__(**kwargs)
Clock.schedule_once(self.load_sound)
# Переменные для управления таймером
self.zero_time = data_json.get_text('zero_time')
self.timer_generator = None
self.timer_generator = None
self.paused = False
self.remaining_time = None
self.sound_stop_event = None
self.timer = None
self.timer_start_time = None
self.source_timer_names = None
self.sound = None # Заглушка
def choice_timer(self) -> None:
"""Метод для выбора таймера"""
def load_sound(self, dt) -> None:
sound_timer_name = data_json.get_text('sound_timer')
path_file = f'sounds/{sound_timer_name}'
current_timer_name = self.manager.state_machine.pop(0)
if not isfile(path_file):
Logger.error(f'[TimerScreen] Файл звука не найден: {path_file}')
return
if current_timer_name == 'pomodoro':
self.timer_generator = custom_timer(mm_user=self.timer.pomodoro_time)
self.ids.time_label.text = calculate_time(mm_user=self.timer.pomodoro_time)
self.ids.type_timer_label.text = 'Помидор'
elif current_timer_name == 'break':
self.timer_generator = custom_timer(mm_user=self.timer.break_time)
self.ids.time_label.text = calculate_time(mm_user=self.timer.break_time)
self.ids.type_timer_label.text = 'Перерыв'
else:
self.timer_generator = custom_timer(mm_user=self.timer.break_long_time)
self.ids.time_label.text = calculate_time(mm_user=self.timer.break_long_time)
self.ids.type_timer_label.text = 'Перерывище'
self.sound = SoundLoader.load(path_file)
if not self.sound:
Logger.error(f'[TimerScreen] Не удалось загрузить файл звука (неподдерживаемый формат?): {path_file}')
return
def on_pre_enter(self, *args) -> None:
"""Вызывается перед появлением экрана"""
# Прячем кнопки "Стоп" и "Пауза"
self.ids.stop_button.opacity = 0
self.ids.stop_button.disabled = True
def start_timer(self, instance) -> None:
"""Метод для запуска таймера"""
self.ids.pause_button.opacity = 0
self.ids.pause_button.disabled = True
# Проверяем состояние state_machine
if not hasattr(self.manager, 'state_machine') or self.manager.state_machine is None:
self.manager.state_machine = []
def choice_timer(self) -> None:
"""Выбор таймера (помидор / перерыв / длинный перерыв)"""
if not self.manager.state_machine:
return
current_timer_name = self.manager.state_machine.pop(0)
match current_timer_name:
case 'pomodoro':
self.timer_generator = custom_timer(mm_user=self.timer.pomodoro_time)
self.ids.time_label.text = calculate_time(self.timer.pomodoro_time)
self.ids.type_timer_label.text = 'Помидор'
case 'break':
self.timer_generator = custom_timer(mm_user=self.timer.break_time)
self.ids.time_label.text = calculate_time(self.timer.break_time)
self.ids.type_timer_label.text = 'Перерыв'
case _:
self.timer_generator = custom_timer(mm_user=self.timer.break_long_time)
self.ids.time_label.text = calculate_time(self.timer.break_long_time)
self.ids.type_timer_label.text = 'Перерывище'
def start_timer(self, *args) -> None:
"""Запуск таймера"""
# Делаем кнопки панели навишгации неактивными
self.ids.nav_panel.ids.menu.disabled = True
self.ids.nav_panel.ids.timers_nav.disabled = True
self.ids.nav_panel.ids.guide_nav.disabled = True
self.ids.nav_panel.ids.info_nav.disabled = True
# Активируем кнопки "Стоп" и "Пауза"
self.ids.stop_button.opacity = 1
self.ids.stop_button.disabled = False
self.ids.pause_button.opacity = 1
self.ids.pause_button.disabled = False
# Прячем кнопку "Старт"
self.ids.start_button.opacity = 0
self.ids.start_button.disabled = True
if self.paused:
self.paused = False
else:
# Инициализация генератора таймера
if len(self.manager.state_machine) and self.timer_generator is None:
self.choice_timer()
self.ids.stop_button.opacity = 1
self.ids.stop_button.disabled = False
self.remaining_time = next(self.timer_generator, self.zero_time)
# Запуск обновления времени каждую секунду
Clock.schedule_interval(self.update_time, 1)
def pause_timer(self, instance) -> None:
"""Метод для паузы таймера"""
def pause_timer(self, *args) -> None:
"""Пауза таймера"""
if not self.paused:
self.paused = True
# Остановка обновления времени
Clock.unschedule(self.update_time)
def stop_timer(self, instance) -> None:
"""Метод для остановки таймера"""
# Остановка обновления времени
# Активируем кнопку "Старт"
self.ids.start_button.opacity = 1
self.ids.start_button.disabled = False
# Прячем кнопку "Пауза"
self.ids.pause_button.opacity = 0
self.ids.pause_button.disabled = True
def stop_timer(self, *args) -> None:
"""Остановка таймера"""
Clock.unschedule(self.update_time)
self.paused = False
# Сбрасываем оставшееся время
self.remaining_time = next(self.timer_generator, self.zero_time)
self.ids.time_label.text = self.timer_start_time
self.sound.stop()
# Останавливаем звук, если играет
if self.sound:
self.sound.stop()
if self.sound_stop_event:
Clock.unschedule(self.sound_stop_event)
self.sound_stop_event = None
# Возврат цикла таймеров
if not len(self.manager.state_machine):
self.manager.state_machine = self.source_timer_names.copy()
# Делаем кнопки панели навишгации неактивными
self.ids.nav_panel.ids.menu.disabled = False
self.ids.nav_panel.ids.timers_nav.disabled = False
self.ids.nav_panel.ids.guide_nav.disabled = False
self.ids.nav_panel.ids.info_nav.disabled = False
# Прячем кнопки "Стоп" и "Пауза"
self.ids.stop_button.opacity = 0
self.ids.stop_button.disabled = True
self.ids.pause_button.opacity = 0
self.ids.pause_button.disabled = True
# Активируем кнопку "Старт"
self.ids.start_button.opacity = 1
self.ids.start_button.disabled = False
self.choice_timer()
def back(self, instance) -> None:
"""Метод для кнопки назад - возврат в меню таймеров"""
def play_sound(self, *args) -> None:
"""Воспроизведение звука"""
if self.ids.title_warning.text:
self.ids.title_warning.text = ''
self.ids.del_timer.color = (1, 1, 1, 1)
self.manager.current = 'timers_screen'
def delete_timer(self, instance) -> None:
"""Метод для удаления таймера"""
if not self.ids.title_warning.text:
self.ids.title_warning.text = ('Вы действительно хотите удалить таймер?\n'
'Если да, то нажмите кнопку "удалить" ещё раз.')
self.ids.del_timer.color = (1, 0.3, 0.3, 1)
else:
del_timer(timer_id=self.timer.id)
timers = list_timers()
timers_screen = self.manager.get_screen('timers_screen')
timers_screen.ids.timers_view.data = timers
self.manager.current = 'timers_screen'
def play_sound(self, dt) -> None:
"""Метод для воспроизведения звука"""
self.sound.play()
# Планируем остановку звука через 20 секунд
self.sound_stop_event = Clock.schedule_once(lambda dt: self.sound.stop(), 20)
if self.sound:
self.sound.play()
self.sound_stop_event = Clock.schedule_once(lambda dt: self.sound.stop(), 20)
def update_time(self, dt) -> None:
"""Метод для обновления времени на экране"""
"""Обновление времени"""
if self.paused:
return
# Получение следующего значения времени из генератора
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)