Merge pull request #7 from Arduinum/mvp_1_pomodoro
feat: added functional for new disign for timers_screen
This commit was merged in pull request #7.
This commit is contained in:
@@ -42,10 +42,10 @@ def list_timers() -> list[dict[str, int | str]]:
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
'timer_id': timer.id,
|
'timer_id': timer.id,
|
||||||
'text': (
|
'title': timer.title,
|
||||||
f'{timer.title}\n'
|
'info': (
|
||||||
f'Помидоров: {timer.count_pomodoro}, '
|
f'Помидоров: {timer.count_pomodoro}, '
|
||||||
f'размер помидора: {timer.pomodoro_time}'
|
f'Размер помидора: {timer.pomodoro_time}'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
for timer in timers
|
for timer in timers
|
||||||
|
|||||||
@@ -1,42 +1,67 @@
|
|||||||
import kivy
|
import kivy
|
||||||
kivy.require('2.3.1')
|
kivy.require('2.3.1')
|
||||||
|
|
||||||
from kivy.app import App
|
|
||||||
from kivy.uix.screenmanager import ScreenManager
|
|
||||||
from kivy.lang import Builder
|
from kivy.lang import Builder
|
||||||
|
from kivy.animation import Animation
|
||||||
|
from kivy.metrics import dp
|
||||||
|
from kivy.uix.behaviors import ButtonBehavior
|
||||||
from kivy.logger import Logger
|
from kivy.logger import Logger
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from kawai_focus.screens.timer_screen import TimerScreen
|
from kivymd.app import MDApp
|
||||||
from kawai_focus.screens.timer_constructor_screen import TimerConstructorScreen
|
from kivymd.uix.screenmanager import MDScreenManager
|
||||||
|
from kivymd.uix.expansionpanel import MDExpansionPanel
|
||||||
|
from kivymd.uix.behaviors import RotateBehavior
|
||||||
|
from kivymd.uix.list import MDListItemTrailingIcon
|
||||||
|
|
||||||
|
from kawai_focus.menu_app import MenuApp
|
||||||
from kawai_focus.screens.timers_screen import TimersScreen
|
from kawai_focus.screens.timers_screen import TimersScreen
|
||||||
from kawai_focus.database.cruds import list_timers
|
|
||||||
|
|
||||||
|
|
||||||
Logger.setLevel(logging.DEBUG)
|
Logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
class KawaiFocusApp(App):
|
class TrailingPressedIconButton(
|
||||||
"""Класс для создания приложения"""
|
ButtonBehavior, RotateBehavior, MDListItemTrailingIcon
|
||||||
|
):
|
||||||
|
"""Кнопка-иконка в конце элемента списка с поддержкой анимации"""
|
||||||
|
...
|
||||||
|
|
||||||
title = 'Kawai.Focus'
|
|
||||||
|
|
||||||
def build(self):
|
class KawaiFocusApp(MDApp, MenuApp):
|
||||||
|
"""Главный класс приложения"""
|
||||||
|
|
||||||
|
def build(self) -> MDScreenManager:
|
||||||
|
"""Создаёт и возвращает менеджер экранов приложения"""
|
||||||
|
|
||||||
|
self.theme_cls.theme_style = 'Dark'
|
||||||
# Загрузка kv файла
|
# Загрузка kv файла
|
||||||
Builder.load_file('kv/timer_screen.kv')
|
|
||||||
Builder.load_file('kv/timer_constructor_screen.kv')
|
|
||||||
Builder.load_file('kv/timers_screen.kv')
|
Builder.load_file('kv/timers_screen.kv')
|
||||||
|
|
||||||
screen_manager = ScreenManager()
|
self.screen_manager = MDScreenManager()
|
||||||
timers = list_timers()
|
self.timers_screen = TimersScreen(name='timers_screen')
|
||||||
timers_screen = TimersScreen(name='timers_screen')
|
self.screen_manager.add_widget(self.timers_screen)
|
||||||
timers_screen.ids.timers_view.data = timers
|
|
||||||
|
return self.screen_manager
|
||||||
|
|
||||||
|
def tap_expansion_chevron(
|
||||||
|
self, panel: MDExpansionPanel, chevron: TrailingPressedIconButton
|
||||||
|
) -> None:
|
||||||
|
"""Анимация и переключение состояния раскрытия панели"""
|
||||||
|
|
||||||
screen_manager.add_widget(timers_screen)
|
Animation(
|
||||||
screen_manager.add_widget(TimerConstructorScreen(name='timer_constructor_screen'))
|
padding=(
|
||||||
screen_manager.add_widget(TimerScreen(name='timer_screen'))
|
[0, dp(12), 0, dp(12)] if not panel.is_open else [0, 0, 0, 0]
|
||||||
return screen_manager
|
),
|
||||||
|
d=0.2,
|
||||||
|
).start(panel)
|
||||||
|
panel.open() if not panel.is_open else panel.close()
|
||||||
|
(
|
||||||
|
panel.set_chevron_down(chevron)
|
||||||
|
if not panel.is_open
|
||||||
|
else panel.set_chevron_up(chevron)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
|
|||||||
115
kawai_focus/menu_app.py
Normal file
115
kawai_focus/menu_app.py
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
from kivy.utils import hex_colormap
|
||||||
|
from kivy.uix.widget import Widget
|
||||||
|
from materialyoucolor.utils.platform_utils import SCHEMES
|
||||||
|
from kivymd.uix.menu import MDDropdownMenu
|
||||||
|
|
||||||
|
|
||||||
|
class MenuApp:
|
||||||
|
"""Класс для меню приложения"""
|
||||||
|
|
||||||
|
menu: MDDropdownMenu = None
|
||||||
|
|
||||||
|
def open_menu(self, menu_button) -> None:
|
||||||
|
"""Открывает выпадающее меню"""
|
||||||
|
|
||||||
|
menu_items = []
|
||||||
|
|
||||||
|
for item, method in {
|
||||||
|
'Цвет темы': lambda: self.set_palette(),
|
||||||
|
'Стиль темы': lambda: self.switch_theme(),
|
||||||
|
'Тип схемы': lambda: self.set_scheme_type(),
|
||||||
|
'Новый таймер': None
|
||||||
|
}.items():
|
||||||
|
menu_items.append(
|
||||||
|
{
|
||||||
|
'text': item,
|
||||||
|
'on_release': method,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.menu = MDDropdownMenu(
|
||||||
|
caller=menu_button,
|
||||||
|
items=menu_items,
|
||||||
|
)
|
||||||
|
self.menu.open()
|
||||||
|
|
||||||
|
def switch_palette(self, selected_palette: str) -> None:
|
||||||
|
"""Переключает основной цвет темы"""
|
||||||
|
|
||||||
|
self.theme_cls.primary_palette = selected_palette
|
||||||
|
|
||||||
|
def switch_theme(self) -> None:
|
||||||
|
"""Переключает светлую/тёмную тему"""
|
||||||
|
|
||||||
|
self.theme_cls.switch_theme()
|
||||||
|
|
||||||
|
def set_palette(self) -> None:
|
||||||
|
"""Формирует и открывает меню выбора палитры"""
|
||||||
|
|
||||||
|
instance_from_menu = self.get_instance_from_menu('Set palette')
|
||||||
|
available_palettes = [
|
||||||
|
name_color.capitalize() for name_color in hex_colormap.keys()
|
||||||
|
]
|
||||||
|
|
||||||
|
menu_items = []
|
||||||
|
for name_palette in available_palettes:
|
||||||
|
menu_items.append(
|
||||||
|
{
|
||||||
|
'text': name_palette,
|
||||||
|
'on_release': lambda x=name_palette: self.switch_palette(x),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
MDDropdownMenu(
|
||||||
|
caller=instance_from_menu,
|
||||||
|
items=menu_items,
|
||||||
|
).open()
|
||||||
|
|
||||||
|
def set_scheme_type(self) -> None:
|
||||||
|
"""Формирует и открывает меню выбора типа цветовой схемы"""
|
||||||
|
|
||||||
|
instance_from_menu = self.get_instance_from_menu("Switch scheme type")
|
||||||
|
|
||||||
|
menu_items = []
|
||||||
|
for scheme_name in SCHEMES.keys():
|
||||||
|
menu_items.append(
|
||||||
|
{
|
||||||
|
'text': scheme_name,
|
||||||
|
'on_release': lambda x=scheme_name: self.update_scheme_name(x),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
MDDropdownMenu(
|
||||||
|
caller=instance_from_menu,
|
||||||
|
items=menu_items,
|
||||||
|
).open()
|
||||||
|
|
||||||
|
def update_scheme_name(self, scheme_name: str) -> None:
|
||||||
|
"""Обновляет тип цветовой схемы"""
|
||||||
|
|
||||||
|
self.theme_cls.dynamic_scheme_name = scheme_name
|
||||||
|
|
||||||
|
def get_instance_from_menu(self, name_item: str) -> Widget:
|
||||||
|
"""Возвращает экземпляр элемента меню по его названию"""
|
||||||
|
|
||||||
|
index = 0
|
||||||
|
rv = self.menu.ids.md_menu
|
||||||
|
opts = rv.layout_manager.view_opts
|
||||||
|
datas = rv.data[0]
|
||||||
|
|
||||||
|
for data in rv.data:
|
||||||
|
if data.get('text') == name_item:
|
||||||
|
index = rv.data.index(data)
|
||||||
|
break
|
||||||
|
|
||||||
|
instance = rv.view_adapter.get_view(
|
||||||
|
index, datas, opts[index].get('viewclass')
|
||||||
|
)
|
||||||
|
return instance
|
||||||
|
|
||||||
|
def disabled_widgets(self) -> None:
|
||||||
|
"""Переключает активность виджетов (блокирует/разблокирует)"""
|
||||||
|
|
||||||
|
for widget in self.root.ids.widget_box.children:
|
||||||
|
widget.disabled = not widget.disabled
|
||||||
|
|
||||||
|
for widget in self.root.ids.custom_widget_box.children:
|
||||||
|
widget.disabled = not widget.disabled
|
||||||
@@ -1,31 +1,55 @@
|
|||||||
from kivy.uix.screenmanager import Screen
|
from kivy.properties import StringProperty, NumericProperty
|
||||||
|
from kivymd.uix.screen import MDScreen
|
||||||
|
from kivymd.uix.expansionpanel import MDExpansionPanel
|
||||||
|
|
||||||
from kawai_focus.utils.utils import calculate_time, gen_types_timers
|
from kawai_focus.utils.utils import calculate_time, gen_types_timers
|
||||||
from kawai_focus.database.cruds import get_timer
|
from kawai_focus.database.cruds import get_timer, list_timers
|
||||||
|
from kawai_focus.main import Logger
|
||||||
|
|
||||||
|
|
||||||
class TimersScreen(Screen):
|
class ExpansionPanelItem(MDExpansionPanel):
|
||||||
|
"""Кастомный элемент панели с данными таймера"""
|
||||||
|
|
||||||
|
timer_id = NumericProperty()
|
||||||
|
title = StringProperty()
|
||||||
|
info = StringProperty()
|
||||||
|
|
||||||
|
|
||||||
|
class TimersScreen(MDScreen):
|
||||||
"""Экран таймеры"""
|
"""Экран таймеры"""
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def on_enter(self, *args) -> None:
|
||||||
super(TimersScreen, self).__init__(**kwargs)
|
"""Загружает список таймеров при входе на экран"""
|
||||||
|
|
||||||
|
self.load_timers()
|
||||||
def switch_new_timer(self, instance) -> None:
|
|
||||||
"""Метод для переключения на экран Конструктор таймера"""
|
|
||||||
|
|
||||||
self.manager.current = 'timer_constructor_screen'
|
def load_timers(self) -> None:
|
||||||
|
"""Загружает все таймеры и добавляет их в контейнер"""
|
||||||
|
|
||||||
|
self.ids.container.clear_widgets()
|
||||||
|
all_timers = list_timers()
|
||||||
|
|
||||||
|
for timer_data in all_timers:
|
||||||
|
panel = ExpansionPanelItem(
|
||||||
|
timer_id=timer_data.get('timer_id'),
|
||||||
|
title=timer_data.get('title'),
|
||||||
|
info=timer_data.get('info'),
|
||||||
|
)
|
||||||
|
self.ids.container.add_widget(panel)
|
||||||
|
|
||||||
def switch_timer(self, instance) -> None:
|
def switch_timer(self, instance) -> None:
|
||||||
"""Метод для кнопки переключения на выбранный таймер"""
|
"""Метод для кнопки переключения на выбранный таймер"""
|
||||||
|
|
||||||
timer = get_timer(timer_id=instance.timer_id)
|
timer = get_timer(timer_id=instance.timer_id)
|
||||||
|
if not timer:
|
||||||
|
Logger.warning(f'Таймер с ID {instance.timer_id} не найден!')
|
||||||
|
return
|
||||||
|
|
||||||
screen_timer = self.manager.get_screen('timer_screen')
|
screen_timer = self.manager.get_screen('timer_screen')
|
||||||
screen_timer.timer = timer
|
screen_timer.timer = timer
|
||||||
time_culc = calculate_time(mm_user=timer.pomodoro_time)
|
time_calc = calculate_time(mm_user=timer.pomodoro_time)
|
||||||
screen_timer.timer_start_time = time_culc
|
screen_timer.timer_start_time = time_calc
|
||||||
screen_timer.ids.time_label.text = time_culc
|
screen_timer.ids.time_label.text = time_calc
|
||||||
screen_timer.ids.title_label.text = timer.title
|
screen_timer.ids.title_label.text = timer.title
|
||||||
screen_timer.source_timer_names = gen_types_timers(count_pomodoro=timer.count_pomodoro)
|
screen_timer.source_timer_names = gen_types_timers(count_pomodoro=timer.count_pomodoro)
|
||||||
|
|
||||||
|
|||||||
@@ -1,49 +1,72 @@
|
|||||||
#:kivy 2.3.1
|
#:kivy 2.3.1
|
||||||
|
|
||||||
|
# Кастомная панель для каждого таймера
|
||||||
|
<ExpansionPanelItem>:
|
||||||
|
MDExpansionPanelHeader:
|
||||||
|
MDListItem:
|
||||||
|
theme_bg_color: "Custom"
|
||||||
|
md_bg_color: self.theme_cls.surfaceContainerLowColor
|
||||||
|
ripple_effect: False
|
||||||
|
|
||||||
|
MDListItemSupportingText:
|
||||||
|
text: root.title
|
||||||
|
|
||||||
|
TrailingPressedIconButton:
|
||||||
|
id: chevron
|
||||||
|
icon: "chevron-right"
|
||||||
|
on_release: app.tap_expansion_chevron(root, chevron)
|
||||||
|
|
||||||
|
MDExpansionPanelContent:
|
||||||
|
orientation: "vertical"
|
||||||
|
padding: "12dp", 0, "12dp", "12dp"
|
||||||
|
md_bg_color: self.theme_cls.surfaceContainerLowColor
|
||||||
|
adaptive_height: True
|
||||||
|
|
||||||
|
MDLabel:
|
||||||
|
text: "Информация о таймере"
|
||||||
|
adaptive_height: True
|
||||||
|
padding_x: "16dp"
|
||||||
|
padding_y: "12dp"
|
||||||
|
|
||||||
|
MDListItem:
|
||||||
|
theme_bg_color: "Custom"
|
||||||
|
md_bg_color: self.theme_cls.surfaceContainerLowColor
|
||||||
|
ripple_effect: False
|
||||||
|
on_release: None
|
||||||
|
MDListItemLeadingIcon:
|
||||||
|
icon: "information-outline"
|
||||||
|
MDListItemHeadlineText:
|
||||||
|
text: root.info
|
||||||
|
|
||||||
|
MDBoxLayout:
|
||||||
|
padding: [8, 8, 0, 0]
|
||||||
|
size_hint_y: None
|
||||||
|
height: self.minimum_height
|
||||||
|
|
||||||
|
MDButton:
|
||||||
|
style: "outlined"
|
||||||
|
size_hint_x: 1
|
||||||
|
|
||||||
|
MDButtonText:
|
||||||
|
text: "Открыть"
|
||||||
|
|
||||||
|
# Основной экран
|
||||||
<TimersScreen>:
|
<TimersScreen>:
|
||||||
FloatLayout:
|
md_bg_color: self.theme_cls.backgroundColor
|
||||||
RecycleView:
|
|
||||||
id: timers_view
|
MDIconButton:
|
||||||
size_hint: 1, 1
|
on_release: app.open_menu(self)
|
||||||
pos_hint: {"x": 0, "y": 0}
|
pos_hint: {"top": .98}
|
||||||
|
x: "12dp"
|
||||||
|
icon: "menu"
|
||||||
|
|
||||||
viewclass: "TimerButton"
|
ScrollView:
|
||||||
|
size_hint_x: .9
|
||||||
|
pos_hint: {"center_x": .5, "center_y": .5}
|
||||||
|
do_scroll_x: False
|
||||||
|
do_scroll_y: True
|
||||||
|
|
||||||
RecycleBoxLayout:
|
MDList:
|
||||||
default_size: None, dp(50)
|
id: container
|
||||||
default_size_hint: 1, None
|
spacing: "8dp"
|
||||||
size_hint_y: None
|
padding: [56, 8, 56, 0] # [слева, сверху, справа, снизу]
|
||||||
height: self.minimum_height
|
|
||||||
orientation: "vertical"
|
|
||||||
spacing: dp(5)
|
|
||||||
|
|
||||||
Button:
|
|
||||||
id: new_timer
|
|
||||||
text: 'Новый таймер'
|
|
||||||
size_hint: None, None
|
|
||||||
height: 40
|
|
||||||
width: 120
|
|
||||||
pos_hint: {"center_x": 0.3, "center_y": 0.1}
|
|
||||||
on_release: root.switch_new_timer(self)
|
|
||||||
|
|
||||||
<TimerButton@Button>:
|
|
||||||
timer_id: None
|
|
||||||
text: ""
|
|
||||||
on_release: app.root.get_screen("timers_screen").switch_timer(self)
|
|
||||||
size_hint: None, None
|
|
||||||
height: dp(50)
|
|
||||||
background_normal: "" # отключаем стандартный фон
|
|
||||||
background_color: 0, 0, 0, 0 # прозрачный
|
|
||||||
|
|
||||||
canvas.before:
|
|
||||||
Color:
|
|
||||||
rgba: 1, 1, 1, 1 # белый контур
|
|
||||||
Line:
|
|
||||||
width: 1.5
|
|
||||||
rectangle: (self.x, self.y, self.width, self.height)
|
|
||||||
|
|
||||||
Color:
|
|
||||||
rgba: (0, 0.5, 1, 0.5) if self.state == "down" else (0, 0, 0, 0)
|
|
||||||
Rectangle:
|
|
||||||
pos: self.pos
|
|
||||||
size: self.size
|
|
||||||
Reference in New Issue
Block a user