Все мы привыкли записывать дела на бумагу, но в эпоху смартфонов гораздо удобнее иметь всё под рукой в цифре. Сегодня я покажу вам, как написать собственное приложение-ежедневник на Python с красивым интерфейсом Material Design. Наш инструмент — библиотека KivyMD.
Почему KivyMD?
KivyMD — это набор виджетов для фреймворка Kivy, который следует гайдлайнам Google Material Design . Это значит, что ваше приложение будет выглядеть современно как на Android, так и на Windows или macOS. В отличие от написания веб-интерфейса, здесь всё будет работать нативно.
Мы создадим приложение, которое позволяет:
- Добавлять новые задачи.
- Отмечать их выполнение.
- Выбирать дату и время для напоминания.
Шаг 1: Настройка окружения
Первым делом установим саму библиотеку. Откройте терминал (командную строку) и выполните команду:
pip install kivymd
Также нам понадобится стандартная библиотека datetime, но она обычно уже установлена в Python.
Шаг 2: Создаем каркас приложения (main.py)
В Python-файле мы определим основной класс приложения и экраны. Нам понадобятся: главный экран со списком задач и диалоговое окно для добавления новой записи.
from kivymd.app import MDApp
from kivymd.uix.screen import MDScreen
from kivymd.uix.list import MDList, OneLineAvatarIconListItem
from kivymd.uix.dialog import MDDialog
from kivymd.uix.button import MDRectangleFlatButton
from kivy.uix.screenmanager import ScreenManager
from kivy.properties import ObjectProperty, StringProperty
from datetime import datetime
# Класс для элемента списка (одна задача)
class TaskItem(OneLineAvatarIconListItem):
# Здесь мы будем хранить текст задачи
text = StringProperty()
# ID задачи для удаления
task_id = ObjectProperty(None)
# Класс главного экрана
class MainScreen(MDScreen):
def show_add_dialog(self):
# Здесь будет логика открытия окна добавления
pass
def add_task(self, text, date, time):
# Логика добавления задачи
pass
# Менеджер экранов (пока он у нас один, но структура правильная)
class sm(ScreenManager):
pass
class JournalApp(MDApp):
def build(self):
self.theme_cls.primary_palette = "Indigo" # Задаем цветовую тему
self.theme_cls.theme_style = "Light" # Светлая тема
return sm()
if __name__ == "__main__":
JournalApp().run()
Шаг 3: Рисуем интерфейс (journal.kv)
Kivy использует свой язык разметки (.kv), который отделяет логику от дизайна. Создайте файл journal.kv в той же папке, что и main.py.
# journal.kv
# Корневое правило менеджера экранов
<sm>:
MainScreen:
name: 'main'
# Описание главного экрана
<MainScreen>:
MDBoxLayout:
orientation: 'vertical'
# Верхняя панель
MDTopAppBar:
title: "Мои дела"
elevation: 4
left_action_items: [["menu", lambda x: x]]
right_action_items: [["plus", lambda x: root.show_add_dialog()]]
md_bg_color: app.theme_cls.primary_color
# Список задач
ScrollView:
MDList:
id: task_container
# Сюда будут динамически добавляться TaskItem
# Нижняя панель навигации (опционально)
MDBottomNavigation:
MDBottomNavigationItem:
name: "tasks"
text: "Задачи"
icon: "check-circle"
MDBottomNavigationItem:
name: "calendar"
text: "Календарь"
icon: "calendar"
# Дизайн одной задачи
<TaskItem>:
# Корневой виджет - MDBoxLayout для размещения иконки и текста
MDBoxLayout:
orientation: "horizontal"
spacing: "10dp"
padding: "5dp"
size_hint_y: None
height: "50dp"
MDCheckbox:
size_hint: None, None
size: "40dp", "40dp"
on_active: root.check_task(self.active, root.task_id)
MDLabel:
text: root.text
halign: "left"
valign: "middle"
size_hint_x: 0.7
MDIconButton:
icon: "delete"
size_hint: None, None
size: "40dp", "40dp"
on_release: root.delete_task(root.task_id)
Визуализация компонентов: Мы использовали MDTopAppBar для заголовка и кнопок , MDList для отображения ленты записей и MDCheckbox для отметки выполнения задачи .
Шаг 4: Реализуем логику (Диалог добавления)
Вернемся к файлу main.py. Нам нужно реализовать метод show_add_dialog, который будет открывать окошко для ввода текста, а также выбора даты и времени. Для выбора даты используем встроенный MDModalDatePicker .
# Добавьте эти импорты в начало main.py
from kivymd.uix.dialog import MDDialog
from kivymd.uix.picker import MDModalDatePicker, MDTimePicker
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.textfield import MDTextField
# Создадим класс содержимого диалога
class DialogContent(MDBoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.orientation = "vertical"
self.spacing = "10dp"
self.padding = "20dp"
self.size_hint_y = None
self.height = "250dp"
self.text_input = MDTextField(hint_text="Что нужно сделать?")
self.date_button = MDRectangleFlatButton(text="Выбрать дату")
self.time_button = MDRectangleFlatButton(text="Выбрать время")
self.add_widget(self.text_input)
self.add_widget(self.date_button)
self.add_widget(self.time_button)
# Теперь обновим класс MainScreen
class MainScreen(MDScreen):
def show_add_dialog(self):
content = DialogContent()
self.dialog = MDDialog(
title="Новая задача",
type="custom",
content_cls=content,
buttons=[
MDRectangleFlatButton(text="ОТМЕНА", on_release=lambda x: self.dialog.dismiss()),
MDRectangleFlatButton(text="СОХРАНИТЬ", on_release=lambda x: self.save_task(content))
]
)
# Привязываем обработчики для выбора даты и времени
content.date_button.on_release = lambda: self.show_date_picker(content)
content.time_button.on_release = lambda: self.show_time_picker(content)
self.dialog.open()
def show_date_picker(self, content):
date_dialog = MDModalDatePicker()
date_dialog.bind(on_save=lambda instance, value, date_range: self.on_date_selected(content, value))
date_dialog.open()
def on_date_selected(self, content, selected_date):
content.date_button.text = selected_date.strftime("%d/%m/%Y")
content.selected_date = selected_date
def show_time_picker(self, content):
time_dialog = MDTimePicker()
time_dialog.bind(on_save=lambda instance, time: self.on_time_selected(content, time))
time_dialog.open()
def on_time_selected(self, content, selected_time):
content.time_button.text = selected_time.strftime("%H:%M")
content.selected_time = selected_time
def save_task(self, content):
task_text = content.text_input.text
if task_text:
# Добавляем виджет в MDList на экране
task_widget = TaskItem(text=task_text)
# Сохраняем задачу (в следующем шаге)
self.ids.task_container.add_widget(task_widget)
self.dialog.dismiss()
Шаг 5: Сохранение данных
Чтобы задачи не исчезали после закрытия приложения, нужно использовать базу данных. Проще всего подойдет стандартный модуль sqlite3.
- Создаем таблицу в on_start приложения.
- При загрузке экрана читаем данные из БД и создаем виджеты.
- При добавлении или удалении обновляем БД.
import sqlite3
import uuid
class JournalApp(MDApp):
def on_start(self):
self.db_connect()
self.load_tasks()
def db_connect(self):
self.conn = sqlite3.connect("journal.db")
self.cursor = self.conn.cursor()
self.cursor.execute("""CREATE TABLE IF NOT EXISTS tasks (
id TEXT PRIMARY KEY,
text TEXT,
date TEXT,
time TEXT,
done INTEGER
)""")
self.conn.commit()
def load_tasks(self):
self.cursor.execute("SELECT * FROM tasks")
rows = self.cursor.fetchall()
for row in rows:
task_widget = TaskItem(text=row[1], task_id=row[0])
self.root.get_screen('main').ids.task_container.add_widget(task_widget)
# Установка статуса done (checkbox) будет отдельно
Возможные сложности при сборке APK
Если вы захотите запустить это приложение на телефоне (Android), вам понадобится Buildozer.
При сборке APK нужно указать все зависимости в файле buildozer.spec. В строке requirements обязательно должны быть: kivy==2.1.0, kivymd, sqlite3, datetime, android .
Также учтите, что некоторые виджеты KivyMD (например, некоторые темы для MDTopAppBar) могут работать на компьютере, но "падать" на телефоне из-за особенностей рендеринга OpenGL . Всегда тестируйте критические функции на реальном устройстве или эмуляторе.
Вот и всё! У вас получился полноценный ежедневник с Material Design, который вы можете кастомизировать под себя. Вы можете добавлять новые поля, сортировку или даже синхронизацию с облаком. Python и KivyMD дают для этого все возможности.