No description
Find a file
2025-12-23 07:28:41 +03:00
android Загрузить файлы в «android» 2025-11-27 09:01:19 +03:00
README.md Добавить README.md 2025-12-23 07:28:41 +03:00
technical_issue_android.pdf Add Android Technical Issue 2025-10-31 14:50:07 +03:00

Шпаргалка — экзамен (pyqt6 + QtSql)

Коротко, по делу. Храни в README.md и читай в метро.


1. Цель на экзамене

  • Сделать минимальное рабочее приложение: логин → ролевое окно (админ/менеджер/клиент).
  • Главное: видно, работает, объясняешь. Не идеальная архитектура — рабочий код.

2. Минимальная структура проекта (рабочая)

project/
├─ main.py # вызывает Composer.run()
├─ composer.py # composition root: создаёт QApplication, LoginWindow, (после логина) MainWindow/Screen
├─ ui/
│ ├─ base.py # BaseWindow, BaseScreen
│ ├─ windows.py # LoginWindow, MainWindow
│ └─ screens.py # LoginScreen, AdminScreen (QTabWidget / TableTab)
├─ utils/
│ ├─ ui.py # TableTab (QTableView + кнопки)
│ ├─ drivers.py # LoginDriver, AdminDriver (логика кнопок)
│ └─ dialogs.py
├─ db/
│ ├─ core.py # get_connection(), DB wrappers
│ └─ objects.py # User, Rights
└─ requirements.txt

3. Быстрая идея архитектуры (экзамен)

  • UI — только виджеты (без бизнес-логики).
  • Driver — логика виджета (обработчики кнопок), вызывает callback/сигналы.
  • Composer — создаёт окна/драйверы и подписывается на сигналы (переходы экранов).

Почему callback: не импортируем Composer в UI (избегаем циклических импортов). Передаём on_event в драйвер — драйвер вызывает его.

Пример передачи callback:

# в Composer
driver = LoginDriver(login_screen, on_success=self._on_login_success)

# в LoginDriver
self.on_success(user)  # вместо `composer.do(...)`
  1. Минимум кода для переключения экранов
# Composer._login_fabric()
self.wlogin = LoginWindow()
self.slogin = LoginScreen()
self.wlogin.setCentralWidget(self.slogin)
self.dlogin = LoginDriver(self.slogin, on_success=self._on_login_success)

def _on_login_success(self, user):
    if user.rights == Rights.ADMIN:
        self._admin_fabric()
        self.main.show()
  1. AdminScreen / TableTab — разметка

QTabWidget с вкладками: bookings, payments, rooms, guests, users, staff.

Вкладка содержит:

(опционально) область фильтра сверху (QDateEdit/QDateTimeEdit + кнопки Фильтровать / Показать все)

QTableView (центр)

кнопки Добавить, Удалить, Экспорт CSV (внизу)

TableTab должен создавать QSqlTableModel и устанавливать view.setModel(model).

  1. QSqlTableModel — основные операции
model = QSqlTableModel(db=db)
model.setTable("bookings")
model.setEditStrategy(QSqlTableModel.EditStrategy.OnManualSubmit)  # удобно для админки
model.select()
view.setModel(model)

# Добавить
model.insertRow(model.rowCount())
model.submitAll()  # если OnManualSubmit - вызвать для сохранения

# Удалить (с подтверждением)
model.removeRow(row)
model.submitAll()

# Фильтр (bookings: checkin / checkout)
model.setFilter("DATE(checkin) >= '2025-01-01' AND DATE(checkout) <= '2025-01-10'")
model.select()
  1. Частые ошибки (и их исправления)
QSqlDatabase.drivers(): QSqlDatabase requires a QCoreApplication
— вызовите QApplication([]) (или QCoreApplication) до использования QSqlDatabase.

Lambda в цикле захватывает переменную → все слоты привязаны к последнему tab.
Используйте lambda _, t=tab: handler(t) или functools.partial(handler, tab).

Опечатки: self_current → self._current. QMesssageBox → QMessageBox. StandartButton → StandardButton.

QMessageBox: QMessageBox.information(parent, title, text); QMessageBox.question(...) возвращает StandardButton.

SQL сравнения: используйте >=, <=, не =>.

Если модель не отображает изменения — вызовите model.select() или view.repaint().

Не дублировать подключение DB: создавайте QSqlDatabase.addDatabase(...) один раз, передавайте объект db в TableTab или делайте это в BaseScreen._setup_qsql().
  1. Экспорт CSV (быстро)
with open("bookings.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    headers = [model.headerData(i, Qt.Orientation.Horizontal) for i in range(model.columnCount())]
    writer.writerow(headers)
    for r in range(model.rowCount()):
        row = [model.data(model.index(r,c)) for c in range(model.columnCount())]
        writer.writerow(row)

Показываем QMessageBox.information(...) по завершении.

  1. Загрузка тестовых xlsx → DB (Windows, быстро, <10 строчек)

Вариант GUI (рекомендуемый на экзамене):

Открыть xlsx в Excel.

File -> Save As → CSV для каждого листа.

В pgAdmin открыть таблицу → Import/Export → выбрать CSV и импортировать.

Вариант psql:

\copy guests(name, surname, patronym, passport_seria, passport_number, phone) FROM 'guests.csv' DELIMITER ',' CSV HEADER;

(исполнить в psql)

  1. Если MySQL (pymysql) вместо PostgreSQL

db.core: замените psycopg2 на pymysql:

import pymysql
def get_connection():
    return pymysql.connect(host='localhost', port=3306, user='user', password='pw', db='dbname', charset='utf8mb4', cursorclass=pymysql.cursors.Cursor)

Важно: QSqlTableModel использует Qt SQL драйверы (QPSQL, QMYSQL).

Если вы хотите, чтобы QSqlTableModel работал с MySQL, нужен Qt-плагин qsqlmysql (в комплекте с Qt).

Если плагина нет, либо используйте QSqlDatabase.addDatabase("QMYSQL") (и установите/расположите плагин), либо не используйте QSqlTableModel: читайте данные через pymysql и реализуйте QAbstractTableModel вручную (сложнее).

Если вы только используете pymysql в db.core и все UI модели по-прежнему QSqlTableModel с QSqlDatabase("QPSQL"), это НЕ прокатит — нужно согласовать.

  1. Ошибка QSqlDatabase.drivers() — решение

Создайте QApplication([]) перед вызовом. Пример:

from PyQt6.QtWidgets import QApplication from PyQt6.QtSql import QSqlDatabase

app = QApplication([]) print(QSqlDatabase.drivers()) # OK

  1. Полезные сниппеты (на экзамене — запомнить)
lambda capture

for tab in tabs:
    tab.btn.clicked.connect(lambda _, t=tab: do_something(t))


submitAll

if not model.submitAll():
    print(model.lastError().text())


open DB in BaseScreen

db = QSqlDatabase.addDatabase("QPSQL")
db.setHostName(...)
db.open()
  1. Тайм-менеджмент (чек-лист перед экзаменом)

05 мин: открыть проект, убедиться, что main.py запускается.

525 мин: реализовать/проверить LoginWindow + LoginDriver + DB authenticate.

2555 мин: реализовать AdminScreen (TableTab) и подключить QSqlTableModel (bookings/payments first).

5575 мин: добавить кнопки Add/Delete/Export CSV для текущей вкладки.

7590 мин: протестировать фильтр по датам (bookings/payments).

90120 мин: мелкие фиксы, подготовка к демонстрации (порядок запуска, что сказать).

  1. Что говорить на защите (3060 сек)

"Архитектура: UI — presentation, драйверы — логика виджетов, Composer — собирает приложение и управляет переходами."

"DB: использую PostgreSQL, таблицы: guests, rooms, bookings, payments, staff, users. QSqlTableModel для отображения и редактирования."

"Вкладки: bookings/payments — фильтр по дате; остальные — интерактивный редактор и экспорт CSV."

"Безопасность: подтверждение удаления; ошибки DB показываются через диалоги."

  1. Быстрые исправления при баге

nothing happens on click → проверь: (1) слот подключён? (2) метод _setup_db не перезаписывает модель? (3) lambda closure bug?

QSqlDatabase.drivers() error → убедись, что QApplication создан.

filter не срабатывает → проверь SQL-условие и формат даты (YYYY-MM-DD).

  1. Последние советы (психология)

Если устал — 2030 мин сна/отдыха дадут больше, чем бессонная борьба.

На экзамене — работая стабильно и объясняя логику, можно получить высокий балл даже без всех «крутых» фич.

Делай минимально рабочую вещь первым, потом дополняй.

Удачи на экзамене — ты уже сделал огромную работу. Если хочешь, могу за 1015 минут сжато подготовить короткую шпаргалку именно по запуску проекта (команды, быстрые проверки).