bec-widgets 0.106.0__py3-none-any.whl → 0.108.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 +30 -26
- PKG-INFO +1 -1
- bec_widgets/cli/client.py +90 -1
- bec_widgets/cli/generate_cli.py +20 -3
- bec_widgets/cli/server.py +7 -2
- bec_widgets/utils/linear_region_selector.py +72 -0
- bec_widgets/widgets/bec_progressbar/__init__.py +0 -0
- bec_widgets/widgets/bec_progressbar/bec_progress_bar.pyproject +1 -0
- bec_widgets/widgets/bec_progressbar/bec_progress_bar_plugin.py +54 -0
- bec_widgets/widgets/bec_progressbar/bec_progressbar.py +257 -0
- bec_widgets/widgets/bec_progressbar/register_bec_progress_bar.py +15 -0
- bec_widgets/widgets/figure/plots/waveform/waveform.py +126 -8
- bec_widgets/widgets/waveform/waveform_widget.py +69 -4
- {bec_widgets-0.106.0.dist-info → bec_widgets-0.108.0.dist-info}/METADATA +1 -1
- {bec_widgets-0.106.0.dist-info → bec_widgets-0.108.0.dist-info}/RECORD +19 -13
- pyproject.toml +1 -1
- {bec_widgets-0.106.0.dist-info → bec_widgets-0.108.0.dist-info}/WHEEL +0 -0
- {bec_widgets-0.106.0.dist-info → bec_widgets-0.108.0.dist-info}/entry_points.txt +0 -0
- {bec_widgets-0.106.0.dist-info → bec_widgets-0.108.0.dist-info}/licenses/LICENSE +0 -0
CHANGELOG.md
CHANGED
@@ -1,5 +1,35 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## v0.108.0 (2024-09-06)
|
4
|
+
|
5
|
+
### Documentation
|
6
|
+
|
7
|
+
* docs(progressbar): added docs ([`7d07cea`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/7d07cea946f9c884477b01bebfb60b332ff09e0a))
|
8
|
+
|
9
|
+
### Feature
|
10
|
+
|
11
|
+
* feat(progressbar): added bec progressbar ([`f6d1d0b`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/f6d1d0bbe3ba30a3b7291cd36a1f7f8e6bd5b895))
|
12
|
+
|
13
|
+
* feat(generate_cli): added support for property and qproperty setter ([`a52182d`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/a52182dca978833bfc3fad755c596d3a2ef45c42))
|
14
|
+
|
15
|
+
## v0.107.0 (2024-09-06)
|
16
|
+
|
17
|
+
### Documentation
|
18
|
+
|
19
|
+
* docs: extend waveform docs ([`e6976dc`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/e6976dc15105209852090a00a97b7cda723142e9))
|
20
|
+
|
21
|
+
### Feature
|
22
|
+
|
23
|
+
* feat: add roi select for dap, allow automatic clear curves on plot request ([`7bdca84`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/7bdca8431496fe6562d2c28f5a6af869d1a2e654))
|
24
|
+
|
25
|
+
### Refactor
|
26
|
+
|
27
|
+
* refactor: change style to bec_accent_colors ([`bd126dd`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/bd126dddbbec3e6c448cce263433d328d577c5c0))
|
28
|
+
|
29
|
+
### Test
|
30
|
+
|
31
|
+
* test: add tests, including extension to end-2-end test ([`b1aff6d`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/b1aff6d791ff847eb2f628e66ccaa4672fdeea08))
|
32
|
+
|
3
33
|
## v0.106.0 (2024-09-05)
|
4
34
|
|
5
35
|
### Feature
|
@@ -131,29 +161,3 @@
|
|
131
161
|
* fix(positioner_box): fixed positioner box dialog; added test; closes #332 ([`0bf1cf9`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/0bf1cf9b8ab2f9171d5ff63d4e3672eb93e9a5fa))
|
132
162
|
|
133
163
|
## v0.99.14 (2024-08-30)
|
134
|
-
|
135
|
-
### Fix
|
136
|
-
|
137
|
-
* fix(color_button): signal and slot added for selecting color and for emitting color after change ([`99a98de`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/99a98de8a3b7a83d71e4b567e865ac6f5c62a754))
|
138
|
-
|
139
|
-
* fix(color_button): inheritance changed to QWidget ([`3c0e501`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/3c0e501c56227d4d98ff0ac2186ff5065bff8d7a))
|
140
|
-
|
141
|
-
## v0.99.13 (2024-08-30)
|
142
|
-
|
143
|
-
### Documentation
|
144
|
-
|
145
|
-
* docs: minor updates to the widget tutorial ([`ec9c8f2`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/ec9c8f29633364c45ebd998a5411d428c1ce488d))
|
146
|
-
|
147
|
-
* docs(widget tutorial): step by step guide added ([`b32ced8`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/b32ced85fff628a9e1303a781630cdae3865238e))
|
148
|
-
|
149
|
-
### Fix
|
150
|
-
|
151
|
-
* fix(dark mode button): fixed dark mode button state for external updates, including auto ([`a3110d9`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/a3110d98147295dcb1f9353f9aaf5461cba9232a))
|
152
|
-
|
153
|
-
## v0.99.12 (2024-08-29)
|
154
|
-
|
155
|
-
### Fix
|
156
|
-
|
157
|
-
* fix(toolbar): widget action added ([`2efd487`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/2efd48736cbe04e84533f7933c552ea8274e2162))
|
158
|
-
|
159
|
-
* fix(reset_button): reset button added ([`6ed1efc`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/6ed1efc6af193908f70aa37fb73157d2ca6a62f4))
|
PKG-INFO
CHANGED
bec_widgets/cli/client.py
CHANGED
@@ -19,6 +19,7 @@ class Widgets(str, enum.Enum):
|
|
19
19
|
BECFigure = "BECFigure"
|
20
20
|
BECImageWidget = "BECImageWidget"
|
21
21
|
BECMotorMapWidget = "BECMotorMapWidget"
|
22
|
+
BECProgressBar = "BECProgressBar"
|
22
23
|
BECQueue = "BECQueue"
|
23
24
|
BECStatusBox = "BECStatusBox"
|
24
25
|
BECWaveformWidget = "BECWaveformWidget"
|
@@ -1693,6 +1694,57 @@ class BECPlotBase(RPCBase):
|
|
1693
1694
|
"""
|
1694
1695
|
|
1695
1696
|
|
1697
|
+
class BECProgressBar(RPCBase):
|
1698
|
+
@rpc_call
|
1699
|
+
def set_value(self, value):
|
1700
|
+
"""
|
1701
|
+
Set the value of the progress bar.
|
1702
|
+
|
1703
|
+
Args:
|
1704
|
+
value (float): The value to set.
|
1705
|
+
"""
|
1706
|
+
|
1707
|
+
@rpc_call
|
1708
|
+
def set_maximum(self, maximum: float):
|
1709
|
+
"""
|
1710
|
+
Set the maximum value of the progress bar.
|
1711
|
+
|
1712
|
+
Args:
|
1713
|
+
maximum (float): The maximum value.
|
1714
|
+
"""
|
1715
|
+
|
1716
|
+
@rpc_call
|
1717
|
+
def set_minimum(self, minimum: float):
|
1718
|
+
"""
|
1719
|
+
Set the minimum value of the progress bar.
|
1720
|
+
|
1721
|
+
Args:
|
1722
|
+
minimum (float): The minimum value.
|
1723
|
+
"""
|
1724
|
+
|
1725
|
+
@property
|
1726
|
+
@rpc_call
|
1727
|
+
def label_template(self):
|
1728
|
+
"""
|
1729
|
+
The template for the center label. Use $value, $maximum, and $percentage to insert the values.
|
1730
|
+
|
1731
|
+
Examples:
|
1732
|
+
>>> progressbar.label_template = "$value / $maximum - $percentage %"
|
1733
|
+
>>> progressbar.label_template = "$value / $percentage %"
|
1734
|
+
"""
|
1735
|
+
|
1736
|
+
@label_template.setter
|
1737
|
+
@rpc_call
|
1738
|
+
def label_template(self):
|
1739
|
+
"""
|
1740
|
+
The template for the center label. Use $value, $maximum, and $percentage to insert the values.
|
1741
|
+
|
1742
|
+
Examples:
|
1743
|
+
>>> progressbar.label_template = "$value / $maximum - $percentage %"
|
1744
|
+
>>> progressbar.label_template = "$value / $percentage %"
|
1745
|
+
"""
|
1746
|
+
|
1747
|
+
|
1696
1748
|
class BECQueue(RPCBase):
|
1697
1749
|
@property
|
1698
1750
|
@rpc_call
|
@@ -1887,7 +1939,7 @@ class BECWaveform(RPCBase):
|
|
1887
1939
|
"""
|
1888
1940
|
|
1889
1941
|
@rpc_call
|
1890
|
-
def get_all_data(self, output: "Literal['dict', 'pandas']" = "dict") -> "dict
|
1942
|
+
def get_all_data(self, output: "Literal['dict', 'pandas']" = "dict") -> "dict":
|
1891
1943
|
"""
|
1892
1944
|
Extract all curve data into a dictionary or a pandas DataFrame.
|
1893
1945
|
|
@@ -2050,6 +2102,25 @@ class BECWaveform(RPCBase):
|
|
2050
2102
|
size(int): Font size of the legend.
|
2051
2103
|
"""
|
2052
2104
|
|
2105
|
+
@rpc_call
|
2106
|
+
def toggle_roi(self, toggled: "bool") -> "None":
|
2107
|
+
"""
|
2108
|
+
Toggle the linear region selector on the plot.
|
2109
|
+
|
2110
|
+
Args:
|
2111
|
+
toggled(bool): If True, enable the linear region selector.
|
2112
|
+
"""
|
2113
|
+
|
2114
|
+
@rpc_call
|
2115
|
+
def select_roi(self, region: "tuple[float, float]"):
|
2116
|
+
"""
|
2117
|
+
Set the fit region of the plot widget. At the moment only a single region is supported.
|
2118
|
+
To remove the roi region again, use toggle_roi_region
|
2119
|
+
|
2120
|
+
Args:
|
2121
|
+
region(tuple[float, float]): The fit region.
|
2122
|
+
"""
|
2123
|
+
|
2053
2124
|
|
2054
2125
|
class BECWaveformWidget(RPCBase):
|
2055
2126
|
@property
|
@@ -2321,6 +2392,24 @@ class BECWaveformWidget(RPCBase):
|
|
2321
2392
|
Export the plot widget to Matplotlib.
|
2322
2393
|
"""
|
2323
2394
|
|
2395
|
+
@rpc_call
|
2396
|
+
def toggle_roi(self, checked: "bool"):
|
2397
|
+
"""
|
2398
|
+
Toggle the linear region selector.
|
2399
|
+
|
2400
|
+
Args:
|
2401
|
+
checked(bool): If True, enable the linear region selector.
|
2402
|
+
"""
|
2403
|
+
|
2404
|
+
@rpc_call
|
2405
|
+
def select_roi(self, region: "tuple"):
|
2406
|
+
"""
|
2407
|
+
Set the region of interest of the plot widget.
|
2408
|
+
|
2409
|
+
Args:
|
2410
|
+
region(tuple): Region of interest.
|
2411
|
+
"""
|
2412
|
+
|
2324
2413
|
|
2325
2414
|
class DapComboBox(RPCBase):
|
2326
2415
|
@rpc_call
|
bec_widgets/cli/generate_cli.py
CHANGED
@@ -8,6 +8,7 @@ import sys
|
|
8
8
|
|
9
9
|
import black
|
10
10
|
import isort
|
11
|
+
from qtpy.QtCore import Property as QtProperty
|
11
12
|
|
12
13
|
from bec_widgets.utils.generate_designer_plugin import DesignerPluginGenerator
|
13
14
|
from bec_widgets.utils.plugin_utils import BECClassContainer, get_rpc_classes
|
@@ -90,11 +91,27 @@ class {class_name}(RPCBase):"""
|
|
90
91
|
self.content += """...
|
91
92
|
"""
|
92
93
|
for method in cls.USER_ACCESS:
|
93
|
-
|
94
|
-
|
95
|
-
|
94
|
+
is_property_setter = False
|
95
|
+
obj = getattr(cls, method, None)
|
96
|
+
if obj is None:
|
97
|
+
obj = getattr(cls, method.split(".setter")[0], None)
|
98
|
+
is_property_setter = True
|
99
|
+
method = method.split(".setter")[0]
|
100
|
+
if obj is None:
|
101
|
+
raise AttributeError(
|
102
|
+
f"Method {method} not found in class {cls.__name__}. Please check the USER_ACCESS list."
|
103
|
+
)
|
104
|
+
if isinstance(obj, (property, QtProperty)):
|
105
|
+
# for the cli, we can map qt properties to regular properties
|
106
|
+
if is_property_setter:
|
107
|
+
self.content += f"""
|
108
|
+
@{method}.setter
|
109
|
+
@rpc_call"""
|
110
|
+
else:
|
111
|
+
self.content += """
|
96
112
|
@property
|
97
113
|
@rpc_call"""
|
114
|
+
|
98
115
|
sig = str(inspect.signature(obj.fget))
|
99
116
|
doc = inspect.getdoc(obj.fget)
|
100
117
|
else:
|
bec_widgets/cli/server.py
CHANGED
@@ -86,10 +86,15 @@ class BECWidgetsCLIServer:
|
|
86
86
|
return obj
|
87
87
|
|
88
88
|
def run_rpc(self, obj, method, args, kwargs):
|
89
|
+
logger.debug(f"Running RPC instruction: {method} with args: {args}, kwargs: {kwargs}")
|
89
90
|
method_obj = getattr(obj, method)
|
90
91
|
# check if the method accepts args and kwargs
|
91
92
|
if not callable(method_obj):
|
92
|
-
|
93
|
+
if not args:
|
94
|
+
res = method_obj
|
95
|
+
else:
|
96
|
+
setattr(obj, method, args[0])
|
97
|
+
res = None
|
93
98
|
else:
|
94
99
|
sig = inspect.signature(method_obj)
|
95
100
|
if sig.parameters:
|
@@ -245,5 +250,5 @@ def main():
|
|
245
250
|
|
246
251
|
|
247
252
|
if __name__ == "__main__": # pragma: no cover
|
248
|
-
sys.argv = ["bec_widgets.cli.server", "--id", "
|
253
|
+
sys.argv = ["bec_widgets.cli.server", "--id", "e2860", "--gui_class", "BECDockArea"]
|
249
254
|
main()
|
@@ -0,0 +1,72 @@
|
|
1
|
+
""" Module for a thin wrapper (LinearRegionWrapper) around the LinearRegionItem in pyqtgraph.
|
2
|
+
The class is mainly designed for usage with the BECWaveform and 1D plots. """
|
3
|
+
|
4
|
+
import pyqtgraph as pg
|
5
|
+
from qtpy.QtCore import QObject, Signal, Slot
|
6
|
+
from qtpy.QtGui import QColor
|
7
|
+
|
8
|
+
|
9
|
+
class LinearRegionWrapper(QObject):
|
10
|
+
"""Wrapper class for the LinearRegionItem in pyqtgraph for 1D plots (BECWaveform)
|
11
|
+
|
12
|
+
Args:
|
13
|
+
plot_item (pg.PlotItem): The plot item to add the region selector to.
|
14
|
+
parent (QObject): The parent object.
|
15
|
+
color (QColor): The color of the region selector.
|
16
|
+
hover_color (QColor): The color of the region selector when the mouse is over it.
|
17
|
+
"""
|
18
|
+
|
19
|
+
# Signal with the region tuble (start, end)
|
20
|
+
region_changed = Signal(tuple)
|
21
|
+
|
22
|
+
def __init__(
|
23
|
+
self, plot_item: pg.PlotItem, color: QColor = None, hover_color: QColor = None, parent=None
|
24
|
+
):
|
25
|
+
super().__init__(parent)
|
26
|
+
self._edge_width = 2
|
27
|
+
self.plot_item = plot_item
|
28
|
+
self.linear_region_selector = pg.LinearRegionItem()
|
29
|
+
self.proxy = None
|
30
|
+
self.change_roi_color((color, hover_color))
|
31
|
+
|
32
|
+
# Slot for changing the color of the region selector (edge and fill)
|
33
|
+
@Slot(tuple)
|
34
|
+
def change_roi_color(self, colors: tuple[QColor | str | tuple, QColor | str | tuple]):
|
35
|
+
"""Change the color and hover color of the region selector.
|
36
|
+
Hover color means the color when the mouse is over the region.
|
37
|
+
|
38
|
+
Args:
|
39
|
+
colors (tuple): Tuple with the color and hover color
|
40
|
+
"""
|
41
|
+
color, hover_color = colors
|
42
|
+
if color is not None:
|
43
|
+
self.linear_region_selector.setBrush(pg.mkBrush(color))
|
44
|
+
if hover_color is not None:
|
45
|
+
self.linear_region_selector.setHoverBrush(pg.mkBrush(hover_color))
|
46
|
+
|
47
|
+
@Slot()
|
48
|
+
def add_region_selector(self):
|
49
|
+
"""Add the region selector to the plot item"""
|
50
|
+
self.plot_item.addItem(self.linear_region_selector)
|
51
|
+
# Use proxy to limit the update rate of the region change signal to 10Hz
|
52
|
+
self.proxy = pg.SignalProxy(
|
53
|
+
self.linear_region_selector.sigRegionChanged,
|
54
|
+
rateLimit=10,
|
55
|
+
slot=self._region_change_proxy,
|
56
|
+
)
|
57
|
+
|
58
|
+
@Slot()
|
59
|
+
def remove_region_selector(self):
|
60
|
+
"""Remove the region selector from the plot item"""
|
61
|
+
self.proxy.disconnect()
|
62
|
+
self.proxy = None
|
63
|
+
self.plot_item.removeItem(self.linear_region_selector)
|
64
|
+
|
65
|
+
def _region_change_proxy(self):
|
66
|
+
"""Emit the region change signal"""
|
67
|
+
region = self.linear_region_selector.getRegion()
|
68
|
+
self.region_changed.emit(region)
|
69
|
+
|
70
|
+
def cleanup(self):
|
71
|
+
"""Cleanup the widget."""
|
72
|
+
self.remove_region_selector()
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
{'files': ['bec_progressbar.py']}
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# Copyright (C) 2022 The Qt Company Ltd.
|
2
|
+
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
3
|
+
|
4
|
+
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
5
|
+
|
6
|
+
from bec_widgets.utils.bec_designer import designer_material_icon
|
7
|
+
from bec_widgets.widgets.bec_progressbar.bec_progressbar import BECProgressBar
|
8
|
+
|
9
|
+
DOM_XML = """
|
10
|
+
<ui language='c++'>
|
11
|
+
<widget class='BECProgressBar' name='bec_progress_bar'>
|
12
|
+
</widget>
|
13
|
+
</ui>
|
14
|
+
"""
|
15
|
+
|
16
|
+
|
17
|
+
class BECProgressBarPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
18
|
+
def __init__(self):
|
19
|
+
super().__init__()
|
20
|
+
self._form_editor = None
|
21
|
+
|
22
|
+
def createWidget(self, parent):
|
23
|
+
t = BECProgressBar(parent)
|
24
|
+
return t
|
25
|
+
|
26
|
+
def domXml(self):
|
27
|
+
return DOM_XML
|
28
|
+
|
29
|
+
def group(self):
|
30
|
+
return "BEC Utils"
|
31
|
+
|
32
|
+
def icon(self):
|
33
|
+
return designer_material_icon(BECProgressBar.ICON_NAME)
|
34
|
+
|
35
|
+
def includeFile(self):
|
36
|
+
return "bec_progress_bar"
|
37
|
+
|
38
|
+
def initialize(self, form_editor):
|
39
|
+
self._form_editor = form_editor
|
40
|
+
|
41
|
+
def isContainer(self):
|
42
|
+
return False
|
43
|
+
|
44
|
+
def isInitialized(self):
|
45
|
+
return self._form_editor is not None
|
46
|
+
|
47
|
+
def name(self):
|
48
|
+
return "BECProgressBar"
|
49
|
+
|
50
|
+
def toolTip(self):
|
51
|
+
return "A custom progress bar with smooth transitions and a modern design."
|
52
|
+
|
53
|
+
def whatsThis(self):
|
54
|
+
return self.toolTip()
|
@@ -0,0 +1,257 @@
|
|
1
|
+
import sys
|
2
|
+
from string import Template
|
3
|
+
|
4
|
+
from qtpy.QtCore import Property, QEasingCurve, QPropertyAnimation, QRectF, Qt, QTimer, Slot
|
5
|
+
from qtpy.QtGui import QColor, QPainter, QPainterPath
|
6
|
+
from qtpy.QtWidgets import QApplication, QLabel, QVBoxLayout, QWidget
|
7
|
+
|
8
|
+
from bec_widgets.utils.bec_widget import BECWidget
|
9
|
+
|
10
|
+
|
11
|
+
class BECProgressBar(BECWidget, QWidget):
|
12
|
+
"""
|
13
|
+
A custom progress bar with smooth transitions. The displayed text can be customized using a template.
|
14
|
+
"""
|
15
|
+
|
16
|
+
USER_ACCESS = [
|
17
|
+
"set_value",
|
18
|
+
"set_maximum",
|
19
|
+
"set_minimum",
|
20
|
+
"label_template",
|
21
|
+
"label_template.setter",
|
22
|
+
]
|
23
|
+
ICON_NAME = "page_control"
|
24
|
+
|
25
|
+
def __init__(self, parent=None, client=None, config=None, gui_id=None):
|
26
|
+
super().__init__(client=client, config=config, gui_id=gui_id)
|
27
|
+
QWidget.__init__(self, parent=parent)
|
28
|
+
|
29
|
+
accent_colors = QApplication.instance().theme.accent_colors
|
30
|
+
|
31
|
+
# internal values
|
32
|
+
self._oversampling_factor = 50
|
33
|
+
self._value = 0
|
34
|
+
self._target_value = 0
|
35
|
+
self._maximum = 100 * self._oversampling_factor
|
36
|
+
|
37
|
+
# User values
|
38
|
+
self._user_value = 0
|
39
|
+
self._user_minimum = 0
|
40
|
+
self._user_maximum = 100
|
41
|
+
self._label_template = "$value / $maximum - $percentage %"
|
42
|
+
|
43
|
+
# Color settings
|
44
|
+
self._background_color = QColor(30, 30, 30)
|
45
|
+
self._progress_color = accent_colors.highlight # QColor(210, 55, 130)
|
46
|
+
|
47
|
+
self._completed_color = accent_colors.success
|
48
|
+
self._border_color = QColor(50, 50, 50)
|
49
|
+
|
50
|
+
# layout settings
|
51
|
+
self._value_animation = QPropertyAnimation(self, b"_progressbar_value")
|
52
|
+
self._value_animation.setDuration(200)
|
53
|
+
self._value_animation.setEasingCurve(QEasingCurve.Type.OutCubic)
|
54
|
+
|
55
|
+
# label on top of the progress bar
|
56
|
+
self.center_label = QLabel(self)
|
57
|
+
self.center_label.setAlignment(Qt.AlignCenter)
|
58
|
+
self.center_label.setStyleSheet("color: white;")
|
59
|
+
self.center_label.setMinimumSize(0, 0)
|
60
|
+
|
61
|
+
layout = QVBoxLayout(self)
|
62
|
+
layout.setContentsMargins(0, 0, 0, 0)
|
63
|
+
layout.setSpacing(0)
|
64
|
+
layout.addWidget(self.center_label)
|
65
|
+
self.setLayout(layout)
|
66
|
+
|
67
|
+
self.update()
|
68
|
+
|
69
|
+
@Property(str, doc="The template for the center label. Use $value, $maximum, and $percentage.")
|
70
|
+
def label_template(self):
|
71
|
+
"""
|
72
|
+
The template for the center label. Use $value, $maximum, and $percentage to insert the values.
|
73
|
+
|
74
|
+
Examples:
|
75
|
+
>>> progressbar.label_template = "$value / $maximum - $percentage %"
|
76
|
+
>>> progressbar.label_template = "$value / $percentage %"
|
77
|
+
|
78
|
+
"""
|
79
|
+
return self._label_template
|
80
|
+
|
81
|
+
@label_template.setter
|
82
|
+
def label_template(self, template):
|
83
|
+
self._label_template = template
|
84
|
+
self.set_value(self._user_value)
|
85
|
+
self.update()
|
86
|
+
|
87
|
+
@Property(float, designable=False)
|
88
|
+
def _progressbar_value(self):
|
89
|
+
"""
|
90
|
+
The current value of the progress bar.
|
91
|
+
"""
|
92
|
+
return self._value
|
93
|
+
|
94
|
+
@_progressbar_value.setter
|
95
|
+
def _progressbar_value(self, val):
|
96
|
+
self._value = val
|
97
|
+
self.update()
|
98
|
+
|
99
|
+
def _update_template(self):
|
100
|
+
template = Template(self._label_template)
|
101
|
+
return template.safe_substitute(
|
102
|
+
value=self._user_value,
|
103
|
+
maximum=self._user_maximum,
|
104
|
+
percentage=int((self.map_value(self._user_value) / self._maximum) * 100),
|
105
|
+
)
|
106
|
+
|
107
|
+
@Slot(float)
|
108
|
+
@Slot(int)
|
109
|
+
def set_value(self, value):
|
110
|
+
"""
|
111
|
+
Set the value of the progress bar.
|
112
|
+
|
113
|
+
Args:
|
114
|
+
value (float): The value to set.
|
115
|
+
"""
|
116
|
+
if value > self._user_maximum:
|
117
|
+
value = self._user_maximum
|
118
|
+
elif value < self._user_minimum:
|
119
|
+
value = self._user_minimum
|
120
|
+
self._target_value = self.map_value(value)
|
121
|
+
self._user_value = value
|
122
|
+
self.center_label.setText(self._update_template())
|
123
|
+
self.animate_progress()
|
124
|
+
|
125
|
+
def paintEvent(self, event):
|
126
|
+
painter = QPainter(self)
|
127
|
+
painter.setRenderHint(QPainter.Antialiasing)
|
128
|
+
rect = self.rect().adjusted(10, 0, -10, -1)
|
129
|
+
|
130
|
+
# Draw background
|
131
|
+
painter.setBrush(self._background_color)
|
132
|
+
painter.setPen(Qt.NoPen)
|
133
|
+
painter.drawRoundedRect(rect, 10, 10) # Rounded corners
|
134
|
+
|
135
|
+
# Draw border
|
136
|
+
painter.setBrush(Qt.NoBrush)
|
137
|
+
painter.setPen(self._border_color)
|
138
|
+
painter.drawRoundedRect(rect, 10, 10)
|
139
|
+
|
140
|
+
# Determine progress color based on completion
|
141
|
+
if self._value >= self._maximum:
|
142
|
+
current_color = self._completed_color
|
143
|
+
else:
|
144
|
+
current_color = self._progress_color
|
145
|
+
|
146
|
+
# Set clipping region to preserve the background's rounded corners
|
147
|
+
progress_rect = rect.adjusted(
|
148
|
+
0, 0, int(-rect.width() + (self._value / self._maximum) * rect.width()), 0
|
149
|
+
)
|
150
|
+
clip_path = QPainterPath()
|
151
|
+
clip_path.addRoundedRect(QRectF(rect), 10, 10) # Clip to the background's rounded corners
|
152
|
+
painter.setClipPath(clip_path)
|
153
|
+
|
154
|
+
# Draw progress bar
|
155
|
+
painter.setBrush(current_color)
|
156
|
+
painter.drawRect(progress_rect) # Less rounded, no additional rounding
|
157
|
+
|
158
|
+
painter.end()
|
159
|
+
|
160
|
+
def animate_progress(self):
|
161
|
+
"""
|
162
|
+
Animate the progress bar from the current value to the target value.
|
163
|
+
"""
|
164
|
+
self._value_animation.stop()
|
165
|
+
self._value_animation.setStartValue(self._value)
|
166
|
+
self._value_animation.setEndValue(self._target_value)
|
167
|
+
self._value_animation.start()
|
168
|
+
|
169
|
+
@Property(float)
|
170
|
+
def maximum(self):
|
171
|
+
"""
|
172
|
+
The maximum value of the progress bar.
|
173
|
+
"""
|
174
|
+
return self._user_maximum
|
175
|
+
|
176
|
+
@maximum.setter
|
177
|
+
def maximum(self, maximum: float):
|
178
|
+
"""
|
179
|
+
Set the maximum value of the progress bar.
|
180
|
+
"""
|
181
|
+
self.set_maximum(maximum)
|
182
|
+
|
183
|
+
@Property(float)
|
184
|
+
def minimum(self):
|
185
|
+
"""
|
186
|
+
The minimum value of the progress bar.
|
187
|
+
"""
|
188
|
+
return self._user_minimum
|
189
|
+
|
190
|
+
@minimum.setter
|
191
|
+
def minimum(self, minimum: float):
|
192
|
+
self.set_minimum(minimum)
|
193
|
+
|
194
|
+
@Property(float)
|
195
|
+
def initial_value(self):
|
196
|
+
"""
|
197
|
+
The initial value of the progress bar.
|
198
|
+
"""
|
199
|
+
return self._user_value
|
200
|
+
|
201
|
+
@initial_value.setter
|
202
|
+
def initial_value(self, value: float):
|
203
|
+
self.set_value(value)
|
204
|
+
|
205
|
+
@Slot(float)
|
206
|
+
def set_maximum(self, maximum: float):
|
207
|
+
"""
|
208
|
+
Set the maximum value of the progress bar.
|
209
|
+
|
210
|
+
Args:
|
211
|
+
maximum (float): The maximum value.
|
212
|
+
"""
|
213
|
+
self._user_maximum = maximum
|
214
|
+
self.set_value(self._user_value) # Update the value to fit the new range
|
215
|
+
self.update()
|
216
|
+
|
217
|
+
@Slot(float)
|
218
|
+
def set_minimum(self, minimum: float):
|
219
|
+
"""
|
220
|
+
Set the minimum value of the progress bar.
|
221
|
+
|
222
|
+
Args:
|
223
|
+
minimum (float): The minimum value.
|
224
|
+
"""
|
225
|
+
self._user_minimum = minimum
|
226
|
+
self.set_value(self._user_value) # Update the value to fit the new range
|
227
|
+
self.update()
|
228
|
+
|
229
|
+
def map_value(self, value: float):
|
230
|
+
"""
|
231
|
+
Map the user value to the range [0, 100*self._oversampling_factor] for the progress
|
232
|
+
"""
|
233
|
+
return (
|
234
|
+
(value - self._user_minimum) / (self._user_maximum - self._user_minimum) * self._maximum
|
235
|
+
)
|
236
|
+
|
237
|
+
|
238
|
+
if __name__ == "__main__": # pragma: no cover
|
239
|
+
app = QApplication(sys.argv)
|
240
|
+
|
241
|
+
progressBar = BECProgressBar()
|
242
|
+
progressBar.show()
|
243
|
+
progressBar.set_minimum(-100)
|
244
|
+
progressBar.set_maximum(0)
|
245
|
+
|
246
|
+
# Example of setting values
|
247
|
+
def update_progress():
|
248
|
+
value = progressBar._user_value + 2.5
|
249
|
+
if value > progressBar._user_maximum:
|
250
|
+
value = -100 # progressBar._maximum / progressBar._upsampling_factor
|
251
|
+
progressBar.set_value(value)
|
252
|
+
|
253
|
+
timer = QTimer()
|
254
|
+
timer.timeout.connect(update_progress)
|
255
|
+
timer.start(200) # Update every half second
|
256
|
+
|
257
|
+
sys.exit(app.exec())
|
@@ -0,0 +1,15 @@
|
|
1
|
+
def main(): # pragma: no cover
|
2
|
+
from qtpy import PYSIDE6
|
3
|
+
|
4
|
+
if not PYSIDE6:
|
5
|
+
print("PYSIDE6 is not available in the environment. Cannot patch designer.")
|
6
|
+
return
|
7
|
+
from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
|
8
|
+
|
9
|
+
from bec_widgets.widgets.bec_progressbar.bec_progress_bar_plugin import BECProgressBarPlugin
|
10
|
+
|
11
|
+
QPyDesignerCustomWidgetCollection.addCustomWidget(BECProgressBarPlugin())
|
12
|
+
|
13
|
+
|
14
|
+
if __name__ == "__main__": # pragma: no cover
|
15
|
+
main()
|
@@ -12,10 +12,11 @@ from bec_lib.logger import bec_logger
|
|
12
12
|
from pydantic import Field, ValidationError, field_validator
|
13
13
|
from pyqtgraph.exporters import MatplotlibExporter
|
14
14
|
from qtpy.QtCore import Signal as pyqtSignal
|
15
|
-
from qtpy.QtWidgets import QWidget
|
15
|
+
from qtpy.QtWidgets import QApplication, QWidget
|
16
16
|
|
17
17
|
from bec_widgets.qt_utils.error_popups import SafeSlot as Slot
|
18
18
|
from bec_widgets.utils import Colors, EntryValidator
|
19
|
+
from bec_widgets.utils.linear_region_selector import LinearRegionWrapper
|
19
20
|
from bec_widgets.widgets.figure.plots.plot_base import BECPlotBase, SubplotConfig
|
20
21
|
from bec_widgets.widgets.figure.plots.waveform.waveform_curve import (
|
21
22
|
BECCurve,
|
@@ -74,6 +75,8 @@ class BECWaveform(BECPlotBase):
|
|
74
75
|
"remove",
|
75
76
|
"clear_all",
|
76
77
|
"set_legend_label_size",
|
78
|
+
"toggle_roi",
|
79
|
+
"select_roi",
|
77
80
|
]
|
78
81
|
scan_signal_update = pyqtSignal()
|
79
82
|
async_signal_update = pyqtSignal()
|
@@ -81,6 +84,9 @@ class BECWaveform(BECPlotBase):
|
|
81
84
|
dap_summary_update = pyqtSignal(dict, dict)
|
82
85
|
autorange_signal = pyqtSignal()
|
83
86
|
new_scan = pyqtSignal()
|
87
|
+
roi_changed = pyqtSignal(tuple)
|
88
|
+
roi_active = pyqtSignal(bool)
|
89
|
+
request_dap_refresh = pyqtSignal()
|
84
90
|
|
85
91
|
def __init__(
|
86
92
|
self,
|
@@ -100,6 +106,9 @@ class BECWaveform(BECPlotBase):
|
|
100
106
|
self.old_scan_id = None
|
101
107
|
self.scan_id = None
|
102
108
|
self.scan_item = None
|
109
|
+
self._roi_region = None
|
110
|
+
self.roi_select = None
|
111
|
+
self._accent_colors = QApplication.instance().theme.accent_colors
|
103
112
|
self._x_axis_mode = {
|
104
113
|
"name": None,
|
105
114
|
"entry": None,
|
@@ -130,6 +139,63 @@ class BECWaveform(BECPlotBase):
|
|
130
139
|
self.add_legend()
|
131
140
|
self.apply_config(self.config)
|
132
141
|
|
142
|
+
@Slot(bool)
|
143
|
+
def toggle_roi(self, toggled: bool) -> None:
|
144
|
+
"""Toggle the linear region selector on the plot.
|
145
|
+
|
146
|
+
Args:
|
147
|
+
toggled(bool): If True, enable the linear region selector.
|
148
|
+
"""
|
149
|
+
if toggled:
|
150
|
+
return self._hook_roi()
|
151
|
+
return self._unhook_roi()
|
152
|
+
|
153
|
+
@Slot(tuple)
|
154
|
+
def select_roi(self, region: tuple[float, float]):
|
155
|
+
"""Set the fit region of the plot widget. At the moment only a single region is supported.
|
156
|
+
To remove the roi region again, use toggle_roi_region
|
157
|
+
|
158
|
+
Args:
|
159
|
+
region(tuple[float, float]): The fit region.
|
160
|
+
"""
|
161
|
+
if self.roi_region == (None, None):
|
162
|
+
self.toggle_roi(True)
|
163
|
+
try:
|
164
|
+
self.roi_select.linear_region_selector.setRegion(region)
|
165
|
+
except Exception as e:
|
166
|
+
logger.error(f"Error setting region {tuple}; Exception raised: {e}")
|
167
|
+
raise ValueError(f"Error setting region {tuple}; Exception raised: {e}")
|
168
|
+
|
169
|
+
def _hook_roi(self):
|
170
|
+
"""Hook the linear region selector to the plot."""
|
171
|
+
color = self._accent_colors.default
|
172
|
+
color.setAlpha(int(0.2 * 255))
|
173
|
+
hover_color = self._accent_colors.default
|
174
|
+
hover_color.setAlpha(int(0.35 * 255))
|
175
|
+
if self.roi_select is None:
|
176
|
+
self.roi_select = LinearRegionWrapper(
|
177
|
+
self.plot_item, color=color, hover_color=hover_color, parent=self
|
178
|
+
)
|
179
|
+
self.roi_select.add_region_selector()
|
180
|
+
self.roi_select.region_changed.connect(self.roi_changed)
|
181
|
+
self.roi_select.region_changed.connect(self.set_roi_region)
|
182
|
+
self.request_dap_refresh.connect(self.refresh_dap)
|
183
|
+
self._emit_roi_region()
|
184
|
+
self.roi_active.emit(True)
|
185
|
+
|
186
|
+
def _unhook_roi(self):
|
187
|
+
"""Unhook the linear region selector from the plot."""
|
188
|
+
if self.roi_select is not None:
|
189
|
+
self.roi_select.region_changed.disconnect(self.roi_changed)
|
190
|
+
self.roi_select.region_changed.disconnect(self.set_roi_region)
|
191
|
+
self.request_dap_refresh.disconnect(self.refresh_dap)
|
192
|
+
self.roi_active.emit(False)
|
193
|
+
self.roi_region = None
|
194
|
+
self.refresh_dap()
|
195
|
+
self.roi_select.cleanup()
|
196
|
+
self.roi_select.deleteLater()
|
197
|
+
self.roi_select = None
|
198
|
+
|
133
199
|
def apply_config(self, config: dict | SubplotConfig, replot_last_scan: bool = False):
|
134
200
|
"""
|
135
201
|
Apply the configuration to the 1D waveform widget.
|
@@ -171,6 +237,48 @@ class BECWaveform(BECPlotBase):
|
|
171
237
|
for curve in self.curves:
|
172
238
|
curve.config.parent_id = new_gui_id
|
173
239
|
|
240
|
+
###################################
|
241
|
+
# Fit Range Properties
|
242
|
+
###################################
|
243
|
+
|
244
|
+
@property
|
245
|
+
def roi_region(self) -> tuple[float, float] | None:
|
246
|
+
"""
|
247
|
+
Get the fit region of the plot widget.
|
248
|
+
|
249
|
+
Returns:
|
250
|
+
tuple: The fit region.
|
251
|
+
"""
|
252
|
+
if self._roi_region is not None:
|
253
|
+
return self._roi_region
|
254
|
+
return None, None
|
255
|
+
|
256
|
+
@roi_region.setter
|
257
|
+
def roi_region(self, value: tuple[float, float] | None):
|
258
|
+
"""Set the fit region of the plot widget.
|
259
|
+
|
260
|
+
Args:
|
261
|
+
value(tuple[float, float]|None): The fit region.
|
262
|
+
"""
|
263
|
+
self._roi_region = value
|
264
|
+
if value is not None:
|
265
|
+
self.request_dap_refresh.emit()
|
266
|
+
|
267
|
+
@Slot(tuple)
|
268
|
+
def set_roi_region(self, region: tuple[float, float]):
|
269
|
+
"""
|
270
|
+
Set the fit region of the plot widget.
|
271
|
+
|
272
|
+
Args:
|
273
|
+
region(tuple[float, float]): The fit region.
|
274
|
+
"""
|
275
|
+
self.roi_region = region
|
276
|
+
|
277
|
+
def _emit_roi_region(self):
|
278
|
+
"""Emit the current ROI from selector the plot widget."""
|
279
|
+
if self.roi_select is not None:
|
280
|
+
self.set_roi_region(self.roi_select.linear_region_selector.getRegion())
|
281
|
+
|
174
282
|
###################################
|
175
283
|
# Waveform Properties
|
176
284
|
###################################
|
@@ -1058,13 +1166,14 @@ class BECWaveform(BECPlotBase):
|
|
1058
1166
|
y_entry = curve.config.signals.y.entry
|
1059
1167
|
model_name = curve.config.signals.dap
|
1060
1168
|
model = getattr(self.dap, model_name)
|
1169
|
+
x_min, x_max = self.roi_region
|
1061
1170
|
|
1062
1171
|
msg = messages.DAPRequestMessage(
|
1063
1172
|
dap_cls="LmfitService1D",
|
1064
1173
|
dap_type="on_demand",
|
1065
1174
|
config={
|
1066
1175
|
"args": [self.scan_id, x_name, x_entry, y_name, y_entry],
|
1067
|
-
"kwargs": {},
|
1176
|
+
"kwargs": {"x_min": x_min, "x_max": x_max},
|
1068
1177
|
"class_args": model._plugin_info["class_args"],
|
1069
1178
|
"class_kwargs": model._plugin_info["class_kwargs"],
|
1070
1179
|
},
|
@@ -1304,7 +1413,8 @@ class BECWaveform(BECPlotBase):
|
|
1304
1413
|
self.scan_signal_update.emit()
|
1305
1414
|
self.async_signal_update.emit()
|
1306
1415
|
|
1307
|
-
|
1416
|
+
# pylint: ignore: undefined-variable
|
1417
|
+
def get_all_data(self, output: Literal["dict", "pandas"] = "dict") -> dict: # | pd.DataFrame:
|
1308
1418
|
"""
|
1309
1419
|
Extract all curve data into a dictionary or a pandas DataFrame.
|
1310
1420
|
|
@@ -1351,13 +1461,21 @@ class BECWaveform(BECPlotBase):
|
|
1351
1461
|
"""
|
1352
1462
|
MatplotlibExporter(self.plot_item).export()
|
1353
1463
|
|
1354
|
-
def
|
1464
|
+
def clear_source(self, source: Literal["DAP", "async", "scan_segment", "custom"]):
|
1465
|
+
"""Clear speicific source from self._curves_data.
|
1466
|
+
|
1467
|
+
Args:
|
1468
|
+
source (Literal["DAP", "async", "scan_segment", "custom"]): Source to be cleared.
|
1469
|
+
"""
|
1355
1470
|
curves_data = self._curves_data
|
1356
|
-
|
1471
|
+
curve_ids_to_remove = list(curves_data[source].keys())
|
1472
|
+
for curve_id in curve_ids_to_remove:
|
1473
|
+
self.remove_curve(curve_id)
|
1474
|
+
|
1475
|
+
def clear_all(self):
|
1476
|
+
sources = list(self._curves_data.keys())
|
1357
1477
|
for source in sources:
|
1358
|
-
|
1359
|
-
for curve_id in curve_ids_to_remove:
|
1360
|
-
self.remove_curve(curve_id)
|
1478
|
+
self.clear_source(source)
|
1361
1479
|
|
1362
1480
|
def cleanup(self):
|
1363
1481
|
"""Cleanup the widget connection from BECDispatcher."""
|
@@ -6,7 +6,7 @@ from typing import Literal
|
|
6
6
|
import numpy as np
|
7
7
|
import pyqtgraph as pg
|
8
8
|
from bec_lib.logger import bec_logger
|
9
|
-
from qtpy.QtCore import Signal
|
9
|
+
from qtpy.QtCore import Property, Signal, Slot
|
10
10
|
from qtpy.QtWidgets import QVBoxLayout, QWidget
|
11
11
|
|
12
12
|
from bec_widgets.qt_utils.error_popups import SafeSlot, WarningPopupUtility
|
@@ -55,6 +55,8 @@ class BECWaveformWidget(BECWidget, QWidget):
|
|
55
55
|
"lock_aspect_ratio",
|
56
56
|
"export",
|
57
57
|
"export_to_matplotlib",
|
58
|
+
"toggle_roi",
|
59
|
+
"select_roi",
|
58
60
|
]
|
59
61
|
scan_signal_update = Signal()
|
60
62
|
async_signal_update = Signal()
|
@@ -70,6 +72,8 @@ class BECWaveformWidget(BECWidget, QWidget):
|
|
70
72
|
crosshair_coordinates_changed_string = Signal(str)
|
71
73
|
crosshair_coordinates_clicked = Signal(tuple)
|
72
74
|
crosshair_coordinates_clicked_string = Signal(str)
|
75
|
+
roi_changed = Signal(tuple)
|
76
|
+
roi_active = Signal(bool)
|
73
77
|
|
74
78
|
def __init__(
|
75
79
|
self,
|
@@ -120,6 +124,11 @@ class BECWaveformWidget(BECWidget, QWidget):
|
|
120
124
|
"crosshair": MaterialIconAction(
|
121
125
|
icon_name="point_scan", tooltip="Show Crosshair", checkable=True
|
122
126
|
),
|
127
|
+
"roi_select": MaterialIconAction(
|
128
|
+
icon_name="align_justify_space_between",
|
129
|
+
tooltip="Add ROI region for DAP",
|
130
|
+
checkable=True,
|
131
|
+
),
|
123
132
|
},
|
124
133
|
target_widget=self,
|
125
134
|
)
|
@@ -133,6 +142,7 @@ class BECWaveformWidget(BECWidget, QWidget):
|
|
133
142
|
self.waveform.apply_config(config)
|
134
143
|
|
135
144
|
self.config = config
|
145
|
+
self._clear_curves_on_plot_update = False
|
136
146
|
|
137
147
|
self.hook_waveform_signals()
|
138
148
|
self._hook_actions()
|
@@ -160,6 +170,8 @@ class BECWaveformWidget(BECWidget, QWidget):
|
|
160
170
|
self.waveform.crosshair_position_clicked.connect(
|
161
171
|
self._emit_crosshair_position_clicked_string
|
162
172
|
)
|
173
|
+
self.waveform.roi_changed.connect(self.roi_changed)
|
174
|
+
self.waveform.roi_active.connect(self.roi_active)
|
163
175
|
|
164
176
|
def _hook_actions(self):
|
165
177
|
self.toolbar.widgets["save"].action.triggered.connect(self.export)
|
@@ -173,6 +185,7 @@ class BECWaveformWidget(BECWidget, QWidget):
|
|
173
185
|
self.toolbar.widgets["fit_params"].action.triggered.connect(self.show_fit_summary_dialog)
|
174
186
|
self.toolbar.widgets["axis_settings"].action.triggered.connect(self.show_axis_settings)
|
175
187
|
self.toolbar.widgets["crosshair"].action.triggered.connect(self.waveform.toggle_crosshair)
|
188
|
+
self.toolbar.widgets["roi_select"].action.toggled.connect(self.waveform.toggle_roi)
|
176
189
|
# self.toolbar.widgets["import"].action.triggered.connect(
|
177
190
|
# lambda: self.load_config(path=None, gui=True)
|
178
191
|
# )
|
@@ -180,6 +193,29 @@ class BECWaveformWidget(BECWidget, QWidget):
|
|
180
193
|
# lambda: self.save_config(path=None, gui=True)
|
181
194
|
# )
|
182
195
|
|
196
|
+
@Slot(bool)
|
197
|
+
def toogle_roi_select(self, checked: bool):
|
198
|
+
"""Toggle the linear region selector.
|
199
|
+
|
200
|
+
Args:
|
201
|
+
checked(bool): If True, enable the linear region selector.
|
202
|
+
"""
|
203
|
+
self.toolbar.widgets["roi_select"].action.setChecked(checked)
|
204
|
+
|
205
|
+
@Property(bool)
|
206
|
+
def clear_curves_on_plot_update(self) -> bool:
|
207
|
+
"""If True, clear curves on plot update."""
|
208
|
+
return self._clear_curves_on_plot_update
|
209
|
+
|
210
|
+
@clear_curves_on_plot_update.setter
|
211
|
+
def clear_curves_on_plot_update(self, value: bool):
|
212
|
+
"""Set the clear curves on plot update property.
|
213
|
+
|
214
|
+
Args:
|
215
|
+
value(bool): If True, clear curves on plot update.
|
216
|
+
"""
|
217
|
+
self._clear_curves_on_plot_update = value
|
218
|
+
|
183
219
|
@SafeSlot(tuple)
|
184
220
|
def _emit_crosshair_coordinates_changed_string(self, coordinates):
|
185
221
|
self.crosshair_coordinates_changed_string.emit(str(coordinates))
|
@@ -260,7 +296,8 @@ class BECWaveformWidget(BECWidget, QWidget):
|
|
260
296
|
"""
|
261
297
|
self.waveform.set_colormap(colormap)
|
262
298
|
|
263
|
-
@
|
299
|
+
@Slot(str, str) # Slot for x_name, x_entry
|
300
|
+
@SafeSlot(str, popup_error=True) # Slot for x_name and
|
264
301
|
def set_x(self, x_name: str, x_entry: str | None = None):
|
265
302
|
"""
|
266
303
|
Change the x axis of the plot widget.
|
@@ -275,7 +312,8 @@ class BECWaveformWidget(BECWidget, QWidget):
|
|
275
312
|
"""
|
276
313
|
self.waveform.set_x(x_name, x_entry)
|
277
314
|
|
278
|
-
@
|
315
|
+
@Slot(str) # Slot for y_name
|
316
|
+
@SafeSlot(popup_error=True)
|
279
317
|
def plot(
|
280
318
|
self,
|
281
319
|
arg1: list | np.ndarray | str | None = None,
|
@@ -315,7 +353,8 @@ class BECWaveformWidget(BECWidget, QWidget):
|
|
315
353
|
Returns:
|
316
354
|
BECCurve: The curve object.
|
317
355
|
"""
|
318
|
-
|
356
|
+
if self.clear_curves_on_plot_update is True:
|
357
|
+
self.waveform.clear_source(source="scan_segment")
|
319
358
|
return self.waveform.plot(
|
320
359
|
arg1=arg1,
|
321
360
|
x=x,
|
@@ -334,6 +373,9 @@ class BECWaveformWidget(BECWidget, QWidget):
|
|
334
373
|
**kwargs,
|
335
374
|
)
|
336
375
|
|
376
|
+
@Slot(
|
377
|
+
str, str, str, str, str, str, bool
|
378
|
+
) # Slot for x_name, y_name, x_entry, y_entry, color, validate_bec
|
337
379
|
@SafeSlot(str, str, str, popup_error=True)
|
338
380
|
def add_dap(
|
339
381
|
self,
|
@@ -362,6 +404,8 @@ class BECWaveformWidget(BECWidget, QWidget):
|
|
362
404
|
Returns:
|
363
405
|
BECCurve: The curve object.
|
364
406
|
"""
|
407
|
+
if self.clear_curves_on_plot_update is True:
|
408
|
+
self.waveform.clear_source(source="DAP")
|
365
409
|
return self.waveform.add_dap(
|
366
410
|
x_name=x_name,
|
367
411
|
y_name=y_name,
|
@@ -543,6 +587,23 @@ class BECWaveformWidget(BECWidget, QWidget):
|
|
543
587
|
"""
|
544
588
|
self.waveform.set_auto_range(enabled, axis)
|
545
589
|
|
590
|
+
def toggle_roi(self, checked: bool):
|
591
|
+
"""Toggle the linear region selector.
|
592
|
+
|
593
|
+
Args:
|
594
|
+
checked(bool): If True, enable the linear region selector.
|
595
|
+
"""
|
596
|
+
self.waveform.toggle_roi(checked)
|
597
|
+
|
598
|
+
def select_roi(self, region: tuple):
|
599
|
+
"""
|
600
|
+
Set the region of interest of the plot widget.
|
601
|
+
|
602
|
+
Args:
|
603
|
+
region(tuple): Region of interest.
|
604
|
+
"""
|
605
|
+
self.waveform.select_roi(region)
|
606
|
+
|
546
607
|
@SafeSlot()
|
547
608
|
def _auto_range_from_toolbar(self):
|
548
609
|
"""
|
@@ -644,6 +705,10 @@ def main(): # pragma: no cover
|
|
644
705
|
|
645
706
|
app = QApplication(sys.argv)
|
646
707
|
widget = BECWaveformWidget()
|
708
|
+
widget.plot(x_name="samx", y_name="bpm4i")
|
709
|
+
widget.plot(y_name="bpm3i")
|
710
|
+
widget.plot(y_name="bpm4a")
|
711
|
+
widget.plot(y_name="bpm5i")
|
647
712
|
widget.show()
|
648
713
|
sys.exit(app.exec_())
|
649
714
|
|
@@ -2,11 +2,11 @@
|
|
2
2
|
.gitlab-ci.yml,sha256=Dc1iDjsc72UxdUtihx4uSZU0lrTQeR8hZwGx1MQBfKE,8432
|
3
3
|
.pylintrc,sha256=eeY8YwSI74oFfq6IYIbCqnx3Vk8ZncKaatv96n_Y8Rs,18544
|
4
4
|
.readthedocs.yaml,sha256=aSOc277LqXcsTI6lgvm_JY80lMlr69GbPKgivua2cS0,603
|
5
|
-
CHANGELOG.md,sha256=
|
5
|
+
CHANGELOG.md,sha256=apWeMcUSqInZhgc5D8Hr58kcnMt6hA1Ybx3ouRbOhZk,7244
|
6
6
|
LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
|
7
|
-
PKG-INFO,sha256=
|
7
|
+
PKG-INFO,sha256=BGf3jlAjgMTSAg0eeqjTNolgh8gfWwzSsOdWxL-e8FY,1334
|
8
8
|
README.md,sha256=Od69x-RS85Hph0-WwWACwal4yUd67XkEn4APEfHhHFw,2649
|
9
|
-
pyproject.toml,sha256=
|
9
|
+
pyproject.toml,sha256=6_DDd-tPxZvvy7p-Wk0HRZV6gbxqb39tEpIBh1yo9R0,2544
|
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
|
@@ -22,12 +22,12 @@ bec_widgets/assets/status_icons/running.svg,sha256=nlc6rKh_f-uOxQSk0BkBNyWnPAJU5
|
|
22
22
|
bec_widgets/assets/status_icons/warning.svg,sha256=CNx88p9kbDG51s9ztKf-cfYan4JdDBbk3-IFKfOOFlI,364
|
23
23
|
bec_widgets/cli/__init__.py,sha256=d0Q6Fn44e7wFfLabDOBxpcJ1DPKWlFunGYDUBmO-4hA,22
|
24
24
|
bec_widgets/cli/auto_updates.py,sha256=DwzRChcFIWPH2kCYvp8H7dXvyYSKGYv6LwCmK2sDR2E,5676
|
25
|
-
bec_widgets/cli/client.py,sha256=
|
25
|
+
bec_widgets/cli/client.py,sha256=aG1m5xXbIsn0Cq7isrUgyA_l4yLFgbKsmmiXye6yPp8,81550
|
26
26
|
bec_widgets/cli/client_utils.py,sha256=EdDfo3uuYAWtZiDGGu3_GPnl94FSLkNG2N_4I9FNfMc,11809
|
27
|
-
bec_widgets/cli/generate_cli.py,sha256=
|
27
|
+
bec_widgets/cli/generate_cli.py,sha256=zRhhcErjHqnNymoxu9oqeUZUfwLX84De1RIeGiZGUyY,6602
|
28
28
|
bec_widgets/cli/rpc_register.py,sha256=QxXUZu5XNg00Yf5O3UHWOXg3-f_pzKjjoZYMOa-MOJc,2216
|
29
29
|
bec_widgets/cli/rpc_wigdet_handler.py,sha256=6kQng2DyS6rhLJqSJ7xa0kdgSxp-35A2upcf833dJRE,1483
|
30
|
-
bec_widgets/cli/server.py,sha256=
|
30
|
+
bec_widgets/cli/server.py,sha256=KfZ0W2OKb1UeqkR0buXrdVF4MpznzyLw9EQtGzEBkvI,8833
|
31
31
|
bec_widgets/examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
32
32
|
bec_widgets/examples/general_app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
33
33
|
bec_widgets/examples/general_app/general_app.py,sha256=PoFCTuA_1yqrpgthASpYFgH7JDUZTcXAPZ5h0e3XZfc,3053
|
@@ -59,6 +59,7 @@ bec_widgets/utils/crosshair.py,sha256=8lik9k69WI2WMj5FGLbrKtny9duxqXUJAhpX8tHoyp
|
|
59
59
|
bec_widgets/utils/entry_validator.py,sha256=3skJIsUwTYicT76AMHm_M78RiWtUgyD2zb-Rxo2HdHQ,1313
|
60
60
|
bec_widgets/utils/generate_designer_plugin.py,sha256=eidqauS8YLgoxkPntPL0oSG_lYqI2D7fSyOZvOtCU_U,5891
|
61
61
|
bec_widgets/utils/layout_manager.py,sha256=H0nKsIMaPxRkof1MEXlSmW6w1dFxA6astaGzf4stI84,4727
|
62
|
+
bec_widgets/utils/linear_region_selector.py,sha256=kRLoZ0P0FttcsFvDBN8NGPm3swCIBv7Ax9AuqmjkRjE,2719
|
62
63
|
bec_widgets/utils/plugin_utils.py,sha256=njvVdvF-AR47Yn80ntpvFldEvLuFx9GV-qEX4p_n4AI,5263
|
63
64
|
bec_widgets/utils/reference_utils.py,sha256=8pq06TOvZBZdim0G6hvPJXzVDib7ve4o-Ptvfp563nk,2859
|
64
65
|
bec_widgets/utils/rpc_decorator.py,sha256=pIvtqySQLnuS7l2Ti_UAe4WX7CRivZnsE5ZdKAihxh0,479
|
@@ -72,6 +73,11 @@ bec_widgets/utils/plugin_templates/register.template,sha256=XyL3OZPT_FTArLAM8tHd
|
|
72
73
|
bec_widgets/widgets/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
73
74
|
bec_widgets/widgets/base_classes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
74
75
|
bec_widgets/widgets/base_classes/device_input_base.py,sha256=thCOHOa9Z0b3-vlNFWK6PT_DdPTANnfj0_DLES_K-eE,3902
|
76
|
+
bec_widgets/widgets/bec_progressbar/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
77
|
+
bec_widgets/widgets/bec_progressbar/bec_progress_bar.pyproject,sha256=Pb3n9seM95_8myKJ0pv_9IcfJDNJJNOFBskuRdEVzhU,33
|
78
|
+
bec_widgets/widgets/bec_progressbar/bec_progress_bar_plugin.py,sha256=b0b0F37HrwFAtizF4VC9udOe1eKbpDkrUE1pM7fI_f0,1352
|
79
|
+
bec_widgets/widgets/bec_progressbar/bec_progressbar.py,sha256=-poyvjmRSccVR7GNbiDoYfetfaBwDy2mimzMDGNyAU8,7949
|
80
|
+
bec_widgets/widgets/bec_progressbar/register_bec_progress_bar.py,sha256=ZcGLPYEwkrbSOpxQeuZJRRVV3yXvcFh133hnlKIQGKw,488
|
75
81
|
bec_widgets/widgets/bec_queue/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
76
82
|
bec_widgets/widgets/bec_queue/bec_queue.py,sha256=8gayhWyIB3_gdpeongGTDQxHs37Waf3H5TDjySCctho,7998
|
77
83
|
bec_widgets/widgets/bec_queue/bec_queue.pyproject,sha256=VhoNmAv1DQUl9dg7dELyf5i4pZ5k65N3GnqOYiSwbQo,27
|
@@ -156,7 +162,7 @@ bec_widgets/widgets/figure/plots/image/image_processor.py,sha256=GeTtWjbldy6VejM
|
|
156
162
|
bec_widgets/widgets/figure/plots/motor_map/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
157
163
|
bec_widgets/widgets/figure/plots/motor_map/motor_map.py,sha256=AiDq4bmcEoj9PlkRQtHCk-5cwvOFGPBcfxPNNr8E53Y,18399
|
158
164
|
bec_widgets/widgets/figure/plots/waveform/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
159
|
-
bec_widgets/widgets/figure/plots/waveform/waveform.py,sha256=
|
165
|
+
bec_widgets/widgets/figure/plots/waveform/waveform.py,sha256=aU_anZcC6Z17ZHsBPxPeC_0x5YRyJMP4BN7pWk0nG3E,56270
|
160
166
|
bec_widgets/widgets/figure/plots/waveform/waveform_curve.py,sha256=RUo4GfXwlaCei8BBvWBQF3IEB8h-KgpvaHur6JUvT6s,8682
|
161
167
|
bec_widgets/widgets/image/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
162
168
|
bec_widgets/widgets/image/bec_image_widget.pyproject,sha256=PHisdBo5_5UCApd27GkizzqgfdjsDx2bFZa_p9LiSW8,30
|
@@ -237,7 +243,7 @@ bec_widgets/widgets/waveform/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
|
|
237
243
|
bec_widgets/widgets/waveform/bec_waveform_widget.pyproject,sha256=GLD8GN9dXx9wNbtnevrxqqcwk7vKV-Uv8QYSycdaoaI,33
|
238
244
|
bec_widgets/widgets/waveform/bec_waveform_widget_plugin.py,sha256=qSQTeCzIUn8rgDkjIM47Rr3-fqg1uk8rDf_lCoY9gZw,1402
|
239
245
|
bec_widgets/widgets/waveform/register_bec_waveform_widget.py,sha256=qZHVZH_lP2hvzkG1Ra0EyrXlMeLkRCy0aceH-bfJ1cs,490
|
240
|
-
bec_widgets/widgets/waveform/waveform_widget.py,sha256=
|
246
|
+
bec_widgets/widgets/waveform/waveform_widget.py,sha256=jUyeGZCKKqtyk219Xkj_IMca-nvPELW23FqjMn-SMhc,24736
|
241
247
|
bec_widgets/widgets/waveform/waveform_popups/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
242
248
|
bec_widgets/widgets/waveform/waveform_popups/curve_dialog/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
243
249
|
bec_widgets/widgets/waveform/waveform_popups/curve_dialog/curve_dialog.py,sha256=VxbAtI_ZLfkrkTXqImQcNPwKDqFRWEj-vI8v6mmVMJ8,13025
|
@@ -249,8 +255,8 @@ bec_widgets/widgets/website/register_website_widget.py,sha256=LIQJpV9uqcBiPR9cEA
|
|
249
255
|
bec_widgets/widgets/website/website.py,sha256=42pncCc_zI2eqeMArIurVmPUukRo5bTxa2h1Skah-io,3012
|
250
256
|
bec_widgets/widgets/website/website_widget.pyproject,sha256=scOiV3cV1_BjbzpPzy2N8rIJL5P2qIZz8ObTJ-Uvdtg,25
|
251
257
|
bec_widgets/widgets/website/website_widget_plugin.py,sha256=pz38_C2cZ0yvPPS02wdIPcmhFo_yiwUhflsASocAPQQ,1341
|
252
|
-
bec_widgets-0.
|
253
|
-
bec_widgets-0.
|
254
|
-
bec_widgets-0.
|
255
|
-
bec_widgets-0.
|
256
|
-
bec_widgets-0.
|
258
|
+
bec_widgets-0.108.0.dist-info/METADATA,sha256=BGf3jlAjgMTSAg0eeqjTNolgh8gfWwzSsOdWxL-e8FY,1334
|
259
|
+
bec_widgets-0.108.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
260
|
+
bec_widgets-0.108.0.dist-info/entry_points.txt,sha256=3otEkCdDB9LZJuBLzG4pFLK5Di0CVybN_12IsZrQ-58,166
|
261
|
+
bec_widgets-0.108.0.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
|
262
|
+
bec_widgets-0.108.0.dist-info/RECORD,,
|
pyproject.toml
CHANGED
File without changes
|
File without changes
|
File without changes
|