bec-widgets 0.46.7__py3-none-any.whl → 0.47.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.
- bec_widgets/utils/__init__.py +1 -0
- bec_widgets/utils/container_utils.py +45 -0
- bec_widgets/utils/thread_checker.py +37 -0
- bec_widgets/widgets/figure/figure.py +14 -35
- {bec_widgets-0.46.7.dist-info → bec_widgets-0.47.0.dist-info}/METADATA +1 -1
- {bec_widgets-0.46.7.dist-info → bec_widgets-0.47.0.dist-info}/RECORD +9 -7
- {bec_widgets-0.46.7.dist-info → bec_widgets-0.47.0.dist-info}/LICENSE +0 -0
- {bec_widgets-0.46.7.dist-info → bec_widgets-0.47.0.dist-info}/WHEEL +0 -0
- {bec_widgets-0.46.7.dist-info → bec_widgets-0.47.0.dist-info}/top_level.txt +0 -0
bec_widgets/utils/__init__.py
CHANGED
@@ -2,6 +2,7 @@ from .bec_connector import BECConnector, ConnectionConfig
|
|
2
2
|
from .bec_dispatcher import BECDispatcher
|
3
3
|
from .bec_table import BECTable
|
4
4
|
from .colors import Colors
|
5
|
+
from .container_utils import WidgetContainerUtils
|
5
6
|
from .crosshair import Crosshair
|
6
7
|
from .entry_validator import EntryValidator
|
7
8
|
from .rpc_decorator import register_rpc_methods, rpc_public
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import itertools
|
2
|
+
from typing import Type
|
3
|
+
|
4
|
+
from qtpy.QtWidgets import QWidget
|
5
|
+
|
6
|
+
|
7
|
+
class WidgetContainerUtils:
|
8
|
+
|
9
|
+
@staticmethod
|
10
|
+
def generate_unique_widget_id(container: dict, prefix: str = "widget") -> str:
|
11
|
+
"""
|
12
|
+
Generate a unique widget ID.
|
13
|
+
Args:
|
14
|
+
container(dict): The container of widgets.
|
15
|
+
prefix(str): The prefix of the widget ID.
|
16
|
+
|
17
|
+
Returns:
|
18
|
+
widget_id(str): The unique widget ID.
|
19
|
+
"""
|
20
|
+
existing_ids = set(container.keys())
|
21
|
+
for i in itertools.count(1):
|
22
|
+
widget_id = f"{prefix}_{i}"
|
23
|
+
if widget_id not in existing_ids:
|
24
|
+
return widget_id
|
25
|
+
|
26
|
+
@staticmethod
|
27
|
+
def find_first_widget_by_class(
|
28
|
+
container: dict, widget_class: Type[QWidget], can_fail: bool = True
|
29
|
+
) -> QWidget | None:
|
30
|
+
"""
|
31
|
+
Find the first widget of a given class in the figure.
|
32
|
+
Args:
|
33
|
+
container(dict): The container of widgets.
|
34
|
+
widget_class(Type): The class of the widget to find.
|
35
|
+
can_fail(bool): If True, the method will return None if no widget is found. If False, it will raise an error.
|
36
|
+
Returns:
|
37
|
+
widget: The widget of the given class.
|
38
|
+
"""
|
39
|
+
for widget_id, widget in container.items():
|
40
|
+
if isinstance(widget, widget_class):
|
41
|
+
return widget
|
42
|
+
if can_fail:
|
43
|
+
return None
|
44
|
+
else:
|
45
|
+
raise ValueError(f"No widget of class {widget_class} found.")
|
@@ -0,0 +1,37 @@
|
|
1
|
+
import threading
|
2
|
+
|
3
|
+
|
4
|
+
class ThreadTracker:
|
5
|
+
def __init__(self, exclude_names=None):
|
6
|
+
self.exclude_names = exclude_names if exclude_names else []
|
7
|
+
self.initial_threads = self._capture_threads()
|
8
|
+
|
9
|
+
def _capture_threads(self):
|
10
|
+
return set(
|
11
|
+
th
|
12
|
+
for th in threading.enumerate()
|
13
|
+
if not any(ex_name in th.name for ex_name in self.exclude_names)
|
14
|
+
and th is not threading.main_thread()
|
15
|
+
)
|
16
|
+
|
17
|
+
def _thread_info(self, threads):
|
18
|
+
return ", \n".join(f"{th.name}(ID: {th.ident})" for th in threads)
|
19
|
+
|
20
|
+
def check_unfinished_threads(self):
|
21
|
+
current_threads = self._capture_threads()
|
22
|
+
additional_threads = current_threads - self.initial_threads
|
23
|
+
closed_threads = self.initial_threads - current_threads
|
24
|
+
if additional_threads:
|
25
|
+
raise Exception(
|
26
|
+
f"###### Initial threads ######:\n {self._thread_info(self.initial_threads)}\n"
|
27
|
+
f"###### Current threads ######:\n {self._thread_info(current_threads)}\n"
|
28
|
+
f"###### Closed threads ######:\n {self._thread_info(closed_threads)}\n"
|
29
|
+
f"###### Unfinished threads ######:\n {self._thread_info(additional_threads)}"
|
30
|
+
)
|
31
|
+
else:
|
32
|
+
print(
|
33
|
+
"All threads properly closed.\n"
|
34
|
+
f"###### Initial threads ######:\n {self._thread_info(self.initial_threads)}\n"
|
35
|
+
f"###### Current threads ######:\n {self._thread_info(current_threads)}\n"
|
36
|
+
f"###### Closed threads ######:\n {self._thread_info(closed_threads)}"
|
37
|
+
)
|
@@ -14,7 +14,7 @@ from pyqtgraph.Qt import uic
|
|
14
14
|
from qtpy.QtCore import Signal as pyqtSignal
|
15
15
|
from qtpy.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
|
16
16
|
|
17
|
-
from bec_widgets.utils import BECConnector, BECDispatcher, ConnectionConfig
|
17
|
+
from bec_widgets.utils import BECConnector, BECDispatcher, ConnectionConfig, WidgetContainerUtils
|
18
18
|
from bec_widgets.widgets.plots import (
|
19
19
|
BECImageShow,
|
20
20
|
BECMotorMap,
|
@@ -188,7 +188,7 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
|
188
188
|
config(dict): Additional configuration for the widget.
|
189
189
|
**axis_kwargs(dict): Additional axis properties to set on the widget after creation.
|
190
190
|
"""
|
191
|
-
widget_id = self.
|
191
|
+
widget_id = WidgetContainerUtils.generate_unique_widget_id(self._widgets)
|
192
192
|
waveform = self.add_widget(
|
193
193
|
widget_type="Waveform1D",
|
194
194
|
widget_id=widget_id,
|
@@ -278,7 +278,9 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
|
278
278
|
Returns:
|
279
279
|
BECWaveform: The waveform plot widget.
|
280
280
|
"""
|
281
|
-
waveform =
|
281
|
+
waveform = WidgetContainerUtils.find_first_widget_by_class(
|
282
|
+
self._widgets, BECWaveform, can_fail=True
|
283
|
+
)
|
282
284
|
if waveform is not None:
|
283
285
|
if axis_kwargs:
|
284
286
|
waveform.set(**axis_kwargs)
|
@@ -355,7 +357,9 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
|
355
357
|
Returns:
|
356
358
|
BECImageShow: The image widget.
|
357
359
|
"""
|
358
|
-
image =
|
360
|
+
image = WidgetContainerUtils.find_first_widget_by_class(
|
361
|
+
self._widgets, BECImageShow, can_fail=True
|
362
|
+
)
|
359
363
|
if image is not None:
|
360
364
|
if axis_kwargs:
|
361
365
|
image.set(**axis_kwargs)
|
@@ -410,7 +414,7 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
|
410
414
|
BECImageShow: The image widget.
|
411
415
|
"""
|
412
416
|
|
413
|
-
widget_id = self.
|
417
|
+
widget_id = WidgetContainerUtils.generate_unique_widget_id(self._widgets)
|
414
418
|
if config is None:
|
415
419
|
config = ImageConfig(
|
416
420
|
widget_class="BECImageShow",
|
@@ -457,7 +461,9 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
|
457
461
|
Returns:
|
458
462
|
BECMotorMap: The motor map widget.
|
459
463
|
"""
|
460
|
-
motor_map =
|
464
|
+
motor_map = WidgetContainerUtils.find_first_widget_by_class(
|
465
|
+
self._widgets, BECMotorMap, can_fail=True
|
466
|
+
)
|
461
467
|
if motor_map is not None:
|
462
468
|
if axis_kwargs:
|
463
469
|
motor_map.set(**axis_kwargs)
|
@@ -491,7 +497,7 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
|
491
497
|
Returns:
|
492
498
|
BECMotorMap: The motor map widget.
|
493
499
|
"""
|
494
|
-
widget_id = self.
|
500
|
+
widget_id = WidgetContainerUtils.generate_unique_widget_id(self._widgets)
|
495
501
|
if config is None:
|
496
502
|
config = MotorMapConfig(
|
497
503
|
widget_class="BECMotorMap",
|
@@ -532,7 +538,7 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
|
532
538
|
**axis_kwargs(dict): Additional axis properties to set on the widget after creation.
|
533
539
|
"""
|
534
540
|
if not widget_id:
|
535
|
-
widget_id = self.
|
541
|
+
widget_id = WidgetContainerUtils.generate_unique_widget_id(self._widgets)
|
536
542
|
if widget_id in self._widgets:
|
537
543
|
raise ValueError(f"Widget with ID '{widget_id}' already exists.")
|
538
544
|
|
@@ -610,25 +616,6 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
|
610
616
|
self.setBackground("k" if theme == "dark" else "w")
|
611
617
|
self.config.theme = theme
|
612
618
|
|
613
|
-
def _find_first_widget_by_class(
|
614
|
-
self, widget_class: Type[BECPlotBase], can_fail: bool = True
|
615
|
-
) -> BECPlotBase | None:
|
616
|
-
"""
|
617
|
-
Find the first widget of a given class in the figure.
|
618
|
-
Args:
|
619
|
-
widget_class(Type[BECPlotBase]): The class of the widget to find.
|
620
|
-
can_fail(bool): If True, the method will return None if no widget is found. If False, it will raise an error.
|
621
|
-
Returns:
|
622
|
-
BECPlotBase: The widget of the given class.
|
623
|
-
"""
|
624
|
-
for widget_id, widget in self._widgets.items():
|
625
|
-
if isinstance(widget, widget_class):
|
626
|
-
return widget
|
627
|
-
if can_fail:
|
628
|
-
return None
|
629
|
-
else:
|
630
|
-
raise ValueError(f"No widget of class {widget_class} found.")
|
631
|
-
|
632
619
|
def _remove_by_coordinates(self, row: int, col: int) -> None:
|
633
620
|
"""
|
634
621
|
Remove a widget from the figure by its coordinates.
|
@@ -695,14 +682,6 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
|
695
682
|
row += 1
|
696
683
|
return row, col
|
697
684
|
|
698
|
-
def _generate_unique_widget_id(self):
|
699
|
-
"""Generate a unique widget ID."""
|
700
|
-
existing_ids = set(self._widgets.keys())
|
701
|
-
for i in itertools.count(1):
|
702
|
-
widget_id = f"widget_{i}"
|
703
|
-
if widget_id not in existing_ids:
|
704
|
-
return widget_id
|
705
|
-
|
706
685
|
def _change_grid(self, widget_id: str, row: int, col: int):
|
707
686
|
"""
|
708
687
|
Change the grid to reflect the new position of the widget.
|
@@ -25,15 +25,17 @@ bec_widgets/examples/stream_plot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRk
|
|
25
25
|
bec_widgets/examples/stream_plot/line_plot.ui,sha256=rgNfhOXu1AcWF0P6wnOlmJKDjS-VIoduVrREvmzJQR8,4626
|
26
26
|
bec_widgets/examples/stream_plot/stream_plot.py,sha256=vHii1p9JxSyGQ_VcCjnk9SHJ41Q6Oi1GGd6swVVHLRM,12177
|
27
27
|
bec_widgets/simulations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
28
|
-
bec_widgets/utils/__init__.py,sha256
|
28
|
+
bec_widgets/utils/__init__.py,sha256=xytx86Yosjkta0PU4rHfoeO7FCPcimS15xjMPQUgIXc,403
|
29
29
|
bec_widgets/utils/bec_connector.py,sha256=QHVjgGRGEpojvZ04absLnUNiXEtZu3tvrHsW52MPWOk,4138
|
30
30
|
bec_widgets/utils/bec_dispatcher.py,sha256=sLv9CmJ3GKGDhvCXCDmuKtNRlI4w1oWxuQu_Mq2mDDY,4840
|
31
31
|
bec_widgets/utils/bec_table.py,sha256=Xy5qM343K8EvEpB4g_129b63yo1wdEvEY3wqxB_p_Iw,716
|
32
32
|
bec_widgets/utils/colors.py,sha256=JsLxzkxbw-I8GIuvnIKyiM83n0edhyMG2Fa4Ffm62ww,2392
|
33
|
+
bec_widgets/utils/container_utils.py,sha256=rL-ryupQ4-7Y8mKNup5aklXleOfXSd97uwXTa9UR90A,1492
|
33
34
|
bec_widgets/utils/crosshair.py,sha256=5gG4G6jjtp6Bd1Y5EySHP2EkmtR4mYdxLCgVtx9fokE,9406
|
34
35
|
bec_widgets/utils/ctrl_c.py,sha256=NMJlPDZcuqMUGykyhuZY5Ibed4yRI1K_uh16z2MmlXQ,1198
|
35
36
|
bec_widgets/utils/entry_validator.py,sha256=88OpJqaldMjiaEk7F9rRcwPuCOTLCzXmAlSEL-_7iXU,1296
|
36
37
|
bec_widgets/utils/rpc_decorator.py,sha256=pIvtqySQLnuS7l2Ti_UAe4WX7CRivZnsE5ZdKAihxh0,479
|
38
|
+
bec_widgets/utils/thread_checker.py,sha256=rDNuA3X6KQyA7JPb67mccTg0z8YkInynLAENQDQpbuE,1607
|
37
39
|
bec_widgets/utils/validator_delegate.py,sha256=Emj1WF6W8Ke1ruBWUfmHdVJpmOSPezuOt4zvQTay_44,442
|
38
40
|
bec_widgets/utils/widget_io.py,sha256=JKl508VnqQSxcaHqKaoBQ1TWSOm3pXhxQGx7iF_pRA0,10875
|
39
41
|
bec_widgets/utils/yaml_dialog.py,sha256=soZI8BOjlqYGfYDga70MEvkxJTsktq4y7B3uog2cSik,1851
|
@@ -43,7 +45,7 @@ bec_widgets/widgets/__init__.py,sha256=GptryTiWJ4yWZZVBG_03guISJabSOzVpOMRkgW0Ld
|
|
43
45
|
bec_widgets/widgets/editor/__init__.py,sha256=5mBdFYi_IpygCz81kbLEZUWhd1b6oqiO3nASejuV_ug,30
|
44
46
|
bec_widgets/widgets/editor/editor.py,sha256=pIIYLPqqqhXqT11Xj10cyGEiy-ieNGE4ZujN5lf0e68,15110
|
45
47
|
bec_widgets/widgets/figure/__init__.py,sha256=3hGx_KOV7QHCYAV06aNuUgKq4QIYCjUTad-DrwkUaBM,44
|
46
|
-
bec_widgets/widgets/figure/figure.py,sha256=
|
48
|
+
bec_widgets/widgets/figure/figure.py,sha256=55Dc3DwdeC4rBDz9KLF6udfQJjnDuLQ-1QJ5oOF4Quw,28559
|
47
49
|
bec_widgets/widgets/monitor/__init__.py,sha256=afXuZcBOxNAuYdCkIQXX5J60R5A3Q_86lNEW2vpFtPI,32
|
48
50
|
bec_widgets/widgets/monitor/config_dialog.py,sha256=Z1a4WRIVlfEGdwC-QG25kba2EHCZWi5J843tBVZlWiI,20275
|
49
51
|
bec_widgets/widgets/monitor/config_dialog.ui,sha256=ISMcF7CLTAMXhfZh2Yv5yezzAjMtb9fxY1pmX4B_jCg,5932
|
@@ -92,8 +94,8 @@ tests/unit_tests/test_widget_io.py,sha256=FeL3ZYSBQnRt6jxj8VGYw1cmcicRQyHKleahw7
|
|
92
94
|
tests/unit_tests/test_yaml_dialog.py,sha256=HNrqferkdg02-9ieOhhI2mr2Qvt7GrYgXmQ061YCTbg,5794
|
93
95
|
tests/unit_tests/test_msgs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
94
96
|
tests/unit_tests/test_msgs/available_scans_message.py,sha256=m_z97hIrjHXXMa2Ex-UvsPmTxOYXfjxyJaGkIY6StTY,46532
|
95
|
-
bec_widgets-0.
|
96
|
-
bec_widgets-0.
|
97
|
-
bec_widgets-0.
|
98
|
-
bec_widgets-0.
|
99
|
-
bec_widgets-0.
|
97
|
+
bec_widgets-0.47.0.dist-info/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
|
98
|
+
bec_widgets-0.47.0.dist-info/METADATA,sha256=fWRxvjg6pFXNEcNvoKCEcl9B3MSBi0eEPh61nI17B7s,3714
|
99
|
+
bec_widgets-0.47.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
100
|
+
bec_widgets-0.47.0.dist-info/top_level.txt,sha256=EXCwhJYmXmd1DjYYL3hrGsddX-97IwYSiIHrf27FFVk,18
|
101
|
+
bec_widgets-0.47.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|