pyqt6-scaffold/pyqt6_scaffold/core/composer.py
2026-03-06 16:05:24 +03:00

127 lines
No EOL
3.7 KiB
Python

# SPDX-License-Identifier: LGPL-3.0-or-later
from .objects import NavigateRequest
from .windows import BaseWindow
from .logging import setup_logger
from .database import AbstractDatabase
from PyQt6.QtWidgets import QApplication
from PyQt6.QtCore import QObject, pyqtSignal, pyqtSlot
from typing import Dict
log = setup_logger(__name__)
class Composer(QObject):
"""
Application router and window lifecycle manager.
Manages window registration, navigation between windows,
and holds the active database connection. Acts as the
central hub of the application.
Args:
app: QApplication instance.
db: Connected AbstractDatabase instance.
"""
navigate_request = pyqtSignal(NavigateRequest)
def __init__(self, app: QApplication, db: AbstractDatabase):
super().__init__()
self._db = db
self._current = None
self._app = app
self._registry: Dict[str, Dict[str, object]] = dict()
self._current_ctx: Dict[str, Dict[str, object]] = dict()
self.navigate_request.connect(self.navigate)
@property
def context(self):
"""
Return the current navigation context.
"""
return self._current_ctx
def register(self, name: str,
window: type[BaseWindow],
lazy: bool = True):
"""
Register a window class under a given name.
Args:
name: Unique string identifier for the window.
window: A subclass of BaseWindow.
lazy: If True, the window is instantiated on first navigation.
If False, the window is instantiated immediately.
Raises:
TypeError: If window is not a subclass of BaseWindow.
"""
if not issubclass(window, BaseWindow):
raise TypeError(f"{window} is not a descendant of BaseWindow")
entry = {
"class": window,
"instance": None,
"lazy": lazy
}
if not lazy:
entry["instance"] = window(self, self._db)
self._registry[name] = entry
def _switch_window(self, window):
"""
Close the current window and show the new one.
"""
if self._current:
self._current.close()
self._current = window
self._current.show()
def _create_window(self, name: str) -> BaseWindow:
"""
Instantiate or retrieve the window registered under name.
For lazy windows, creates a new instance on every call.
For eager windows, returns the existing instance.
"""
entry = self._registry[name]
if not entry["lazy"]:
return entry["instance"]
instance = entry["class"](self, self._db)
return instance
@pyqtSlot(NavigateRequest)
def navigate(self, request: NavigateRequest):
"""
Handle a NavigateRequest signal and switch to the target window.
Raises:
KeyError: If the target window is not registered.
"""
if request.target not in self._registry:
raise KeyError("Window is not registered")
self._current_ctx = request.context
window = self._create_window(request.target)
self._switch_window(window)
def run(self, start: str):
"""
Start the application from the given window.
Args:
start: Name of the window to show first.
Raises:
KeyError: If the start window is not registered.
"""
if start not in self._registry:
raise KeyError("Window is not registered")
window = self._create_window(start)
self._switch_window(window)
return self._app.exec()