"""@file main_window.py @brief Главное окно каталогизатора: шапка, вкладки разбора и поиска. Обе вкладки работают с одной и той же папкой: смена папки на вкладке разбора сразу переключает каталог, по которому идёт поиск. """ from __future__ import annotations from pathlib import Path from PySide6.QtWidgets import ( QFrame, QHBoxLayout, QLabel, QMainWindow, QTabWidget, QVBoxLayout, QWidget, ) from .. import config, db from .catalog_tab import CatalogTab from .scan_tab import ScanTab from .theme import APP_STYLESHEET class MainWindow(QMainWindow): """@brief Окно с вкладками «Каталог и поиск» и «Разбор папки».""" def __init__(self, root: Path | None = None, parent: QWidget | None = None) -> None: super().__init__(parent) self.setWindowTitle("Каталогизатор документов") self.resize(1280, 820) self.setMinimumSize(1000, 640) self.setStyleSheet(APP_STYLESHEET) self.catalog_tab = CatalogTab() self.scan_tab = ScanTab() central = QWidget() layout = QVBoxLayout(central) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addWidget(self._build_header()) layout.addWidget(self._build_tabs(), 1) self.setCentralWidget(central) self.scan_tab.catalog_ready.connect(self._on_catalog_ready) self.scan_tab.root_changed.connect(self._on_root_changed) self.set_root(Path(root) if root else config.current_root()) # ------------------------------------------------------------------ def _build_header(self) -> QFrame: header = QFrame() header.setObjectName("header") row = QHBoxLayout(header) row.setContentsMargins(18, 12, 18, 12) titles = QVBoxLayout() title = QLabel("Каталогизатор") title.setProperty("title", True) subtitle = QLabel("Описания, тезисы и поиск по папке с документами") subtitle.setProperty("muted", True) titles.addWidget(title) titles.addWidget(subtitle) row.addLayout(titles) row.addStretch(1) self.summary = QLabel("") self.summary.setProperty("muted", True) row.addWidget(self.summary) return header def _build_tabs(self) -> QTabWidget: self.tabs = QTabWidget() self.tabs.addTab(self.catalog_tab, "Каталог и поиск") self.tabs.addTab(self.scan_tab, "Разбор папки") return self.tabs # ------------------------------------------------------------------ def set_root(self, root: Path) -> None: """@brief Переключает обе вкладки на указанную папку.""" self.scan_tab.set_root(root) self.catalog_tab.set_root(root) self._update_summary(root) def _on_root_changed(self, text: str) -> None: root = Path(text.strip('" ')) self.catalog_tab.set_root(root) self._update_summary(root) def _on_catalog_ready(self, text: str) -> None: root = Path(text) self.catalog_tab.set_root(root) self._update_summary(root) self.tabs.setCurrentWidget(self.catalog_tab) def _update_summary(self, root: Path) -> None: """@brief Показывает в шапке, что уже есть в каталоге этой папки.""" try: db_path, _ = config.paths_for(root) con = db.connect(db_path, create=False) total = con.execute("SELECT COUNT(*) c FROM docs").fetchone()["c"] scans = con.execute( "SELECT COUNT(*) c FROM docs WHERE scanned = 1").fetchone()["c"] when = db.get_meta(con, "last_scan") con.close() except Exception: # noqa: BLE001 — база может быть ещё не создана self.summary.setText("") return if not total: self.summary.setText("Каталог пуст — постройте его на вкладке «Разбор папки»") return self.summary.setText( "В каталоге %d документов, из них %d сканов · обновлён %s" % (total, scans, when or "—")) def closeEvent(self, event) -> None: # noqa: N802 — имя из Qt if self.scan_tab.busy(): self.scan_tab.stop_build() event.accept()