# Copyright 2025–2026 European Union
# Author: Bulgheroni Antonio (antonio.bulgheroni@ec.europa.eu)
# SPDX-License-Identifier: EUPL-1.2
"""
Application state management module.
This module provides the :class:`AppState` class which manages the global application state,
including tracking whether images are available and managing the currently active window.
The state changes are propagated through Qt signals to notify interested components.
"""
from typing import TYPE_CHECKING, Any, Optional, Union
from PySide6.QtCore import QObject, Signal
if TYPE_CHECKING:
from radioviz.controllers.sub_window_controller import SubWindowController
[docs]
class AppState(QObject):
"""
Application state manager.
This class maintains the global application state including image availability
and the currently active window. It emits signals when the state changes,
allowing other parts of the application to react to these changes.
The state includes:
- Whether images are available in the application
- Which window is currently active
"""
changed = Signal()
"""Signal emitted when the application state changes."""
def __init__(self) -> None:
"""
Initialize the application state.
Creates a new instance with default values where no images are available
and no window is active.
"""
super().__init__()
self._has_images = False
self._active_window: Optional['SubWindowController[Any]'] = None
@property
def has_images(self) -> bool:
"""
Check if images are available in the application.
:return: True if images are available, False otherwise
:rtype: bool
"""
return self._has_images
@has_images.setter
def has_images(self, value: bool) -> None:
"""
Set the image availability status.
Emits the :attr:`changed` signal if the value actually changes.
:param value: The new image availability status
:type value: bool
"""
if self._has_images != value:
self._has_images = value
self.changed.emit()
[docs]
def _get_active_window(self) -> 'SubWindowController[Any] | None':
"""
Get the currently active window controller.
:return: The active window controller or None if no window is active
:rtype: Union['SubWindowController', None]
"""
return self._active_window
[docs]
def on_window_change(self, window_controller: Union['SubWindowController[Any]', None]) -> None:
"""
Handle window change events.
Updates the active window and image availability status based on the
provided window controller. Emits the :attr:`changed` signal when state changes.
:param window_controller: The new active window controller or None
:type window_controller: Union['SubWindowController', None]
"""
self.active_window = window_controller
[docs]
def _set_active_window(self, value: 'SubWindowController[Any] | None') -> None:
"""
Set the active window controller.
Manages the transition between window states and updates image availability
accordingly. Emits the :attr:`changed` signal when state changes.
:param value: The new active window controller or None
:type value: Union['SubWindowController', None]
"""
if value is None: # the active is None, so there are now windows
# was it None before as well?
if self._active_window is None:
# then do nothing
pass
else:
# it means that we closed the last window
self._active_window = value
self._has_images = False
# propagate the state change
self.changed.emit()
else:
# is the new active different from the previous
if self._active_window != value:
self._active_window = value
self._has_images = True
# propagate the state change
self.changed.emit()
if self._active_window != value:
self._active_window = value
if not self._has_images:
self._has_images = True
self.changed.emit()
active_window = property(_get_active_window, _set_active_window)