bec-widgets 0.86.0__py3-none-any.whl → 0.87.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- CHANGELOG.md +12 -8
- PKG-INFO +1 -1
- bec_widgets/qt_utils/error_popups.py +225 -0
- bec_widgets/utils/bec_connector.py +4 -0
- {bec_widgets-0.86.0.dist-info → bec_widgets-0.87.0.dist-info}/METADATA +1 -1
- {bec_widgets-0.86.0.dist-info → bec_widgets-0.87.0.dist-info}/RECORD +11 -9
- pyproject.toml +1 -1
- tests/unit_tests/test_error_utils.py +63 -0
- {bec_widgets-0.86.0.dist-info → bec_widgets-0.87.0.dist-info}/WHEEL +0 -0
- {bec_widgets-0.86.0.dist-info → bec_widgets-0.87.0.dist-info}/entry_points.txt +0 -0
- {bec_widgets-0.86.0.dist-info → bec_widgets-0.87.0.dist-info}/licenses/LICENSE +0 -0
CHANGELOG.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## v0.87.0 (2024-07-17)
|
4
|
+
|
5
|
+
### Feature
|
6
|
+
|
7
|
+
* feat(qt_utils): added warning utility with simple API to setup warning message ([`787f749`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/787f74949bac27aaa51cbb43911919071481707c))
|
8
|
+
|
9
|
+
* feat(qt_utils): added error handle utility with popup messageBoxes ([`196ef7a`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/196ef7afe11a1b5dcc536f8859dc3b6044ea628e))
|
10
|
+
|
11
|
+
### Unknown
|
12
|
+
|
13
|
+
* tests: add unit tests for error and warning message boxes ([`8f104cf`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/8f104cf4024d3a4516e6aba5daa8fb78c85e2bfd))
|
14
|
+
|
3
15
|
## v0.86.0 (2024-07-17)
|
4
16
|
|
5
17
|
### Feature
|
@@ -131,11 +143,3 @@
|
|
131
143
|
### Refactor
|
132
144
|
|
133
145
|
* refactor(device_input): DeviceComboBox and DeviceLineEdit moved to top layer of widgets ([`f048629`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/f04862933f049030554086adef3ec9e1aebd3eda))
|
134
|
-
|
135
|
-
* refactor(stop_button): moved to top layer, plugin added ([`f5b8375`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/f5b8375fd36e3bb681de571da86a6c0bdb3cb6f0))
|
136
|
-
|
137
|
-
* refactor(motor_map_widget): removed restriction of only PySide6 for widget ([`db1cdf4`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/db1cdf42806fef6d7c6d2db83528f32df3f9751d))
|
138
|
-
|
139
|
-
* refactor(color_button): ColorButton moved to top level of widgets ([`fa1e86f`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/fa1e86ff07b25d2c47c73117b00765b8e2f25da4))
|
140
|
-
|
141
|
-
## v0.81.2 (2024-07-07)
|
PKG-INFO
CHANGED
@@ -0,0 +1,225 @@
|
|
1
|
+
import functools
|
2
|
+
import sys
|
3
|
+
import traceback
|
4
|
+
|
5
|
+
from qtpy.QtCore import QObject, Qt, Signal, Slot
|
6
|
+
from qtpy.QtWidgets import QApplication, QMessageBox, QPushButton, QVBoxLayout, QWidget
|
7
|
+
|
8
|
+
|
9
|
+
def SafeSlot(*slot_args, **slot_kwargs):
|
10
|
+
"""Function with args, acting like a decorator, applying "error_managed" decorator + Qt Slot
|
11
|
+
to the passed function, to display errors instead of potentially raising an exception
|
12
|
+
|
13
|
+
'popup_error' keyword argument can be passed with boolean value if a dialog should pop up,
|
14
|
+
otherwise error display is left to the original exception hook
|
15
|
+
"""
|
16
|
+
popup_error = bool(slot_kwargs.pop("popup_error", False))
|
17
|
+
|
18
|
+
def error_managed(method):
|
19
|
+
@Slot(*slot_args, **slot_kwargs)
|
20
|
+
@functools.wraps(method)
|
21
|
+
def wrapper(*args, **kwargs):
|
22
|
+
try:
|
23
|
+
return method(*args, **kwargs)
|
24
|
+
except Exception:
|
25
|
+
ErrorPopupUtility().custom_exception_hook(*sys.exc_info(), popup_error=popup_error)
|
26
|
+
|
27
|
+
return wrapper
|
28
|
+
|
29
|
+
return error_managed
|
30
|
+
|
31
|
+
|
32
|
+
class WarningPopupUtility(QObject):
|
33
|
+
"""
|
34
|
+
Utility class to show warning popups in the application.
|
35
|
+
"""
|
36
|
+
|
37
|
+
def __init__(self, parent=None):
|
38
|
+
super().__init__(parent)
|
39
|
+
|
40
|
+
@Slot(str, str, str, QWidget)
|
41
|
+
def show_warning_message(self, title, message, detailed_text, widget):
|
42
|
+
msg = QMessageBox(widget)
|
43
|
+
msg.setIcon(QMessageBox.Warning)
|
44
|
+
msg.setWindowTitle(title)
|
45
|
+
msg.setText(message)
|
46
|
+
msg.setStandardButtons(QMessageBox.Ok)
|
47
|
+
msg.setDetailedText(detailed_text)
|
48
|
+
msg.exec_()
|
49
|
+
|
50
|
+
def show_warning(self, title: str, message: str, detailed_text: str, widget: QWidget = None):
|
51
|
+
"""
|
52
|
+
Show a warning message with the given title, message, and detailed text.
|
53
|
+
|
54
|
+
Args:
|
55
|
+
title (str): The title of the warning message.
|
56
|
+
message (str): The main text of the warning message.
|
57
|
+
detailed_text (str): The detailed text to show when the user expands the message.
|
58
|
+
widget (QWidget): The parent widget for the message box.
|
59
|
+
"""
|
60
|
+
self.show_warning_message(title, message, detailed_text, widget)
|
61
|
+
|
62
|
+
|
63
|
+
class ErrorPopupUtility(QObject):
|
64
|
+
"""
|
65
|
+
Utility class to manage error popups in the application to show error messages to the users.
|
66
|
+
This class is singleton and the error popup can be enabled or disabled globally or attach to widget methods with decorator @error_managed.
|
67
|
+
"""
|
68
|
+
|
69
|
+
error_occurred = Signal(str, str, QWidget)
|
70
|
+
|
71
|
+
_instance = None
|
72
|
+
_initialized = False
|
73
|
+
|
74
|
+
def __new__(cls, *args, **kwargs):
|
75
|
+
if cls._instance is None:
|
76
|
+
cls._instance = super(ErrorPopupUtility, cls).__new__(cls)
|
77
|
+
cls._instance._initialized = False
|
78
|
+
return cls._instance
|
79
|
+
|
80
|
+
def __init__(self, parent=None):
|
81
|
+
if not self._initialized:
|
82
|
+
super().__init__(parent=parent)
|
83
|
+
self.error_occurred.connect(self.show_error_message)
|
84
|
+
self.enable_error_popup = False
|
85
|
+
self._initialized = True
|
86
|
+
sys.excepthook = self.custom_exception_hook
|
87
|
+
|
88
|
+
@Slot(str, str, QWidget)
|
89
|
+
def show_error_message(self, title, message, widget):
|
90
|
+
detailed_text = self.format_traceback(message)
|
91
|
+
error_message = self.parse_error_message(detailed_text)
|
92
|
+
|
93
|
+
msg = QMessageBox(widget)
|
94
|
+
msg.setIcon(QMessageBox.Critical)
|
95
|
+
msg.setWindowTitle(title)
|
96
|
+
msg.setText(error_message)
|
97
|
+
msg.setStandardButtons(QMessageBox.Ok)
|
98
|
+
msg.setDetailedText(detailed_text)
|
99
|
+
msg.setTextInteractionFlags(Qt.TextSelectableByMouse)
|
100
|
+
msg.setMinimumWidth(600)
|
101
|
+
msg.setMinimumHeight(400)
|
102
|
+
msg.exec_()
|
103
|
+
|
104
|
+
def format_traceback(self, traceback_message: str) -> str:
|
105
|
+
"""
|
106
|
+
Format the traceback message to be displayed in the error popup by adding indentation to each line.
|
107
|
+
|
108
|
+
Args:
|
109
|
+
traceback_message(str): The traceback message to be formatted.
|
110
|
+
|
111
|
+
Returns:
|
112
|
+
str: The formatted traceback message.
|
113
|
+
"""
|
114
|
+
formatted_lines = []
|
115
|
+
lines = traceback_message.split("\n")
|
116
|
+
for line in lines:
|
117
|
+
formatted_lines.append(" " + line) # Add indentation to each line
|
118
|
+
return "\n".join(formatted_lines)
|
119
|
+
|
120
|
+
def parse_error_message(self, traceback_message):
|
121
|
+
lines = traceback_message.split("\n")
|
122
|
+
error_message = "Error occurred. See details."
|
123
|
+
capture = False
|
124
|
+
captured_message = []
|
125
|
+
|
126
|
+
for line in lines:
|
127
|
+
if "raise" in line:
|
128
|
+
capture = True
|
129
|
+
continue
|
130
|
+
if capture:
|
131
|
+
if line.strip() and not line.startswith(" File "):
|
132
|
+
captured_message.append(line.strip())
|
133
|
+
else:
|
134
|
+
break
|
135
|
+
|
136
|
+
if captured_message:
|
137
|
+
error_message = " ".join(captured_message)
|
138
|
+
return error_message
|
139
|
+
|
140
|
+
def custom_exception_hook(self, exctype, value, tb, popup_error=False):
|
141
|
+
if popup_error or self.enable_error_popup:
|
142
|
+
error_message = traceback.format_exception(exctype, value, tb)
|
143
|
+
self.error_occurred.emit(
|
144
|
+
"Method error" if popup_error else "Application Error",
|
145
|
+
"".join(error_message),
|
146
|
+
self.parent(),
|
147
|
+
)
|
148
|
+
else:
|
149
|
+
sys.__excepthook__(exctype, value, tb) # Call the original excepthook
|
150
|
+
|
151
|
+
def enable_global_error_popups(self, state: bool):
|
152
|
+
"""
|
153
|
+
Enable or disable global error popups for all applications.
|
154
|
+
|
155
|
+
Args:
|
156
|
+
state(bool): True to enable error popups, False to disable error popups.
|
157
|
+
"""
|
158
|
+
self.enable_error_popup = bool(state)
|
159
|
+
|
160
|
+
@classmethod
|
161
|
+
def reset_singleton(cls):
|
162
|
+
"""
|
163
|
+
Reset the singleton instance.
|
164
|
+
"""
|
165
|
+
cls._instance = None
|
166
|
+
cls._initialized = False
|
167
|
+
|
168
|
+
|
169
|
+
class ExampleWidget(QWidget): # pragma: no cover
|
170
|
+
"""
|
171
|
+
Example widget to demonstrate error handling with the ErrorPopupUtility.
|
172
|
+
|
173
|
+
Warnings -> This example works properly only with PySide6, PyQt6 has a bug with the error handling.
|
174
|
+
"""
|
175
|
+
|
176
|
+
def __init__(self, parent=None):
|
177
|
+
super().__init__(parent=parent)
|
178
|
+
self.init_ui()
|
179
|
+
self.warning_utility = WarningPopupUtility(self)
|
180
|
+
|
181
|
+
def init_ui(self):
|
182
|
+
self.layout = QVBoxLayout(self)
|
183
|
+
|
184
|
+
# Button to trigger method with error handling
|
185
|
+
self.error_button = QPushButton("Trigger Handled Error", self)
|
186
|
+
self.error_button.clicked.connect(self.method_with_error_handling)
|
187
|
+
self.layout.addWidget(self.error_button)
|
188
|
+
|
189
|
+
# Button to trigger method without error handling
|
190
|
+
self.normal_button = QPushButton("Trigger Normal Error", self)
|
191
|
+
self.normal_button.clicked.connect(self.method_without_error_handling)
|
192
|
+
self.layout.addWidget(self.normal_button)
|
193
|
+
|
194
|
+
# Button to trigger warning popup
|
195
|
+
self.warning_button = QPushButton("Trigger Warning", self)
|
196
|
+
self.warning_button.clicked.connect(self.trigger_warning)
|
197
|
+
self.layout.addWidget(self.warning_button)
|
198
|
+
|
199
|
+
@SafeSlot(popup_error=True)
|
200
|
+
def method_with_error_handling(self):
|
201
|
+
"""This method raises an error and the exception is handled by the decorator."""
|
202
|
+
raise ValueError("This is a handled error.")
|
203
|
+
|
204
|
+
@SafeSlot()
|
205
|
+
def method_without_error_handling(self):
|
206
|
+
"""This method raises an error and the exception is not handled here."""
|
207
|
+
raise ValueError("This is an unhandled error.")
|
208
|
+
|
209
|
+
@SafeSlot()
|
210
|
+
def trigger_warning(self):
|
211
|
+
"""Trigger a warning using the WarningPopupUtility."""
|
212
|
+
self.warning_utility.show_warning(
|
213
|
+
title="Warning",
|
214
|
+
message="This is a warning message.",
|
215
|
+
detailed_text="This is the detailed text of the warning message.",
|
216
|
+
widget=self,
|
217
|
+
)
|
218
|
+
|
219
|
+
|
220
|
+
if __name__ == "__main__": # pragma: no cover
|
221
|
+
|
222
|
+
app = QApplication(sys.argv)
|
223
|
+
widget = ExampleWidget()
|
224
|
+
widget.show()
|
225
|
+
sys.exit(app.exec_())
|
@@ -13,6 +13,7 @@ from qtpy.QtCore import QObject, QRunnable, QThreadPool, Signal
|
|
13
13
|
from qtpy.QtCore import Slot as pyqtSlot
|
14
14
|
|
15
15
|
from bec_widgets.cli.rpc_register import RPCRegister
|
16
|
+
from bec_widgets.qt_utils.error_popups import ErrorPopupUtility
|
16
17
|
from bec_widgets.utils.bec_widget import BECWidget
|
17
18
|
from bec_widgets.utils.yaml_dialog import load_yaml, load_yaml_gui, save_yaml, save_yaml_gui
|
18
19
|
|
@@ -94,6 +95,9 @@ class BECConnector(BECWidget):
|
|
94
95
|
self.rpc_register = RPCRegister()
|
95
96
|
self.rpc_register.add_rpc(self)
|
96
97
|
|
98
|
+
# Error popups
|
99
|
+
self.error_utility = ErrorPopupUtility()
|
100
|
+
|
97
101
|
self._thread_pool = QThreadPool.globalInstance()
|
98
102
|
|
99
103
|
def submit_task(self, fn, *args, on_complete: pyqtSlot = None, **kwargs) -> Worker:
|
@@ -2,11 +2,11 @@
|
|
2
2
|
.gitlab-ci.yml,sha256=zvb4A6QI5lQTsdfI5nPPL-tUNfcrz__SQjxW03QZ5Ek,8204
|
3
3
|
.pylintrc,sha256=eeY8YwSI74oFfq6IYIbCqnx3Vk8ZncKaatv96n_Y8Rs,18544
|
4
4
|
.readthedocs.yaml,sha256=aSOc277LqXcsTI6lgvm_JY80lMlr69GbPKgivua2cS0,603
|
5
|
-
CHANGELOG.md,sha256=
|
5
|
+
CHANGELOG.md,sha256=J5sVQm-02kM-VZdTMEXiw7GffeaLehxOfJSeSmf2JEw,7474
|
6
6
|
LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
|
7
|
-
PKG-INFO,sha256=
|
7
|
+
PKG-INFO,sha256=zgqysKuGz67anBxMymaso7hUNfkKS04uMlwLVOL5e38,1308
|
8
8
|
README.md,sha256=Od69x-RS85Hph0-WwWACwal4yUd67XkEn4APEfHhHFw,2649
|
9
|
-
pyproject.toml,sha256=
|
9
|
+
pyproject.toml,sha256=j-UQTLL_5Y-jv2eqR7KkbyNjyqJrnoQ6uWGwLEUzkUo,2357
|
10
10
|
.git_hooks/pre-commit,sha256=n3RofIZHJl8zfJJIUomcMyYGFi_rwq4CC19z0snz3FI,286
|
11
11
|
.gitlab/issue_templates/bug_report_template.md,sha256=gAuyEwl7XlnebBrkiJ9AqffSNOywmr8vygUFWKTuQeI,386
|
12
12
|
.gitlab/issue_templates/documentation_update_template.md,sha256=FHLdb3TS_D9aL4CYZCjyXSulbaW5mrN2CmwTaeLPbNw,860
|
@@ -34,10 +34,11 @@ bec_widgets/examples/plugin_example_pyside/tictactoe.py,sha256=s3rCurXloVcmMdzZi
|
|
34
34
|
bec_widgets/examples/plugin_example_pyside/tictactoeplugin.py,sha256=BBt3MD8oDLUMCCY3mioJa1QRR0WQdW6DuvVmK1Taovk,1734
|
35
35
|
bec_widgets/examples/plugin_example_pyside/tictactoetaskmenu.py,sha256=LNwplI6deUdKY6FOhUuWBanotxk9asF2G-6k7lFfA8Y,2301
|
36
36
|
bec_widgets/qt_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
37
|
+
bec_widgets/qt_utils/error_popups.py,sha256=WAN3Qtccy9Yww29kZ3HbLt9VyipgrIamJ6y4PhGTe3I,7983
|
37
38
|
bec_widgets/qt_utils/settings_dialog.py,sha256=rR_Zk4RGTnI4dz5OEzCc13lVpxlOKuwOf4_7wqXSbRw,3373
|
38
39
|
bec_widgets/qt_utils/toolbar.py,sha256=yzxCmZ7c00B2JD1TkUpPeQgM_-v7zuTYe38Qkw_yvrc,2430
|
39
40
|
bec_widgets/utils/__init__.py,sha256=1930ji1Jj6dVuY81Wd2kYBhHYNV-2R0bN_L4o9zBj1U,533
|
40
|
-
bec_widgets/utils/bec_connector.py,sha256=
|
41
|
+
bec_widgets/utils/bec_connector.py,sha256=5FHeoN-2cwBJvei-5CIWYSJm1p06N6ZIqbN5GZxPRhM,9741
|
41
42
|
bec_widgets/utils/bec_designer.py,sha256=ak3G8FdojUPjVBBwdPXw7tN5P2Uxr-SSoQt394jXeAA,4308
|
42
43
|
bec_widgets/utils/bec_dispatcher.py,sha256=fhI7_X0kSZCtXyR55Qn-w7BfNdk2Roc1Tyx0bx3bjoE,6195
|
43
44
|
bec_widgets/utils/bec_table.py,sha256=nA2b8ukSeUfquFMAxGrUVOqdrzMoDYD6O_4EYbOG2zk,717
|
@@ -245,6 +246,7 @@ tests/unit_tests/test_crosshair.py,sha256=3OMAJ2ZaISYXMOtkXf1rPdy94vCr8njeLi6uHb
|
|
245
246
|
tests/unit_tests/test_device_box.py,sha256=q9IVFpt1NF3TBF0Jhk-I-LRiuvvHG3FGUalw4jEYwVo,3431
|
246
247
|
tests/unit_tests/test_device_input_base.py,sha256=r1tI7BFAhpv7V7gf_n5gjusyrBFOfuCqIkdVg7YA7vY,2444
|
247
248
|
tests/unit_tests/test_device_input_widgets.py,sha256=39MtgF-Q67UWz6qapyYP4ukDEUOD81iEJ_jhATyG7dM,5889
|
249
|
+
tests/unit_tests/test_error_utils.py,sha256=LQOxz29WCGOe0qwFkaPDixjUmdnF3qeAGxD4A3t9IKg,2108
|
248
250
|
tests/unit_tests/test_generate_cli_client.py,sha256=ng-eV5iF7Dhm-6YpxYo99CMY0KgqoaZBQNkMeKULDBU,3355
|
249
251
|
tests/unit_tests/test_generate_plugin.py,sha256=9603ucZChM-pYpHadzsR94U1Zec1KZT34WedX9qzgMo,4464
|
250
252
|
tests/unit_tests/test_motor_map_widget.py,sha256=3nbINg3NYvWUrrGGMRPs8SDtePjXhoehSY_CShFGvEI,7507
|
@@ -271,8 +273,8 @@ tests/unit_tests/test_configs/config_device_no_entry.yaml,sha256=hdvue9KLc_kfNzG
|
|
271
273
|
tests/unit_tests/test_configs/config_scan.yaml,sha256=vo484BbWOjA_e-h6bTjSV9k7QaQHrlAvx-z8wtY-P4E,1915
|
272
274
|
tests/unit_tests/test_msgs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
273
275
|
tests/unit_tests/test_msgs/available_scans_message.py,sha256=m_z97hIrjHXXMa2Ex-UvsPmTxOYXfjxyJaGkIY6StTY,46532
|
274
|
-
bec_widgets-0.
|
275
|
-
bec_widgets-0.
|
276
|
-
bec_widgets-0.
|
277
|
-
bec_widgets-0.
|
278
|
-
bec_widgets-0.
|
276
|
+
bec_widgets-0.87.0.dist-info/METADATA,sha256=zgqysKuGz67anBxMymaso7hUNfkKS04uMlwLVOL5e38,1308
|
277
|
+
bec_widgets-0.87.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
278
|
+
bec_widgets-0.87.0.dist-info/entry_points.txt,sha256=3otEkCdDB9LZJuBLzG4pFLK5Di0CVybN_12IsZrQ-58,166
|
279
|
+
bec_widgets-0.87.0.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
|
280
|
+
bec_widgets-0.87.0.dist-info/RECORD,,
|
pyproject.toml
CHANGED
@@ -0,0 +1,63 @@
|
|
1
|
+
from unittest.mock import patch
|
2
|
+
|
3
|
+
import pytest
|
4
|
+
import pytestqt
|
5
|
+
from qtpy.QtWidgets import QMessageBox
|
6
|
+
|
7
|
+
from bec_widgets.qt_utils.error_popups import ErrorPopupUtility, ExampleWidget
|
8
|
+
|
9
|
+
|
10
|
+
@pytest.fixture
|
11
|
+
def widget(qtbot):
|
12
|
+
test_widget = ExampleWidget()
|
13
|
+
qtbot.addWidget(test_widget)
|
14
|
+
qtbot.waitExposed(test_widget)
|
15
|
+
yield test_widget
|
16
|
+
test_widget.close()
|
17
|
+
|
18
|
+
|
19
|
+
@patch.object(QMessageBox, "exec_", return_value=QMessageBox.Ok)
|
20
|
+
def test_show_error_message_global(mock_exec, widget, qtbot):
|
21
|
+
error_utility = ErrorPopupUtility()
|
22
|
+
error_utility.enable_global_error_popups(True)
|
23
|
+
|
24
|
+
with qtbot.waitSignal(error_utility.error_occurred, timeout=1000) as blocker:
|
25
|
+
error_utility.error_occurred.emit("Test Error", "This is a test error message.", widget)
|
26
|
+
|
27
|
+
assert mock_exec.called
|
28
|
+
assert blocker.signal_triggered
|
29
|
+
|
30
|
+
|
31
|
+
@pytest.mark.parametrize("global_pop", [False, True])
|
32
|
+
@patch.object(QMessageBox, "exec_", return_value=QMessageBox.Ok)
|
33
|
+
def test_slot_with_popup_on_error(mock_exec, widget, qtbot, global_pop):
|
34
|
+
error_utility = ErrorPopupUtility()
|
35
|
+
error_utility.enable_global_error_popups(global_pop)
|
36
|
+
|
37
|
+
with qtbot.waitSignal(error_utility.error_occurred, timeout=200) as blocker:
|
38
|
+
widget.method_with_error_handling()
|
39
|
+
|
40
|
+
assert blocker.signal_triggered
|
41
|
+
assert mock_exec.called
|
42
|
+
|
43
|
+
|
44
|
+
@pytest.mark.parametrize("global_pop", [False, True])
|
45
|
+
@patch.object(QMessageBox, "exec_", return_value=QMessageBox.Ok)
|
46
|
+
def test_slot_no_popup_by_default_on_error(mock_exec, widget, qtbot, capsys, global_pop):
|
47
|
+
error_utility = ErrorPopupUtility()
|
48
|
+
error_utility.enable_global_error_popups(global_pop)
|
49
|
+
|
50
|
+
try:
|
51
|
+
with qtbot.waitSignal(error_utility.error_occurred, timeout=200) as blocker:
|
52
|
+
widget.method_without_error_handling()
|
53
|
+
except pytestqt.exceptions.TimeoutError:
|
54
|
+
assert not global_pop
|
55
|
+
|
56
|
+
if global_pop:
|
57
|
+
assert blocker.signal_triggered
|
58
|
+
assert mock_exec.called
|
59
|
+
else:
|
60
|
+
assert not blocker.signal_triggered
|
61
|
+
assert not mock_exec.called
|
62
|
+
stdout, stderr = capsys.readouterr()
|
63
|
+
assert "ValueError" in stderr
|
File without changes
|
File without changes
|
File without changes
|