- BrainTools - https://www.braintools.ru -
Всем доброго дня! В одной из предыдущих статей Вайбкодинг с нейросетью 1: проверяю сборку Flutter-приложения в AppImage [1]:
Сравнил Flutter и Kivy;
Написал Hello World на Dart для Flutter;
Использовал нейросети Perplexity и Giga Chat в качестве инструментов вайб-кодинга;
Собрал приложение для Linux в AppImage.
Предыдущая статья была написана для конкурса «Властелин алгоритмов: сезон „ИИ в разработке“ на Хабре» [2], где основной темой было применение ИИ в разработке. Это был мой первый опыт [3] написания статьи про вайб-кодинг, и мне не очень зашла данная тематика, так как я не знаю Flutter. Ориентироваться только на то, что пишет нейросеть, довольно тяжело, потому что непонятно, к какому результату это может привести и насколько этот результат будет хорошим.
Статья была отражением того факта, что я начал сомневаться в стеке Kivy + Python. Когда я писал на Kivy + KivyMD 2.0.0 и собирал приложение под Linux, я сталкивался с рядом проблем, о которых речь пойдёт ниже в данной статье.
Что же касается Flutter и Dart, то мне понравились удобство и скорость приложения, которые они предоставляют. Однако этот стек нужно учить с нуля. Это несколько затрудняет переписывание приложения Kawai-Focus прямо сейчас, а на освоение Dart и Flutter может уйти много времени. Поэтому я решил попробовать перевести проект на более знакомый мне стек, а Dart и Flutter оставить запасным вариантом и не бросаться в них прямо сейчас.
В данной статье я попытаюсь переписать часть приложения Kawai-Focus на стек: FastAPI + Vue.js + Tauri + Ionic. Также я соберу приложение только с экраном «Таймеры» под Linux в AppImage, которое подойдёт для большинства Linux-дистрибутивов. Реализация одного экрана не будет слишком долгим процессом, а финальная сборка приложения позволит оценить сложность и удобство полного цикла разработки.
Данная статья не будет подробным гайдом, так как размер переписанного кода и его описание слишком велики для одной статьи. Однако я покажу часть кода проекта и расскажу о выбранном стеке, а также о трудностях и преимуществах, с которыми я столкнулся в процессе переписывания проекта. Я буду использовать часть кода старого проекта, который не касается Kivy и KivyMD, поэтому его душа не умрёт, а обновится.
Заваривайте чай, доставайте вкусняшки — пора «старый стек превращать в удобрение для новых помидор»! 🍅
Фреймворк Kivy я впервые увидел несколько лет назад в видеоролике одного блогера. Он рассказывал, как написать на нём простую игру «пинг-понг» и собрать её под Android с помощью Buildozer. Мне это показалось очень занятным, и я вручную переписал его гайд в коде, а затем собрал приложение под Android. Когда я запустил игру на телефоне, для меня это было чем-то волшебным, и мне это очень понравилось. Тогда у меня и возникла мысль попробовать сделать на нём что-то своё. Так, пару лет спустя, и появился прототип приложения Kawai-Focus [4].
Мне удалось реализовать цепочку таймеров и сохранение пользовательских таймеров в базу данных sqlite3. Однако стандартный дизайн Kivy был квадратным и топорным, и его явно нужно было менять. Каким-то чудом я нашёл форк KivyMD 2.0.0 [5], который не заброшен и предоставляет современный Material Design. Я переписал дизайн, и он понравился пользователям в комментариях к статье. Мне казалось, что я почти справился, и я сел за сборку приложения под Linux.
Я ждал чуда, но вместо него получил разрушающее стек озарение…
На самом деле ещё до события, которое поставило на стеке Kivy крест, уже были тревожные звоночки:
Лаги при работе с интерфейсом — особенно это чувствовалось при разворачивании информации о таймере на экране «Таймеры». Блок раскрывается с такими фризами, что это уже стыдно показывать пользователю. Работать с таким интерфейсом неприятно, и, скорее всего, человек просто не захочет пользоваться приложением;
Вёрстка экранов — этот процесс оказался куда более муторным и непонятным, чем вёрстка в HTML. Логи часто сообщают о том, что используемый способ уже deprecated. Можно легко наткнуться на нерабочие варианты позиционирования элементов и другие странности;
Звук, ломающий запуск приложения — мне пришлось перейти с audio_sdl2 на ffpyplayer, так как audio_sdl2 вызывал падение приложения на этапе инициализации. При запуске Kivy не удавалось корректно инициализировать SDL-аудиодрайвер (отсутствие или конфликт [6] системных аудиобиблиотек), из-за чего приложение завершалось ещё до отображения интерфейса;
Работа с версией KivyMD — разработка ведётся с форком, где рекомендуется либо клонировать репозиторий с главной веткой, либо использовать ссылку на zip-архив этой ветки. Это крайне неудобно в работе, например, с Poetry, поскольку зависимость невозможно зафиксировать как версионированный пакет: отсутствуют релизы и теги, а значит, нельзя гарантировать воспроизводимую сборку и стабильность окружения.
Окончательно добил проект на Kivy процесс сборки приложения под Linux. Приложение требует наличия древней и устаревшей библиотеки для сенсорных экранов внутри Flatpak-пакета. Всё это в итоге сильно увеличивает размер сборки.
Вдобавок ко всему этому Kivy заметно устарел, и на нём пишет всё меньше людей. В результате получился бы очень специфичный, медленный, тяжёлый и нишевый продукт, которому скорее место в моих статьях — в качестве музейного экспоната и прототипа, а не в роли качественного и серьёзного приложения.
Наступил тот момент, когда Kawai-Focus должен перейти на новый стек. Что же это может быть? Как я писал во вступлении к этой статье, я уже успел попробовать Flutter + Dart и пришёл к выводу, что это хороший стек, но переход на него займёт слишком много времени. Некий Сергей уже вовсю ждёт возможности опробовать мой Kawai-Focus, поэтому уход в кардинально новый стек — это заставить его ждать условный месяц. Да и сам я хочу получить результат побыстрее, поэтому начал подыскивать более удобные альтернативы на Python.
Одной из таких альтернатив мог оказаться фреймворк Flet. Казалось бы, хороший вариант: написан на Python, использует Flutter под капотом, позволяет писать код быстрее и проще, да и устаревших библиотек не требует. Однако верстать внешний вид на Python оказалось бы крайне неудобно. В качестве прототипа он подошёл бы хорошо, но довести интерфейс до уровня полноценного продукта было бы очень непросто.
В итоге я решил взять на пробу универсальный стек, который больше подойдёт веб-разработчику, решившему попробовать себя в кроссплатформенной разработке, не теряя при этом своей «веб-идентичности».
На самом деле изначально я обучался именно на веб-разработчика с использованием Python, а использование Kivy немного увело меня в сторону от этого направления. Работать сразу в двух стэках — это серьёзное распыление внимания [7] в профессиональной карьере. Поэтому я решил сократить это распыление и немного подкорректировать используемый стек.
Основной стек:
FastAPI — вся логика [8] приложения и бэкенд;
Vue.js — отвечает за внешний вид (фронтенд);
Ionic — нативный UI и мобильная версия;
Tauri — движок для десктопной версии приложения.
Остановимся немного на каждой технологии.
FastAPI — современный асинхронный Python-фреймворк для создания API, ориентированный на высокую производительность, строгую типизацию и удобство разработки. В десктопном приложении FastAPI обычно выступает в роли локального бэкенда, обрабатывающего бизнес-логику, работу с данными и взаимодействие с внешними сервисами.
Благодаря ASGI, асинхронности и использованию Starlette и Pydantic, FastAPI показывает высокую скорость как в веб-приложениях, так и в десктопных сценариях, где фронтенд общается с ним через HTTP или WebSocket. При этом асинхронный подход больше подходит для веб-приложений с большим количеством пользователей и запросов. Для локального приложения с одним пользователем и базой данных sqlite3 вполне достаточно обычного синхронного подхода. Однако я не исключаю, что в будущем могу реализовать более сложную веб-версию, где асинхронность будет действительно необходима.
С этим фреймворком я уже знаком, но опробовал далеко не все его возможности. В основном я использовал FastAPI для работы с WebSocket и раздачи HTML. Это для меня комфортнее, чем Dart, так как здесь у меня есть пусть и небольшой, но реальный опыт, который хочется расширить и углубить.
Vue.js — прогрессивный JavaScript-фреймворк для создания пользовательских интерфейсов с упором на реактивность и компонентный подход. В десктопном приложении Vue.js отвечает за внешний вид и пользовательское взаимодействие, выступая фронтендом, работающим поверх веб-движка (например, Tauri). Благодаря лёгкости, виртуальному DOM и эффективной реактивной системе Vue.js обеспечивает высокую отзывчивость интерфейса как в вебе, так и в десктопных приложениях.
С этим фреймворком я знаком слабо, но у меня был небольшой опыт работы с ним примерно два года назад. Честно говоря, у меня больше практики с React и обычным JavaScript. Vue.js придётся освежить в памяти [9], но так как с фронтендом я всё же сталкивался, процесс пойдёт заметно быстрее, чем изучение Dart и Flutter с нуля.
Ionic — фреймворк для создания кроссплатформенных приложений с использованием веб-технологий (HTML, CSS и JavaScript). В десктопных и мобильных приложениях Ionic отвечает за нативный внешний вид интерфейса и доступ к возможностям устройства, работая поверх фронтенда (например, Vue.js). За счёт оптимизированных UI-компонентов и интеграции с нативными API Ionic обеспечивает приемлемую производительность как на мобильных устройствах, так и в десктопных оболочках.
Как раз раньше я пробовал проходить гайды по Ionic в паре с Vue.js, что внушает надежду на более быстрое освоение этого фреймворка по сравнению с Flutter.
Tauri — фреймворк для создания кроссплатформенных десктоп-приложений на базе веб-технологий. В десктопном приложении Tauri выступает в роли оболочки и движка, а его бэкенд написан на Rust, что даёт высокую производительность, безопасность памяти и существенно меньший размер сборки по сравнению с Electron-подобными решениями. Благодаря тесной интеграции с нативной системой и минимальному оверхеду Tauri обеспечивает быструю работу интерфейса и хорошую отзывчивость на всех поддерживаемых платформах.
Схема десктоп-приложения на Tauri:
Vue
↓
Tauri
↓
System WebView
↓
Native Window
Vue — отвечает за пользовательский интерфейс и логику фронтенда, формируя HTML, CSS и JavaScript-приложение;
Tauri — связующий слой между веб-интерфейсом и операционной системой, управляющий окнами, событиями и безопасным доступом к нативным возможностям;
System WebView — системный веб-движок, в котором отрисовывается Vue-приложение без встроенного браузера;
Native Window — нативное окно операционной системы, с которым взаимодействует пользователь.
Tauri позволяет работать с системными функциями напрямую из приложения:
Файловая система — чтение, запись и управление файлами с контролируемыми правами доступа;
Автозапуск — запуск приложения вместе с системой;
Системный трей — иконка и управление из области уведомлений;
Нотификации — показ системных уведомлений;
Системные хоткеи — регистрация глобальных сочетаний клавиш.
Tauri изначально спроектирован с упором на строгую безопасность: доступ к нативным API ограничен, разрешения настраиваются явно, а фронтенд изолирован от системы, что существенно снижает риск уязвимостей.
Всё это звучит хорошо, но раз речь идёт о веб-приложении, возникает закономерный вопрос: а как обстоят дела с потреблением оперативной памяти и размером итоговой сборки?
Одно из ключевых преимуществ Tauri — минимальный размер приложения и низкое потребление ресурсов. Так как Tauri использует системный WebView, а не встраивает собственный браузер, размер сборки обычно составляет несколько мегабайт, а не сотни, как у Electron-подобных решений. За счёт бэкенда на Rust и отсутствия тяжёлого runtime Tauri потребляет значительно меньше оперативной памяти, что особенно важно для фоновых утилит и постоянно работающих приложений.
Дополнительно я планирую скомпилировать Python-код с помощью Nuitka в C-код — это позволит ускорить выполнение и уменьшить занимаемый объём по сравнению с обычным Python.
В итоге получился довольно интересный стек как для веб-, так и для десктоп-разработки, который мне уже не терпится опробовать в деле. Кроме того, если убрать Tauri, можно сделать веб- и мобильную версии приложения, практически не меняя код. Звучит многообещающе, но как это покажет себя в реальном проекте — узнаете в следующем разделе статьи.
Теперь, когда обозначен новый стек приложения, пора заняться процессом «переезда» Kawai-Focus на него. Так совпало, что этот процесс происходит перед Новым годом, и получается, что вместе с обновлением года и календаря обновится и код приложения.
Лично я очень жду новогоднего чуда и хочу, чтобы моё приложение в новом году стало ещё лучше, а у читателей сбылись их надежды и мечты в наступающем 2026 году ⛄
Часть кода из приложения Kawai-Focus я перенесу в новый проект, улучшая его в процессе переноса. Идея заключается в том, чтобы брать старый код и по возможности адаптировать и улучшать его под новый стек. Таким образом я сохраню основную идею приложения, но при этом сразу сделаю его лучше.
Например, CRUD-операции для работы с базой данных будут отлично работать и на новом стеке. В старом коде уже есть решения, которые вполне можно доработать и улучшить. О процессе рефакторинга я расскажу в следующих статьях, а сейчас сосредоточусь именно на описании переезда и общих идеях проекта.
Я решил не трогать старый репозиторий, а создать новый. В старом репозитории я укажу, что проект переехал на новый стек, и добавлю ссылку на него. Таким образом код старого проекта останется на память и будет использоваться в качестве примеров для статей на сайте «Код на салфетке» [10].
Также изменениям подвергнется структура проекта, менеджер зависимостей (замена poetry на uv), а ещё будет добавлен линтер ruff, работающий под управлением pre-commit. По этой теме недавно у Ивана proDreams вышла отличная статья «ИИ бот-модератор 1: Начало проекта» [11], где подробно рассказывается, как настроить uv и ruff под управлением pre-commit. Поэтому, в целях экономии времени, я воспользуюсь этим материалом для настройки базовой инфраструктуры проекта.
Менеджер пакетов uv написан на Rust и работает быстрее poetry, а ruff — это линтер, который проверяет код, тогда как pre-commit — фреймворк, автоматически запускающий набор проверок. Часто я сосредотачиваюсь на создании прототипа, игнорируя современные инструменты разработки. Поэтому в новом 2026 году я хочу использовать актуальные инструменты и сделать стек более свежим.
Теперь я займусь структурой проекта. Ниже я покажу примерную структуру, которая выступает в роли наброска первоначальной идеи. Итоговый вариант, скорее всего, будет отличаться от этой заготовки — ровно так же, как набросок художника отличается от готовой картины.
Базовая структура проекта:
kawai-focus-v2/
├── src/ # src-layout от uv (ВАЖНО)
│ └── kawai_focus_v2/ # python package
│ ├── database/ # всё, связанное с БД
│ │ ├── __init__.py
│ │ ├── db.py # engine, SessionLocal
│ │ ├── models.py # Base + импорт моделей
│ │ └── mixins.py
│ │
│ ├── tests/
│ │
│ ├── alembic/ # Alembic ка�� часть приложения
│ │ ├── __init__.py
│ │ ├── README.md
│ │ ├── env.py
│ │ ├── script.py.mako
│ │ └── versions/
│ │
│ ├── __init__.py
│ ├── main.py # FastAPI entrypoint
│ ├── api/ # routers
│ ├── core/ # config, settings, paths
│ │ ├── config.py
│ │ └── settings.py
│ ├── schemas/ # Pydantic схемы
│ └── services/ # бизнес-логика
│
├── client/ # Ionic + Vue (frontend)
│ ├── src/
│ │ ├── main.js
│ │ ├── App.vue
│ │ ├── theme/
│ │ ├── views/
│ │ ├── services/
│ │ │ └── api.js
│ │ └── router/
│ │ └── index.js
│ │
│ ├── tests/
│ ├── public/
│ ├── capacitor.config.json
│ ├── ionic.config.json
│ ├── package.json
│ └── vite.config.js
│
├── desktop/ # Tauri
│ └── src-tauri/
│ ├── tauri.conf.json
│ └── src/
│ └── main.rs
│
├── .gitignore
├── .pre-commit-config.yaml
├── alembic.ini
├── .python-version
├── pyproject.toml
├── uv.lock
├── README.md
└── LICENSE
Описание основных элементов структуры:
src/kawai_focus_v2/ — основной Python-пакет приложения, оформленный по src-layout, в котором размещены бизнес-логика, API и инфраструктурный код;
client/ — фронтенд-приложение на Ionic + Vue.js, отвечающее за пользовательский интерфейс и взаимодействие с бекендом;
desktop/ — конфигурация и исходный код десктопной оболочки на Tauri, связывающей фронтенд с нативным окружением ОС;
.pre-commit-config.yaml — конфигурация хуков pre-commit для автоматической проверки кода перед коммитами;
.python-version — файл с указанием версии Python, используемой в проекте, для синхронизации окружений разработки;
pyproject.toml — основной файл конфигурации проекта, описывающий зависимости, метаданные и настройки инструментов Python;
uv.lock — lock-файл менеджера зависимостей uv, фиксирующий точные версии пакетов для воспроизводимых сборок;
LICENSE — файл с лицензией проекта, определяющий условия использования и распространения кода.
Я использую следующие команды для начала работы в проекте:
git clone git@github.com [12]:Arduinum/kawai-focus-v2.git — клонирует репозиторий проекта из GitHub в локальную рабочую директорию;
Установка uv [13] (любым удобным способом);
uv init --package kawai-focus-v2 — инициализирует Python-проект с использованием uv, создавая базовую структуру и конфигурационные файлы;
cd kawai-focus-v2 — переходит в директорию инициализированного проекта;
uv add --dev pre-commit — добавляет pre-commit в dev-зависимости проекта для автоматической проверки кода;
uv run pre-commit install — устанавливает Git-хуки pre-commit, чтобы проверки запускались автоматически перед каждым коммитом.
После инициализации проекта у меня получилась следующая структура.
Мой пример дополняет статью Ивана тем, что в моём случае репозиторий и такие файлы, как README.md [14], .gitignore и LICENSE, создаются в GitHub, а не на локальном ПК. Приятный момент заключается в том, что команда uv init --package kawai-focus-v2, выполненная рядом с каталогом репозитория, а не внутри него, не пересоздаёт и не перезатирает содержимое README.md [14] и .gitignore.
Далее я проверяю работу pre-commit, выполнив следующую команду. Я впервые использую pre-commit, поэтому было особенно интересно посмотреть, какой результат она выдаст.
uv run pre-commit run --all
Данная команда запускает все настроенные pre-commit хуки вручную для всех файлов проекта, а не только для тех, которые были изменены или добавлены в индекс Git.
Вывод команды:
trim trailing whitespace.................................................Failed
- hook id: trailing-whitespace
- exit code: 1
- files were modified by this hook
Fixing .gitignore
check yaml...........................................(no files to check)Skipped
check for case conflicts.................................................Passed
check for merge conflicts................................................Passed
fix end of files.........................................................Passed
pyupgrade............................................(no files to check)Skipped
ruff check...........................................(no files to check)Skipped
ty check.................................................................Passed
Приятно видеть, что инструмент сразу начал приносить пользу. Хук trim trailing whitespace нашёл лишние пробелы и удалил их, о чём говорит сообщение files were modified by this hook.
После этого я добавляю все файлы из корня проекта и делаю первый коммит.
git add . && git commit -m "feat: init project"
Вывод команды:
trim trailing whitespace.................................................Passed
check yaml...............................................................Passed
check for case conflicts.................................................Passed
check for merge conflicts................................................Passed
fix end of files.........................................................Passed
pyupgrade................................................................Passed
ruff check...............................................................Passed
ty check.................................................................Passed
[master 4608b1a] feat: init project
7 files changed, 214 insertions(+), 2 deletions(-)
create mode 100644 .pre-commit-config.yaml
create mode 100644 .python-version
create mode 100644 .vscode/settings.json
create mode 100644 pyproject.toml
create mode 100644 src/kawai_focus_v2/__init__.py
create mode 100644 uv.lock
Все хуки отработали корректно, о чём свидетельствует статус Passed у каждого из них, и коммит успешно применился.
Чуть позже выяснилось, что я использую более старую версию ty-pre-commit, поэтому я выполнил команду обновления. Заодно обновился и ruff-pre-commit, который тоже был устаревшим.
pre-commit autoupdate
Вывод команды:
[https://github.com/pre-commit/pre-commit-hooks] already up to date!
[https://github.com/asottile/pyupgrade] already up to date!
[https://github.com/astral-sh/ruff-pre-commit] updating v0.13.0 -> v0.14.10
[https://foundry.fsky.io/vel/ty-pre-commit] updating 0.0.3 -> 0.0.8
Обновлять и писать новый код я начну с бекенда так как основная логика приложения у меня будет и именно там. Начну с установки dev зависимостей для python.
uv add --dev pytest ruff
pytest — библиотека для тестов;
ruff — линтер для проверки кода на ошибки [15] (его использует alembic у меня в проекте).
Я откажусь от библиотеки ffpyplayer для воспроизведения звука, так как она может вызывать проблемы при установке на Windows и в целом больше ориентирована на Linux. В данной ситуации самым простым и надёжным решением будет использование фронтенда для воспроизведения звука — через Web Audio API или HTML5 Audio. Это позволит одинаково просто воспроизводить звук на разных платформах, не задумываясь о поддержке и установке аудиобиблиотек под различные операционные системы.
Установка основных зависимостей:
uv add fastapi sqlalchemy pydantic-settings alembic uvicorn
FastAPI — веб-фреймворк для создания API и бэкенд-логики приложения;
SQLAlchemy — ORM и инструментарий для работы с базой данных и SQL;
pydantic-settings — управление конфигурацией приложения через настройки и переменные окружения;
Alembic — инструмент для управления миграциями схемы базы данных;
Uvicorn — ASGI-сервер для запуска FastAPI-приложения.
Инициализация alembic:
uv run alembic init src/kawai_focus_v2/database/alembic
Настройки alembic я так же взял из старого проекта и поменял лишь импорты.
Я понимаю, что FastAPI хорош для асинхронной работы, но у меня десктоп-приложение с SQLite3, который не поддерживает асинхронность. Если я напишу веб-версию в будущем, которой потребуется асинхронность, то скорее всего создам её в отдельном репозитории ибо так будет гораздо удобнее.
Файлы models.py [16] и mixins.py [17] я просто скопирую из старого проекта и поменяю пути импорта если это будет необходимо. Файл settings.py [18] теперь будет лежать в отдельной папке (core/) — это признак явного разделения инфраструктуры и бизнес-кода: настройки становятся частью слоя конфигурации, а не «глобальной помойкой» проекта.
Создание миграции для таблицы Timer в бд:
uv run alembic revision --autogenerate -m "Create Timer"
Вывод команды:
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.autogenerate.compare] Detected added table 'timer'
Generating /media/user_name/Files2/Programming/Python/Projects/kawai-focus-v2/src/kawai_focus_v2/database/alembic/versions/2025_12_25_1504-503b50886979_create_timer.py ... done
Running post write hook 'ruff' ...
1 file reformatted
done
Кстати я заметил, что у меня теперь два ruff один в pre-commit используется, а другой в dev зависимостях и настроен для alembic и миграций. Есть два варинта: оставить как есть два ruffa или же оставить один ruff и дёргать его из .venv окружения c pre-commit для проверки перед коммитом. Для этого нужно будет поменять конфиг .pre-commit-config.yaml.
Напишите в комметариях какой вариант грамотнее на ваш взгляд. Мне будет очень интересно узнать ваше мнение. Я пока оставлю оба варианта и дождусь ваших ответов.
В .env поменяю название бд на NAME_DB = "timer.sqlite3" так будет более явнее чем timer.db.
Применение миграций:
uv run alembic upgrade head
Вывод команды:
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.runtime.migration] Running upgrade -> 503b50886979, Create Timer
Отлично у меня теперь есть база данных с полями, с которой уже можно работать. В следующей части статьи я расскажу более подробно о самом коде бекенда, а пока я перейду к процессу создания бинарника.
Nuitka — это компилятор для Python, который превращает Python-код в нативный исполняемый файл, компилируя его через C и убирая зависимость от интерпретатора на целевой машине.
Бинарник — это готовый исполняемый файл в машинном коде (например, ELF в Linux), который запускается напрямую операционной системой без shell-скриптов и интерпретаторов. Он ускоряет работу Python-кода, потому что компилирует его в машинный код, устраняя интерпретацию на лету и уменьшая накладные расходы на запуск и импорт модулей.
Процесс компиляции будет происходить на операционной системе Linux Debian!
Установка зависимостей для компиляции:
uv add --dev nuitka patchelf
Для переиспользования кэша при компиляции можно установить следующую библиотеку.
sudo apt install ccache
ccache при работе с Nuitka кэширует результат компиляции C/C++ файлов, которые Nuitka генерирует из Python-кода, чтобы при повторной сборке те же исходники не компилировались заново.
Теперь можно запустить сам процесс компиляции.
uv run nuitka
--standalone
--follow-imports
--include-module=uvicorn
--include-package=fastapi
--include-package=sqlalchemy
--output-filename=backend-x86_64-unknown-linux-gnu.bin
./src/kawai_focus_v2/main.py
Разбор команды:
uv run nuitka — запускает Nuitka через твой CLI (uv), чтобы скомпилировать Python-код;
--standalone — собирает полностью независимый бинарник со всеми зависимостями, не требующий установленного Python;
--follow-imports — включает в сборку все импортированные модули автоматически;
--include-module=uvicorn — явно добавляет модуль uvicorn в бинарник, даже если он используется динамически;
--include-package=fastapi — включает пакет fastapi целиком в сборку;
--include-package=sqlalchemy — включает пакет sqlalchemy целиком в сборку;
--output-filename=backend-x86_64-unknown-linux-gnu.bin — задаёт имя и путь для сгенерированного бинарника;
./src/kawai_focus_v2/main.py [19] — указывает основной Python-файл, который будет точкой входа для компиляции.
В конце компиляции появится примерно следующая информация.
Nuitka: Successfully created 'main.dist/backend-x86_64-unknown-linux-gnu.bin'.
Строка говорит о том, что бинарный файл успешно создался.
Протестировать работу бинарника можно, запустив его из терминала.
./main.dist/backend-x86_64-unknown-linux-gnu.bin
Вывод команды:
INFO: Started server process [151235]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://127
.0.0.1:8090 (Press CTRL+C to quit)
INFO: 127.0.0.1:32958 - "GET /docs HTTP/1.1" 200 OK
INFO: 127.0.0.1:32958 - "GET /openapi.json HTTP/1.1" 200 OK
Судя по логам, Uvicorn успешно поднял бекенд на FastAPI по локальному адресу http://127.0.0.1:8090 [20].
После того как я успешно создал, настроил и протестировал бинарник бекенда, я перехожу к фронтенду.
Установка npm:
curl -fsSL https://deb.nodesource.com/setup_24.x [21] | sudo -E bash - — добавляет репозиторий Node.js 24.x и настраивает его для установки через apt;
sudo apt install -y nodejs — устанавливает Node.js и npm из настроенного репозитория;
node -v — проверяет версию установленного Node.js;
v24.12.0 — пример вывода команды node -v;
npm -v — проверяет версию установленного npm;
11.6.2 — пример вывода команды npm -v;
cd client — переходит в директорию фронтенда проекта;
npm run build — собирает фронтенд-проект в production-режиме, создавая готовые к раздаче файлы.
Установка ionic:
sudo npm install -g @ionic/cli — устанавливает глобально CLI фреймворка Ionic;
ionic --version — проверяет установленную версию Ionic;
7.2.1 — пример вывода команды ionic --version;
ionic serve — запускает локальный dev-сервер для проекта Ionic и открывает его в браузере.
Я написал код для фронтенда, который получает от бекенда список таймеров. Так как описание этого кода не помещается в текущую статью, я перенесу рассказ о нём на одну из следующих частей.
Мне понадобятся пару плагинов для взаимодействия tauri и фронтенда.
npm install @tauri-apps/plugin-shell @tauri-apps/api
Разбор команды:
@tauri-apps/plugin-shell — плагин для Tauri, который позволяет фронтенду запускать сторонние процессы и команды на локальной машине через безопасный API;
@tauri-apps/api — набор официальных API Tauri для работы с файловой системой, окнами, уведомлениями, диалогами и другими возможностями нативного приложения.
Для проверки запускаю фронтенд через ionic.
ionic serve
Вывод команды:
> vite --host=localhost --port=8100
[vite] ➜ Local: http://localhost:8100/
[vite] ➜ press h + enter to show help
[INFO] Development server running!
Фронтенд успешно поднялся на локальном адресе http://localhost:8100/ [22].
Если в консоли браузера появляется ошибка, связанная с CORS, и данные не отображаются, это означает, что необходимо разрешить CORS на бекенде.
После того как я разрешил URL фронтенда в CORS бекенда, запрос успешно выполнился, а данные отобразились на фронтенде.
Пока единственное действие, которое можно выполнить на странице таймеров, — развернуть информацию о таймере. Интересный момент: если развернуть другой таймер, первый будет сворачиваться автоматически. Это позволяет сократить путь пользователя и не держать большое количество раскрытых таймеров одновременно.
Теперь я перейду к одной из самых интересных частей проекта — настройке desktop-версии приложения.
Установка зависимостей
sudo apt install -y
build-essential
curl
wget
file
libssl-dev
libgtk-3-dev
libayatana-appindicator3-dev
librsvg2-dev
pkg-config
libwebkit2gtk-4.1-dev
Разбор зависимостей:
build-essential — устанавливает набор инструментов для компиляции C/C++ (gcc, g++, make и др.);
curl — утилита для скачивания файлов и работы с URL через командную строку;
wget — ещё одна утилита для загрузки файлов из интернета;
file — определяет тип файла (например, бинарник, текстовый или скрипт);
libssl-dev — библиотеки и заголовочные файлы OpenSSL для работы с криптографией и HTTPS;
libgtk-3-dev — библиотеки и заголовки GTK3 для создания графического интерфейса;
libayatana-appindicator3-dev — библиотеки для создания системных индикаторов/иконок в панели задач;
librsvg2-dev — библиотеки для работы с SVG-файлами (отрисовка и конвертация в растровые изображения);
pkg-config — утилита для получения информации о библиотечных файлах и их флагах компиляции;
libwebkit2gtk-4.1-dev — библиотеки WebKitGTK для интеграции веб-контента в GTK-приложения.
Установка Rust:
Я не смог скачать установщик с официального сайта (курсор зависал и пакеты не скачивались), поэтому пришлось использовать зеркала:
export RUSTUP_DIST_SERVER=https://rsproxy.cn
export RUSTUP_UPDATE_ROOT=https://rsproxy.cn/rustup
curl --proto '=https' --tlsv1.2 -sSf https://rsproxy.cn/rustup-init.sh [23] | sh — скачивает и запускает скрипт установки Rust через rustup;
Rust is installed now. Great! — пример вывода скрипта, подтверждающий успешную установку Rust;
source ~/.cargo/env — подгружает переменные окружения Rust в текущую сессию терминала;
cargo --version — проверяет версию установленного пакетного менеджера и сборщика проектов Cargo;
cargo 1.92.0 (344c4567c 2025-10-21) — пример вывода команды cargo --version;
rustc --version — проверяет версию установленного компилятора Rust;
rustc 1.92.0 (ded5c06cf 2025-12-08) — пример вывода команды rustc --version.
Установка Tauri:
cargo install tauri-cli — устанавливает CLI-инструменты Tauri через Cargo для работы с проектами Tauri;
cargo tauri --version — проверяет установленную версию Tauri CLI;
tauri-cli 2.9.6 — пример вывода команды, показывающий текущую версию Tauri CLI.
Инициализация проекта:
cargo tauri init
Необходимо будет ответить на несколько вопросов в интерактивном режиме: имя приложения, URL сервера, команды сборки фронтенда и т.д.
✔ What is your app name? · Kawai-Focus
✔ What should the window title be? · Kawai-Focus
✔ Where are your web assets (HTML/CSS/JS) located, relative to the "<current dir>/src-tauri/tauri.conf.json" file that will be created? · ../client/dist
✔ What is the url of your dev server? · http://localhost:8100
✔ What is your frontend dev command? · cd ../client && ionic serve
✔ What is your frontend build command? · cd ../client && npm run build
Рекомендую добавить команду, которая подключает плагин для запуска сторонних процессов и команд из Tauri-приложения.
cargo add tauri-plugin-shell
В данной статье я не поместил информацию о настройке конфигурации Tauri — об этом я обязательно расскажу в одной из следующих частей. Задача этой статьи — показать идею, установку, первичную настройку, запуск приложения и процесс сборки.
Поговорим о смене иконок приложения на свои. Приложение использует несколько размеров иконок, но позволяет из одного изображения (например, 512×512) сгенерировать все необходимые размеры автоматически.
Дефолтные icons:
Команда для создания иконок:
cargo tauri icon path_your_icon
После выполнения команды в папке icons дефолтные иконки заменились на мои иконки с помидорками.
Запуск в режиме разработки:
cargo tauri dev
Режим разработчика удобен тем, что позволяет запускать консоль разработчика, похожую на консоль браузера: анализировать ошибки, смотреть HTML и многое другое. В данном случае консоль показала, что приложение не может найти таблицу taimer в базе данных.
Работоспособный экран приложения выглядит так:
Приступим к последнему этапу — сборке приложения AppImage под Linux. AppImage — это формат пакета для Linux, который позволяет распространять приложение вместе со всеми его зависимостями в одном исполняемом файле, который можно запускать напрямую, без установки и изменения системы. Такое приложение удобно запускать, оно не требует установки, поэтому я решил начать с него.
⚠️ Важно: сборка AppImage возможна только на Linux, и для неё должны быть установлены системные зависимости (libwebkit2gtk, libgtk-3, patchelf и др.).
Команда сборки:
cargo tauri build
Вывод команды:
Finished 3 bundles at:
/media/user_name/Files2/Programming/Python/Projects/kawai-focus-v2/desktop/src-tauri/target/release/bundle/deb/Kawai-Focus_0.1.0_amd64.deb
/media/user_name/Files2/Programming/Python/Projects/kawai-focus-v2/desktop/src-tauri/target/release/bundle/rpm/Kawai-Focus-0.1.0-1.x86_64.rpm
/media/user_name/Files2/Programming/Python/Projects/kawai-focus-v2/desktop/src-tauri/target/release/bundle/appimage/Kawai-Focus_0.1.0_amd64.AppImage
Последние строки вывода показывают, что команда успешно собрала приложение сразу в трёх форматах: .deb, .rpm и .AppImage.
Для запуска приложения достаточно нажать на файл Kawai-Focus_0.1.0_amd64.AppImage, который находится по пути desktop/src-tauri/target/release/bundle/appimage.
Если запустить его из терминала, то в логи приложения, отображаемые Tauri, будет видно, что происходит в процессе. Для этого нужно написать специальный код на языке Rust. Подробнее об этом я обязательно расскажу в следующей части статьи. А пока что я успешно запустил приложение на Debian 12 с X11.
Мне очень понравилось, что хорошо работает адаптив, и в теории им будет удобно пользоваться даже на мобильных устройствах.
Также мне удалось успешно запустить приложение на своём ноутбуке с Debian 13 и Wayland.
Но не обошлось и без ложки дёгтя. Приложение не смогло отобразить окно в Arch Linux на ПК Сергея (того самого человека, который больше всего ждал моё приложение). Возможно, проблема связана с webkit2gtk, который предоставляет компонент для рендеринга веб-страниц внутри приложений, использующих GTK (графическую библиотеку).
Так же на ПК Ивана в Arch запустилось отображение, но возникла проблема с undefined symbol: rl_print_keybinding при запуске бекенда, которая связана с динамическими зависимостями, которые bash подхватывает внутри AppImage.
Будет очень интересно решить эти проблемы и запустить Kawai-Focus на Arch Linux. Такие ситуации только подстёгивают мой интерес [24] к продолжению работы над приложением.
Сегодня я собрал первый урожай новых помидоров, которые вырастил, опираясь на предыдущий опыт выращивания 🙂
Я очень рад, что у меня получилось собрать первое работающее десктопное приложение под Linux. Однако не обошлось без подводных камней, поэтому в следующей статье я расскажу, с какими проблемами столкнулись мои товарищи при тестировании на Arch.
Также я постараюсь исправить критические проблемы запуска бэкенда на Arch и успешно запустить на нём своё приложение. Для этого, скорее всего, я уберу sh-скрипт и создам бинарник другим способом. Я пытался с помощью Nuitka собрать один файл бинарника, но Tauri, так сказать, немного испортил его в процессе сборки. Что с ним произошло, узнаете в следующей статье.
Кроме того, осталось много недосказанной информации по коду проекта, которая не поместилась в текущую статью. Например, я не показал код фронтенда, настройку конфигов для Tauri и многое другое. Всё это ждёт вас в следующих частях.
Код приложения я решил пока не выкладывать, так как ещё не показал его в статье. Я выложу сам AppImage, но запуск бэкенда внутри через sh-скрипт у вас может не сработать, как это произошло у Ивана под Arch!
Если у вас есть мысли о том, как можно улучшить проект, пишите в комментариях — с удовольствием ознакомлюсь с вашими предложениями!
Читайте продолжение — не пропустите!
Дорогие читатели — поздравляю всех с наступившим Новым 2026 годом и Рождеством! Желаю каждому, чтобы в 2026-м исполнились самые заветные мечты, а также чтобы год принёс рост, интересные задачи и успехи в IT-карьере.
Обсудили, почему Kivy не подошёл для моего проекта;
Проект был переведён на новый стек технологий;
Собрано приложение для Linux в формате AppImage.
Мои статьи Arduinum628 [25] на Код на салфетке [10];
Собранный Kawai-Focus_0.1.0_amd64.AppImage [26].
Автор: Arduinum
Источник [27]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/24113
URLs in this post:
[1] Вайбкодинг с нейросетью 1: проверяю сборку Flutter-приложения в AppImage: https://pressanybutton.ru/post/eksperimenty-s-ii/vajbkoding-s-nejrosetyu-1-proveryayu-sborku-flutter-prilozheniya-v-appimage/
[2] «Властелин алгоритмов: сезон „ИИ в разработке“ на Хабре»: https://habr.com/ru/specials/969726/
[3] опыт: http://www.braintools.ru/article/6952
[4] Kawai-Focus: https://github.com/Arduinum/kawai-focus
[5] KivyMD 2.0.0: https://github.com/kivymd/KivyMD
[6] конфликт: http://www.braintools.ru/article/7708
[7] внимания: http://www.braintools.ru/article/7595
[8] логика: http://www.braintools.ru/article/7640
[9] памяти: http://www.braintools.ru/article/4140
[10] «Код на салфетке»: https://pressanybutton.ru/
[11] «ИИ бот-модератор 1: Начало проекта»: https://pressanybutton.ru/post/telegram-bot-na-aiogram3/ii-bot-moderator-1-nachalo-proekta/#ustanovka-uv
[12] git@github.com: mailto:git@github.com
[13] Установка uv: https://docs.astral.sh/uv/getting-started/installation/
[14] README.md: http://README.md
[15] ошибки: http://www.braintools.ru/article/4192
[16] models.py: http://models.py
[17] mixins.py: http://mixins.py
[18] settings.py: http://settings.py
[19] main.py: http://main.py
[20] http://127.0.0.1:8090: http://127.0.0.1:8090
[21] https://deb.nodesource.com/setup_24.x: https://deb.nodesource.com/setup%5C_24.x
[22] http://localhost:8100/: http://localhost:8100/
[23] https://rsproxy.cn/rustup-init.sh: https://rsproxy.cn/rustup-init.sh
[24] интерес: http://www.braintools.ru/article/4220
[25] Мои статьи Arduinum628: https://pressanybutton.ru/user/Arduinum628/
[26] Kawai-Focus_0.1.0_amd64.AppImage: https://disk.yandex.ru/d/dYI3b7_0_kTMdQ
[27] Источник: https://habr.com/ru/articles/983652/?utm_source=habrahabr&utm_medium=rss&utm_campaign=983652
Нажмите здесь для печати.