# Copyright 2025–2026 European Union
# Author: Bulgheroni Antonio (antonio.bulgheroni@ec.europa.eu)
# SPDX-License-Identifier: EUPL-1.2
"""
Module for creating and managing subwindow views in the application.
This module provides the infrastructure for registering and creating
subwindow views through a factory pattern. It defines protocols for
subwindow views and manages their lifecycle through a central registry.
"""
from __future__ import annotations
from dataclasses import asdict, dataclass
from typing import TYPE_CHECKING, Any, Dict, Optional, Protocol, Type
from radioviz.services.window_manager import WindowRequest
if TYPE_CHECKING:
from PySide6.QtWidgets import QWidget
[docs]
class SubWindowProtocol(Protocol):
"""
Minimal protocol all subwindow views must satisfy.
This protocol defines the interface that all subwindow views must implement
to ensure compatibility with the window factory system. It avoids direct
imports of Qt classes in this module.
"""
def __init__(self, controller: Any, parent: Optional[QWidget] = None, **kwargs: Any) -> None: ...
[docs]
@dataclass
class WindowSpec:
"""
Specification for window registration.
This dataclass holds the configuration needed to register a window type
with the window factory registry.
"""
window_type: str
"""The unique identifier for the window type."""
view_cls: SubWindowProtocol
"""The view class associated with this window type."""
overwrite: bool = False
"""Flag indicating whether to overwrite existing registration."""
[docs]
def to_dict(self) -> Dict[str, Any]:
"""
Convert the window specification to a dictionary.
:return: Dictionary representation of the window specification
:rtype: dict
"""
return asdict(self)
[docs]
class WindowFactoryError(RuntimeError):
"""
Exception raised for errors in the window factory system.
This exception is raised when there are issues with window registration,
retrieval, or creation operations within the window factory.
"""
[docs]
class WindowFactoryRegistry:
"""
Central registry for subwindow view types.
This class manages the registration and creation of subwindow views
through a factory pattern. It maintains a mapping between window types
and their corresponding view classes.
"""
def __init__(self) -> None:
"""
Initialize the window factory registry.
Creates an empty registry and registers default window types.
"""
self._registry: Dict[str, Type[SubWindowProtocol]] = {}
self.register_default_windows()
# -------------------------
# registration API
# -------------------------
[docs]
def register(
self,
window_type: str,
view_cls: Type[SubWindowProtocol],
*,
overwrite: bool = False,
) -> None:
"""
Register a new window type with the factory.
:param window_type: Unique identifier for the window type
:type window_type: str
:param view_cls: The view class to associate with this window type
:type view_cls: Type[SubWindowProtocol]
:param overwrite: Whether to overwrite existing registration
:type overwrite: bool
:raise WindowFactoryError: If window type already exists and overwrite is False
"""
if window_type in self._registry and not overwrite:
raise WindowFactoryError(
f"Window type '{window_type}' is already registered with {self._registry[window_type]!r}"
)
self._registry[window_type] = view_cls
[docs]
def unregister(self, window_type: str) -> None:
"""
Remove a window type from the registry.
:param window_type: The window type to remove
:type window_type: str
:raise WindowFactoryError: If window type is not registered
"""
if window_type not in self._registry:
raise WindowFactoryError(f"Window type '{window_type}' is not registered")
del self._registry[window_type]
# -------------------------
# lookup / creation
# -------------------------
[docs]
def has(self, window_type: str) -> bool:
"""
Check if a window type is registered.
:param window_type: The window type to check
:type window_type: str
:return: True if window type is registered, False otherwise
:rtype: bool
"""
return window_type in self._registry
[docs]
def get(self, window_type: str) -> Type[SubWindowProtocol]:
"""
Retrieve the view class for a given window type.
:param window_type: The window type to retrieve
:type window_type: str
:return: The view class associated with the window type
:rtype: Type[SubWindowProtocol]
:raise WindowFactoryError: If window type is not registered
"""
try:
return self._registry[window_type]
except KeyError:
raise WindowFactoryError(f"Unknown window type '{window_type}'. Registered types: {list(self._registry)}")
[docs]
def create(
self,
window_request: WindowRequest,
) -> SubWindowProtocol:
"""
Create a new instance of a window view.
:param window_request: Request containing window creation parameters
:type window_request: WindowRequest
:return: New instance of the requested window view
:rtype: SubWindowProtocol
:raise WindowFactoryError: If window type is not registered
"""
view_cls = self.get(window_request.window_type)
return view_cls(controller=window_request.controller, **window_request.init_kwargs)
# -------------------------
# diagnostics
# -------------------------
[docs]
def registered_types(self) -> tuple[str, ...]:
"""
Get all registered window types.
:return: Tuple of all registered window type identifiers
:rtype: tuple[str, ...]
"""
return tuple(self._registry.keys())
[docs]
def register_default_windows(self) -> None:
"""
Register default window types with the factory.
This method registers the basic window types that are available
by default in the application. Currently registers the 'image' and
'image-metadata' window types.
"""
from radioviz.views.image_metadata_window import ImageMetadataWindow
from radioviz.views.image_window import ImageWindow
self.register('image', ImageWindow)
self.register('image-metadata', ImageMetadataWindow)