Coverage for src / ezqt_app / widgets / core / ez_app.py: 48.78%

66 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-03-26 07:07 +0000

1# /////////////////////////////////////////////////////////////// 

2# WIDGETS.CORE.EZ_APP - Extended QApplication 

3# Project: ezqt_app 

4# /////////////////////////////////////////////////////////////// 

5 

6"""EzApplication — QApplication subclass with theme and UTF-8 support.""" 

7 

8from __future__ import annotations 

9 

10# /////////////////////////////////////////////////////////////// 

11# IMPORTS 

12# /////////////////////////////////////////////////////////////// 

13# Standard library imports 

14import contextlib 

15import locale 

16import os 

17import time 

18from typing import Any 

19 

20# Third-party imports 

21from PySide6.QtCore import Qt, Signal 

22from PySide6.QtGui import QGuiApplication 

23from PySide6.QtWidgets import QApplication 

24 

25# Local imports 

26from ...utils.diagnostics import warn_tech 

27from ...utils.qt_runtime import configure_qt_high_dpi 

28 

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 ) 

38 

39 

40# /////////////////////////////////////////////////////////////// 

41# CLASSES 

42# /////////////////////////////////////////////////////////////// 

43class EzApplication(QApplication): 

44 """ 

45 Extended main application with theme and UTF-8 encoding support. 

46 

47 This class inherits from QApplication and adds functionality 

48 for theme management and UTF-8 encoding. 

49 """ 

50 

51 themeChanged = Signal() 

52 

53 def __init__(self, *args: Any, **kwargs: Any) -> None: 

54 """ 

55 Initialize the application with UTF-8 and high resolution support. 

56 

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: 65 ↛ 76line 65 didn't jump to line 76 because the condition on line 65 was always true

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 ) 

75 

76 existing_app = QApplication.instance() 

77 if existing_app and not isinstance(existing_app, EzApplication): 77 ↛ 78line 77 didn't jump to line 78 because the condition on line 77 was never true

78 raise RuntimeError( 

79 "Please destroy the QApplication singleton before creating a new EzApplication instance." 

80 ) 

81 

82 super().__init__(*args, **kwargs) 

83 

84 with contextlib.suppress(locale.Error): 

85 locale.setlocale(locale.LC_ALL, "") 

86 

87 os.environ["PYTHONIOENCODING"] = "utf-8" 

88 os.environ["QT_FONT_DPI"] = "96" 

89 

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 ) 

108 

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): 111 ↛ 113line 111 didn't jump to line 113 because the condition on line 111 was always true

112 return existing_app 

113 existing_app.quit() 

114 existing_app.deleteLater() 

115 time.sleep(0.2) 

116 

117 if nested_app := QApplication.instance(): 

118 nested_app.quit() 

119 nested_app.deleteLater() 

120 time.sleep(0.2) 

121 

122 try: 

123 instance = cls(*args, **kwargs) 

124 except RuntimeError as e: 

125 if "QApplication singleton" in str(e): 

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 

134 

135 with contextlib.suppress(locale.Error): 

136 locale.setlocale(locale.LC_ALL, "") 

137 

138 os.environ["PYTHONIOENCODING"] = "utf-8" 

139 os.environ["QT_FONT_DPI"] = "96" 

140 

141 return instance 

142 

143 

144# /////////////////////////////////////////////////////////////// 

145# PUBLIC API 

146# /////////////////////////////////////////////////////////////// 

147__all__ = ["EzApplication"]