Coverage for src / ezqt_app / widgets / core / ez_app.py: 70.73%
66 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-06 13:12 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-06 13:12 +0000
1# ///////////////////////////////////////////////////////////////
2# WIDGETS.CORE.EZ_APP - Extended QApplication
3# Project: ezqt_app
4# ///////////////////////////////////////////////////////////////
6"""EzApplication — QApplication subclass with theme and UTF-8 support."""
8from __future__ import annotations
10# ///////////////////////////////////////////////////////////////
11# IMPORTS
12# ///////////////////////////////////////////////////////////////
13# Standard library imports
14import contextlib
15import locale
16import os
17import time
18from typing import Any
20# Third-party imports
21from PySide6.QtCore import Qt, Signal
22from PySide6.QtGui import QGuiApplication
23from PySide6.QtWidgets import QApplication
25# Local imports
26from ...utils.diagnostics import warn_tech
27from ...utils.qt_runtime import configure_qt_high_dpi
29# Configure High DPI before any QApplication instance is created
30try:
31 configure_qt_high_dpi()
32except (ImportError, RuntimeError) as e:
33 warn_tech(
34 code="widgets.ez_app.high_dpi_early_config_failed",
35 message="Could not configure High DPI early",
36 error=e,
37 )
40# ///////////////////////////////////////////////////////////////
41# CLASSES
42# ///////////////////////////////////////////////////////////////
43class EzApplication(QApplication):
44 """
45 Extended main application with theme and UTF-8 encoding support.
47 This class inherits from QApplication and adds functionality
48 for theme management and UTF-8 encoding.
49 """
51 themeChanged = Signal()
53 def __init__(self, *args: Any, **kwargs: Any) -> None:
54 """
55 Initialize the application with UTF-8 and high resolution support.
57 Parameters
58 ----------
59 *args : Any
60 Positional arguments passed to QApplication.
61 **kwargs : Any
62 Keyword arguments passed to QApplication.
63 """
64 try:
65 if QGuiApplication.instance() is None:
66 QGuiApplication.setHighDpiScaleFactorRoundingPolicy(
67 Qt.HighDpiScaleFactorRoundingPolicy.PassThrough
68 )
69 except (ImportError, RuntimeError) as e:
70 warn_tech(
71 code="widgets.ez_app.high_dpi_init_config_failed",
72 message="Could not configure High DPI in EzApplication.__init__",
73 error=e,
74 )
76 existing_app = QApplication.instance()
77 if existing_app and not isinstance(existing_app, EzApplication):
78 raise RuntimeError(
79 "Please destroy the QApplication singleton before creating a new EzApplication instance."
80 )
82 super().__init__(*args, **kwargs)
84 with contextlib.suppress(locale.Error):
85 locale.setlocale(locale.LC_ALL, "")
87 os.environ["PYTHONIOENCODING"] = "utf-8"
88 os.environ["QT_FONT_DPI"] = "96"
90 @classmethod
91 def create_for_testing(cls, *args: Any, **kwargs: Any) -> EzApplication:
92 """
93 Create an EzApplication instance for testing, bypassing singleton checks.
94 """
95 try:
96 if QGuiApplication.instance() is None: 96 ↛ 97line 96 didn't jump to line 97 because the condition on line 96 was never true
97 QGuiApplication.setHighDpiScaleFactorRoundingPolicy(
98 Qt.HighDpiScaleFactorRoundingPolicy.PassThrough
99 )
100 except (ImportError, RuntimeError) as e:
101 warn_tech(
102 code="widgets.ez_app.high_dpi_test_config_failed",
103 message=(
104 "Could not configure High DPI in EzApplication.create_for_testing"
105 ),
106 error=e,
107 )
109 existing_app = QApplication.instance()
110 if existing_app: 110 ↛ 122line 110 didn't jump to line 122 because the condition on line 110 was always true
111 if isinstance(existing_app, cls):
112 return existing_app
113 existing_app.quit()
114 existing_app.deleteLater()
115 time.sleep(0.2)
117 if nested_app := QApplication.instance(): 117 ↛ 122line 117 didn't jump to line 122 because the condition on line 117 was always true
118 nested_app.quit()
119 nested_app.deleteLater()
120 time.sleep(0.2)
122 try:
123 instance = cls(*args, **kwargs)
124 except RuntimeError as e:
125 if "QApplication singleton" in str(e): 125 ↛ 126line 125 didn't jump to line 126 because the condition on line 125 was never true
126 app = QApplication.instance()
127 if app:
128 app.quit()
129 app.deleteLater()
130 time.sleep(0.3)
131 instance = cls(*args, **kwargs)
132 else:
133 raise
135 with contextlib.suppress(locale.Error):
136 locale.setlocale(locale.LC_ALL, "")
138 os.environ["PYTHONIOENCODING"] = "utf-8"
139 os.environ["QT_FONT_DPI"] = "96"
141 return instance
144# ///////////////////////////////////////////////////////////////
145# PUBLIC API
146# ///////////////////////////////////////////////////////////////
147__all__ = ["EzApplication"]