127 lines
No EOL
3.7 KiB
Python
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() |