Kivy — универсальный аналог Flutter и PyQt
Kivy — это фреймворк, предназначенный для кроссплатформенной разработки приложений на Python. Вы точно не найдете его в топах самых популярных и обсуждаемых инструментов, потому что проект развивается медленно, имеет не самый широкий и современный набор средств верстки интерфейса, а также плохо оптимизирован для сборки ПО.
Казалось бы, уже по этим причинам не стоит рассматривать Kivy, но не все так просто.
Для крупного продукта, безусловно, приоритетными решениями все еще остаются нативные языки программирования по типу Swift или Kotlin, и инструменты кроссплатформенной разработки вроде Flutter и React Native. Выбор подобных технологий обусловлен запросом на высокий уровень пользовательского опыта и требованиями к стабильности работы создаваемых приложений.
Тем не менее, когда речь заходит о пет-проектах и DIY, данные метрики становятся менее принципиальными, уступая место эксклюзивным фичам, которые добавляет разработчик.
Особенностью Kivy является возможность быстрой пересборки вашего проекта под разные платформы и операционные системы практически без изменения исходного кода программы.
В отличии он от аналогов (вроде PyQT), которые ориентированы в первую очередь на десктоп, Kivy поддерживает все основные платформы (т. к. изначально создавался как мобильно-ориентированный фреймворк). При этом все проекты реализуются с помощью Python, что выгодно отличает Kivy от Flutter, в котором важно будет освоить отдельный язык программирования Dart.
В Kivy можно выделить базовые элементы:
- ядро и графика;
- библиотека элементов интерфейса;
- дополнительные модули.
Понимаю, что для некоторых читателей, которые находятся на самом старте, все это мало о чем говорит. Поэтому с помощью схемы ниже постараюсь упростить ситуацию.

Для написания логики приложения используется Python. Помимо «голого кода», содержащего функции и алгоритмы, потребуется подключать внешние библиотеки.
Kivy уже содержит весь базовый набор: от графического движка до готовых расширений, которые мы сможем использовать в своем приложении.
Python-код выступает связующим звеном. Он берет инструменты из библиотек, обращается к внутренним ресурсам Kivy — его виджетам и графике — и выстраивает их в рабочую систему. На финальном этапе нам остается только прогнать этот набор через модули упаковки, которые превращают разрозненные файлы в один понятный для системы файл — приложение.
Структура базового проекта при таком наборе элементов сводится к:
- исполняемым Python-файлам с описанием логики работы приложения;
- KV-файлам, в которых мы задаем разметку для интерфейса;
- отдельным директориям с ассетами и модулями;
- конфигурационным файлам сборщика, которые помогут подготовить приложение к запуску на нужных нам платформах.
Проект на Kivy похож на Python-приложения на других фреймворках. Тем не менее на архитектуре стоит остановиться подробнее: начинающим разработчикам бывает сложно перейти от одного скрипта к разветвленной системе файлов.
Меньше слов — больше дела. Давайте напишем небольшую программу на Kivy.
Готовим среду разработки и пишем «Hello, world!»
Для наглядности мы напишем однооконное приложение, которое будет выводить надпись «Hello, world!», а также принимать на ввод наши пользовательские данные через текстовое поле.
Для начала работы
Загрузите примеры кода, которые мы будем рассматривать в этой статье, из GitHub.
Перед тем, как мы приступим к разбору кода, нам необходимо подготовить все необходимое для его запуска.
IDE — интегрированная среда разработки
Для написания, запуска и отладки кода можно использовать любую среду разработки, но я рекомендую выбирать VS Code.
В нем есть много расширений, которые позволят быстро тестировать код и искать ошибки. Для реализации проекта мы установим официальное расширение Python от Microsoft.

Также, если хотите ускорить разработку через AI, советую Cursor. Это форк VS Code с нативной интеграцией LLM. Он в разы лучше обычных плагинов понимает контекст всего проекта, помогает быстро фиксить ошибки в промптах и на лету генерить бойлерплейт для API-запросов. Для работы с агентами — мастхэв.
Установка окружения
Для работы проекта нужен Python 3.10+. Проверить версию и наличие интерпретатора в системе можно одной командой:
python –version
Если его нет в системе, установить по инструкции: https://www.python.org/downloads/
Для запуска первого примера в терминале или PowerShell перейдите в директорию первого проекта из загруженного репозитория:
cd ~/kivy-examples/KivyHello
Создайте и активируйте виртуальное окружение командами:
python -m venv venv
source venv/bin/activate #Для Linux/macOS
.\Scripts\activate #Для Windows
Установка Kivy
В терминале введите команду:
pip install kivy
Далее проверим, все ли получилось с помощью интерпретатора Python:
python
>>> import kivy
>>> print(kivy.__version__)
Запуск “Hello, world!”
Чтобы протестировать эту программу, мы используем VS Code:
- открываем папку с проектом — KivyHello (тут же указываем путь к виртуальному окружению, чтобы не было ошибок с импортами);
- вызываем палитру команд сочетанием клавиш CTRL+P (или ⇧⌘P для macOS);
- находим Python: выбор интерпретатора — вводим путь:
./venv/bin/python; - открывааем вкладку с файлом main.py и нажмаем кнопку Play в правом углу редактора.
После запуска приложения нас встречает экран с приветствием и полем ввода. Когда мы введем в него данные — текст будет перепечатан, а затем возвращен обратно к «Hello, world!».

Как Python управляет интерфейсом
Несмотря на то, что основной файл больше по объему, весь визуал здесь строится на стандартных элементах Kivy.
Чтобы не смешивать верстку с логикой, я разделил описание элементов и их поведение. За отрисовку отвечает связка из KV-файла и загрузчика Builder, который подтягивает описание контейнеров и кнопок в дерево виджетов.
Верхнеуровнево интерфейс представляет собой вертикальный BoxLayout с отступами, где размещены поле ввода TextInput, кнопка отправки и текстовая метка Label. Последняя — самая важная часть UI, так как она через переменную app.current_text динамически отображает результат работы нашего алгоритма.
Для вашего удобства собрал небольшую табличку:
| Элемент | Описание | Код |
| App | Базовый класс для создания Kivy-приложения | class HelloApp(App): |
| Builder | Загрузчик .kv файла для создания интерфейса | return Builder.load_file("hello.kv") |
| Clock | Планировщик для анимации и таймеров | Clock.schedule_interval(self._animate_text, 0.08) |
| BoxLayout | Контейнер для вертикального расположения виджетов | BoxLayout: orientation: "vertical" padding: dp(20) spacing: dp(16) |
| Button | Базовая кнопка | Button: text: "Отправить" size_hint_y: 0.2 on_press: app.process_word() |
| TextInput | Поле для ввода текста пользователем | TextInput: id: text_input hint_text: "Введите слово" size_hint_y: 0.18 multiline: False |
| Label | Виджет для отображения текста | Label: id: label text: app.current_text font_size: 30 color: 1, 1, 1, 1 |
Остальная логика программы завязана на обработке событий: по нажатию кнопки, берется слово из ввода, а само поле очищается. В этот момент запускается таймер Kivy и блок с анимацией. Сначала стирается «текущее» слово, а потом посимвольно выводится новое слово и обновляется значение переменной current_text.
В финале, после двухсекундной паузы, запускается обратная анимация и текст возвращается к значению по умолчанию.
Структурно программу внутри main.py можно разбить на три логических блока:
- Класс приложения — основной класс HelloApp, наследуемый от App, в котором определены свойства, методы инициализации и логика анимации.
- Методы – функции, определяющие логику работы нашей программы, например стирание или набор текста.
- Точка входа — запуск приложения через HelloApp().run(), который поднимает событийный цикл приложения.
Material design: добавление элементов
Верстать интерфейс в «чистом» Kivy Language я не советую — процесс долгий: каждый элемент приходится конфигурировать вручную. Подключение дополнительной библиотеки KivyMD решает данную проблему.
Для запуска демо-проекта переходим в директорию KivyMDdemo, разворачиваем виртуальное окружение и ставим пакет:
pip install kivymd
Буквы MD в названии расшифровываются, как Material design — стиль дизайна мобильных и десктопных приложений, разработанный компанией Google.
Давайте познакомимся с основными элементами интерфейса, которые вы сможете использовать в своих проектах. Чтобы запустить у себя такое же демо-приложение с популярными элементами (кнопки, карточки, списки), возвращаемся в VS Code:
- открываем директорию KivyMDdemo, как и в прошлом примере, через палитру команд выбираем интерпретатор;
- аналогично прошлому примеру, запускаем код из main.py с помощью Play.

Библиотека KivyMD содержит большое количество разных элементов и для наглядности я собрал самые популярные на одном экране:
| Элемент | Описание | Код |
| MDApp | Основной класс приложения KivyMD | class DemoApp(MDApp) |
| MDLabel | Текстовая метка | MDLabel: text: "Пример текста" theme_text_color: "Primary" |
| MDCard | Карточка с закругленными углами для группировки элементов | MDCard: orientation: "vertical" |
| MDSpinner | Индикатор загрузки | MDSpinner: id: loader size_hint: None, None |
| MDSlider | Ползунок для выбора значения | MDSlider: min: 0 max: 100 value: 50 |
| MDCheckbox | Чекбокс для выбора опций | MDCheckbox: size_hint: None, None size: dp(48), dp(48) |
| MDSwitch | Переключатель | MDSwitch: active: True |
| MDTextField | Поле ввода текста | MDTextField: hint_text: "Введите текст" |
| MDList | Список с элементами (однострочными и двустрочными) | MDList: OneLineListItem: text: "Пункт списка" |
| MDRaisedButton | Кнопка с эффектом подъема при нажатии | MDRaisedButton: text: "Нажать" on_release: app.set_action("Действие") |
С помощью Python описывается логика работы бэкенда, а с помощью Kivy — разметка и виджеты KivyMD.
Весь набор компонентов — от карточек и диалогов до сложных списков — доступен в официальной документации. Там подробно расписаны свойства каждого виджета и примеры их интеграции в проект.
Пишем демо-приложение «Погода»
Следующим шагом мы объединим элементы с логикой, описанной на Python. В качестве простого прототипа возьмем приложение погоды. Тут надо отметить, чтобы не усложнять код работой с сетевыми запросами и ключами, я не реализовывал корректную обработку API, а добавил все необходимые данные локально.
Реальную погоду за окном приложение не покажет, зато хорошо проиллюстрирует, как подружить интерфейс с бэкендом.
Разберем функционал по частям, чтобы понимать структуру проекта изнутри, а не просто запускать готовый код.
Статус-панель
Пользователю важно видеть текущие вводные — информацию о каком городе мы смотрим. Также в строке я решил реализовать кнопки смены города и обновления данных.
Чтобы не перегружать экран лишними элементами, выбор города я реализовал через выпадающий список MDDropdownMenu. Сама кнопка в статус-панели просто дергает метод open_city_menu, который строит список доступных вариантов.

Вот так выглядит Python-код, для обработки выбора города:
def open_city_menu(self):
# основная функция выбора города из меню
menu_items = [
{"text": "Санкт-Петербург", "on_release": lambda x="Санкт-Петербург": self.set_city(x)},
{"text": "Москва", "on_release": lambda x="Москва": self.set_city(x)},
{"text": "Новосибирск", "on_release": lambda x="Новосибирск": self.set_city(x)},
]
self.menu = CityDropdownMenu(
caller=self.root.ids.city_button,
items=menu_items,
width=dp(240),
position="bottom",
border_margin=dp(12),
ver_growth="down",
)
self.menu.open()
Кнопка Выбрать город открывает выпадающий список (MDDropdownMenu) с городами. При нажатии на нее, вызывается метод app.open_city_menu()

Отображение данных
Чтобы не смешивать логику работы с контентом, все погодные сводки я вынес в отдельный файл weather_data.py. Для хранения данных также можно было выбрать полноценную базу данных или JSON. В рамках теста какого-либо масштабирования объема хранимых данных я не планировал, поэтому остановился на введении дополнительных сущностей в дополнительном Python-файле.
CITY_DATA: Dict[str, CityWeatherTimeline] = {
"Санкт-Петербург": CityWeatherTimeline(
at_11=CityWeather(
temp_now="+20°",
condition_now="Облачно",
emoji_now="🌤️",
feels_like="Ощущается как +19°",
wind="5 м/с",
humidity="62%",
day_forecast=[
DayPoint("+21°", "Переменная облачность", "13:00", "weather-partly-cloudy"),
# ...
],
),
# ...
}
Данные хранятся в словаре CITY_DATA. Я предварительно подготовил их с помощью ChatGPT и немного подогнал под формат нашей задачи. Для структурирования данных используются датаклассы (DayPoint, CityWeather, CityWeatherTimeline).
Актуальная погода в текущем городе
Итак, мы выбрали город из выпадающего списка и программа получила необходимые данные из словаря. Теперь надо куда-то эти данные вывести.
Для этого я создал класс WeatherScreen, где за каждое поле отвечают динамические свойства StringProperty. Это встроенный механизм Kivy: как только мы меняем значение переменной в Python, текст в интерфейсе обновляется автоматически.
class WeatherScreen(MDScreen):
temperature = StringProperty("")
city = StringProperty("")
weather_emoji = StringProperty("")
weather_status = StringProperty("")
Таким классом мы описываем информационный блок, а также поля, для которых мы планируем отобразить данные: текущую температуру, эмодзи погоды и статус (например, «Облачно»). За визуальную часть по-прежнему отвечает Kivy Language:
MDLabel:
id: temperature_label
text: root.temperature
font_size: "64sp"
halign: "center"
На выходе получается главный блок с данными по погоде.

Дополнительные данные
Помимо температуры и статуса погоды в прогноз обычно добавляют детали — скорость ветра и влажность.

Оба блока по сути являются близнецами, поэтому чтобы понять, как они реализованы, достаточно будет рассмотреть код только одной сущности. Сами они объявляются в упомянутом ранее class WeatherScreen(MDScreen):
class WeatherScreen(MDScreen):
# ...
wind_text = StringProperty("")
humidity_text = StringProperty("")
Оформление также производится в weatherapp.kv. Рассмотрим на примере карточки «Ветра»:
MDBoxLayout:
size_hint_y: 0.35
spacing: "10dp"
MDCard:
orientation: "vertical"
padding: "10dp"
elevation: 1
radius: [15, 15, 15, 15]
MDLabel:
text: "Ветер"
halign: "center"
MDLabel:
text: "💨"
font_size: "36sp"
font_name: root.emoji_font_name if root.emoji_font_name else "Roboto"
halign: "center"
valign: "middle"
MDLabel:
text: root.wind_text
halign: "center"
Как видите, эмодзи для данных разделов я задал статически в разметке, так как они не будут меняться в ходе работы приложения.
Прогноз погоды на день
Больше всего повозиться пришлось с прогнозом погоды на день. Логично, что пользователю хочется знать, какая погода будет через пару часов. Для этого мы реализуем последний блок на нашем экране:

Несмотря на количество данных, верстка каждой карточки в Kivy практически идентична предыдущим блокам. Мы используем MDCard с горизонтальной ориентацией, внутри которой размещаются иконка погоды и текстовые данные (температура и время).
Вот пример лейаута для одной карточки: (если смотреть на скриншот — это первое значение в 13:00)
MDCard:
size_hint_y: 0.22
orientation: "horizontal"
padding: "10dp"
spacing: "10dp"
elevation: 1
radius: [15, 15, 15, 15]
MDBoxLayout:
orientation: "vertical"
MDIcon:
icon: root.f1_icon
pos_hint: {"center_x": 0.5, "center_y": 0.7}
font_size: "32sp"
theme_text_color: "Primary"
MDLabel:
text: root.f1_temp + "\n" + root.f1_time
halign: "center"
Большая часть работы заключалась в том, чтобы расписать много одинаковых блоков, а затем наполнить их большим количеством данных в weather_data.py.
Чтобы оживить интерфейс, я добавил кнопку, которая сдвигает время на час вперед и обновляет прогноз. Это имитация запроса к API, реализованная через цепочки анимаций и виджет MDSpinner.
Реализация метода update_weather:
def update_weather(self):
# функция "фейкового обновления страницы" по нажатию кнопки
main_box = self.ids.main_box
spinner = self.ids.spinner
spinner.opacity = 1
spinner.active = True
anim_down = Animation(y=-90, d=0.25, t="out_quad")
anim_up = Animation(y=0, d=0.25, t="out_quad")
def after_down(*_):
Clock.schedule_once(lambda *_: finish(), 1.0)
def finish(*_):
app = MDApp.get_running_app()
if app:
app.refresh_current_city_weather()
anim_up.start(main_box)
spinner.active = False
spinner.opacity = 0
anim_down.bind(on_complete=after_down)
anim_down.start(main_box)
В разметке спиннер скрыт (opacity: 0) и позиционируется по центру экрана, появляясь только в момент «загрузки».
А также готового блока KivyMD — MDSpinner:
MDSpinner:
id: spinner
size_hint: None, None
size: "40dp", "40dp"
pos_hint: {"center_x": 0.5}
center_y: root.height - ((0 - root.ids.main_box.y) / 2)
active: False
opacity: 0
color: 0.05, 0.2, 0.45, 1
Для работы анимации обновления нужно подготовить два стейта данных в weather_data.py: исходный (на 11:00) и обновленный (на 12:00). При переключении город подтянет новые значения из словаря CITY_DATA, создавая эффект реального изменения погоды.
Пример для одного из городов:
"Санкт-Петербург": CityWeatherTimeline(
at_11=CityWeather(
temp_now="+20°",
condition_now="Облачно",
emoji_now="🌤️",
feels_like="Ощущается как +19°",
wind="5 м/с",
humidity="62%",
day_forecast=[
DayPoint("+21°", "Переменная облачность", "13:00", "weather-partly-cloudy"),
DayPoint("+23°", "Солнечно", "15:00", "weather-sunny"),
DayPoint("+26°", "Ясно", "17:00", "weather-sunny"),
DayPoint("+22°", "Облачно", "19:00", "weather-cloudy"),
],
),
at_12=CityWeather(
temp_now="+21°",
condition_now="Небольшая облачность",
emoji_now="🌤️",
feels_like="Ощущается как +20°",
wind="6 м/с",
humidity="58%",
day_forecast=[
DayPoint("+22°", "Переменная облачность", "14:00", "weather-partly-cloudy"),
DayPoint("+24°", "Солнечно", "16:00", "weather-sunny"),
DayPoint("+25°", "Ясно", "18:00", "weather-sunny"),
DayPoint("+21°", "Облачно", "20:00", "weather-cloudy"),
],
),
),
Запускаем код
- в VS Code перейдите в директорию WeatherTest;
- повторно создайте виртуальное окружение venv и поставьте зависимости:
pip install kivy kivymd; - выберите корректный интерпретатор;
- откройте main.py и запустите.

Как видите, на выходе мы получили неплохо функционирующее приложение.
По умолчанию в KivyMD используются компоненты Material design 2, хотя Google активно продвигает новую версию Material You. Из-за этого стандартный интерфейс может отдавать ретро-стилем (что, впрочем, сейчас в моде).
Лично для меня это не было проблемой, так как я старался реализовать весь проект целиком и не делал акцент на дизайне. Если вам важна актуальные гайдлайны, можно править свойства блоков вручную или постучаться в комьюнити фреймворка — там подскажут, как подтянуть визуал.
Сборка приложения для разных платформ
Структурно Kivy содержит не только функциональные блоки для написания кода, но и набор утилит для сборки и развертывания приложения. Набор инструментов зависит от платформы, под которую вы хотите собирать приложение, однако общий алгоритм действий одинаковый:
- подготовка проекта — то, что мы делали ранее при написании и тестировании кода;
- создание конфигурационных файлов — специальные файлы, которые содержат технические данные необходимые для сборки;
- сборка — выполняется с помощью отдельных утилит, не входящих в состав kivy.
Основных инструментов два:
- Buildozer — основной инструмент, позволяющий собрать приложение из Python-кода как под десктопные платформы, так и под мобильные;
- Pyinstaller — менее гибкая утилита, которая поможет собрать проект для десктоп платформ: Linux, macOS, Windows.
Давайте соберем наше приложение «Погода» под десктоп на примере Linux. Первым делом ставим сам сборщик:
pip install pyinstaller
Не забудьте создать requirements.txt с зависимостями (kivy, kivymd), чтобы сборщик понимал, что упаковывать. Сама команда сборки выглядит так:
# для macOS и linux
pyinstaller --clean --name WeatherApp --onefile --windowed --add-data "weatherapp.kv:." --add-data "weather_data.py:." main.py
# Для Windows (разница в разделителе ";")
pyinstaller --clean --name WeatherApp --onefile --windowed --add-data "weatherapp.kv;." --add-data "weather_data.py;." main.py
Помните: PyInstaller не умеет в кросс-компиляцию — чтобы получить бинарник под Linux, команду нужно запускать именно в Linux. Если же ваша цель — сборка под Android или iOS, используйте Buildozer.
После выполнения команды в папке dist/ должен появиться файл KivyMDdemo. Выдадим права и запустим:
chmod +x dist/KivyMDdemo
./dist/KivyMDdemo
Если не работает:
В одной статье невозможно предусмотреть все нюансы, которые возникнут при сборке на вашем локальном устройстве. Например, в некоторых случаях вам может потребоваться дописать пути для директорий сборки в main.py. Поэтому в первую очередь рекомендую свериться с официальной документацией проекта.
Для пользователей Linux: вам может потребоваться установка системных библиотек для работы с графическим интерфейсом:
sudo apt-get install libsdl2-dev libsdl2-ttf-dev libsdl2-image-dev
Для отладки можно попробовать выполнить сборку без конфигурационного файла *.spec. При таком сценарии pyinstaller автоматически определит платформу, на которой запущен и соберет файл под нее:
pyinstaller –name KivyMDdemo –onefile –windowed –add-data “material.kv:.” –add-data “requirements.txt:.” main.py
Заключение
Я не ставил цель изучить новую технологию просто ради развития кругозора. Было желание быстро поэкспериментировать с кодом и реализовать свои идеи без погружения в тонкости. По той же причине, я отбросил нишевые решения, заточенные под конкретную платформу. Так как ключевая цель — это гибкость и скорость работы, будет ошибкой лишить себя возможности за вечер превратить условный «калькулятор» для iPhone в рабочий exe-файл для Windows.
Конечно, у кроссплатформенности есть цена. Сложно заставить приложение на 100% соответствовать гайдлайнам каждой ОС — как визуально (в KivyMD это все еще Material 2), так и на уровне системных вызовов.
Но главное преимущество Kivy — это гибкость. Фреймворк идеально подходит как для быстрой проверки гипотез и сборки тестовых стендов, так и для создания полноценных мультиплатформенных проектов, которыми можно оперативно поделиться с пользователями любой операционки.