Введение
В заключительной статье цикла PySide6 (и всего плана статей в его текущем объёме) собираем итоговый проект — desktop-приложение для управления задачами, объединяющее QML-интерфейс (используя темы из QML-цикла, статьи 479-498, но реализованный через Python-инфраструктуру этого цикла) с Python-backend, включающим модель данных (статья 504), работу с БД через SQLAlchemy (статья 508), и фоновую обработку через QThread (статья 505).
Концепция
Архитектура итогового проекта: TaskListModel (статья 504) служит мостом между SQLAlchemy-сессией (статья 508), хранящей задачи в реальной, persistent БД, и QML-интерфейсом (статья 503), отображающим список задач через ListView; фоновая синхронизация с гипотетическим внешним сервисом (имитирующая сетевую операцию) выполняется через QThread (статья 505), не блокируя основной поток и, соответственно, отзывчивость QML-интерфейса; всё приложение упаковывается для распространения через PyInstaller (статья 506).
Пример кода: Итоговый мини-проект
# main.py — точка входа, объединяющая все компоненты итогового проекта
import sys
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import TaskListModel, Base
from sync_worker import SyncWorker
def main():
app = QGuiApplication(sys.argv)
engine_qml = QQmlApplicationEngine()
db_engine = create_engine("sqlite:///tasks.db")
Base.metadata.create_all(db_engine) # создание таблиц при первом запуске, если их ещё нет
Session = sessionmaker(bind=db_engine)
session = Session()
task_model = TaskListModel(session)
engine_qml.rootContext().setContextProperty("taskListModel", task_model)
sync_worker = SyncWorker()
sync_worker.syncCompleted.connect(task_model.refresh) # обновление модели после фоновой синхронизации
sync_worker.start()
engine_qml.load("qml/main.qml")
if not engine_qml.rootObjects():
sys.exit(-1)
sys.exit(app.exec())
if __name__ == "__main__":
main()
# sync_worker.py — фоновая операция, не блокирующая основной поток (статья 505)
from PySide6.QtCore import QThread, Signal
import time
class SyncWorker(QThread):
syncCompleted = Signal()
def run(self):
while True:
time.sleep(30) # периодическая синхронизация каждые 30 секунд
# ... здесь была бы реальная сетевая синхронизация с внешним сервисом ...
self.syncCompleted.emit()
// qml/main.qml — интерфейс, использующий модель из Python (объединение паттернов QML-цикла)
import QtQuick
import QtQuick.Controls
ApplicationWindow {
visible: true
width: 400; height: 600
title: qsTr("Менеджер задач (PySide6)")
ListView {
anchors.fill: parent
model: taskListModel
delegate: ItemDelegate {
width: ListView.view.width
text: title
onClicked: taskListModel.toggleCompleted(index)
}
}
}
Пояснения к коду
main() показывает явную, последовательную сборку всего приложения — создание движка QML, инициализация БД через SQLAlchemy с автоматическим созданием схемы при первом запуске (Base.metadata.create_all), создание и передача модели в QML-контекст, и запуск фонового SyncWorker, чей сигнал syncCompleted подключается прямо к методу refresh() модели — это короткое соединение демонстрирует, как фоновая работа (статья 505) естественно интегрируется с моделью данных (статья 504) через стандартный механизм сигналов/слотов (статья 501), без необходимости в более сложной, явной синхронизации. QML-файл показывает, что использование Python-модели из QML синтаксически полностью идентично его C++ эквиваленту из QML-цикла (статья 489) — model: taskListModel и taskListModel.toggleCompleted(index) не отличаются от того, как выглядел бы тот же код, если бы модель была реализована на C++, что наглядно подтверждает практическую взаимозаменяемость двух языков для этой части архитектуры.
Подводные камни
- Выполнение операций с БД (через SQLAlchemy-сессию) непосредственно внутри
SyncWorker.run(), выполняемого в фоновом потоке, без учёта потокобезопасности самой сессии (статья 508) — если синхронизация с внешним сервисом в реальной реализации потребовала бы также записи результатов синхронизации непосредственно в БД из фонового потока, нужна отдельная, специально созданная для этого потока SQLAlchemy-сессия, а не совместное использование той же сессии, что и основной поток приложения, использующий её для обслуживанияTaskListModel. - Бесконечный цикл с
time.sleep(30)внутриrun()без предусмотренного, явного способа корректно остановить этот поток при закрытии приложения — без явного механизма остановки (флага, проверяемого в цикле, или использования прерываемого ожидания вместо простогоtime.sleep) поток может продолжать попытки выполнения уже после того, как остальная часть приложения должна была завершиться, потенциально препятствуя корректному, своевременному закрытию всего процесса приложения. - Отсутствие обработки ошибок при инициализации БД или при сбое фоновой синхронизации, что может привести к необработанному исключению, аварийно завершающему либо весь процесс приложения (если ошибка происходит в основном потоке при инициализации), либо тихо «теряющему» фоновый поток без какого-либо уведомления пользователя о проблеме (если ошибка происходит внутри
run()фоновогоQThread, исключения в котором не всегда поднимаются столь же явно, как в основном потоке) — production-готовый код должен явно перехватывать и обрабатывать вероятные категории ошибок в обоих случаях. - Игнорирование необходимости тщательного тестирования (статья 509) и упаковки (статья 506) именно полной, интегрированной комбинации QML-интерфейса, Python-backend, и SQLAlchemy перед распространением конечным пользователям — каждый из этих компонентов по отдельности может работать корректно при изолированном тестировании, но их совместная интеграция (особенно после упаковки через PyInstaller, который должен правильно обнаружить и включить зависимости и для PySide6, и для SQLAlchemy, и для самих QML-файлов как ресурсов) требует отдельной, целостной проверки именно итогового, полностью собранного и упакованного приложения, а не предположения об автоматической корректности этой комбинации на основании успешности отдельных, изолированных частей.
Цикл «Qt на Python (PySide6)» (статьи 499-510) завершён, замыкая текущий, обширный объём плана статей. Общий план теперь включает 510 тем — от языковых основ Qt/C++ через embedded/IoT, сети, СУБД, DevOps, алгоритмы, мультимедиа, углублённый QML, и теперь полноценную альтернативную точку входа в экосистему Qt через Python, делающую весь объём знаний доступным и для разработчиков, предпочитающих этот язык.