bec-widgets 1.21.3__py3-none-any.whl → 1.22.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 +48 -0
- PKG-INFO +1 -1
- bec_widgets/qt_utils/round_frame.py +18 -41
- bec_widgets/qt_utils/toolbar.py +364 -81
- bec_widgets/utils/bec_widget.py +1 -1
- bec_widgets/utils/colors.py +58 -10
- bec_widgets/widgets/plots_next_gen/plot_base.py +52 -17
- bec_widgets/widgets/plots_next_gen/toolbar_bundles/mouse_interactions.py +21 -0
- bec_widgets/widgets/plots_next_gen/toolbar_bundles/roi_bundle.py +26 -0
- {bec_widgets-1.21.3.dist-info → bec_widgets-1.22.0.dist-info}/METADATA +1 -1
- {bec_widgets-1.21.3.dist-info → bec_widgets-1.22.0.dist-info}/RECORD +15 -14
- pyproject.toml +1 -1
- {bec_widgets-1.21.3.dist-info → bec_widgets-1.22.0.dist-info}/WHEEL +0 -0
- {bec_widgets-1.21.3.dist-info → bec_widgets-1.22.0.dist-info}/entry_points.txt +0 -0
- {bec_widgets-1.21.3.dist-info → bec_widgets-1.22.0.dist-info}/licenses/LICENSE +0 -0
CHANGELOG.md
CHANGED
@@ -1,6 +1,54 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
3
|
|
4
|
+
## v1.22.0 (2025-02-19)
|
5
|
+
|
6
|
+
### Bug Fixes
|
7
|
+
|
8
|
+
- **modular_toolbar**: Add action to an already existing bundle
|
9
|
+
([`4c4f159`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/4c4f1592c29974bb095c3c8325e93a1383efa289))
|
10
|
+
|
11
|
+
- **toolbar**: Qmenu Icons are visible
|
12
|
+
([`c2c0221`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/c2c022154bddc15d81eb55aad912d8fe1e34c698))
|
13
|
+
|
14
|
+
- **toolbar**: Update_separators logic updated, there cannot be two separators next to each other
|
15
|
+
([`facb8c3`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/facb8c30ffa3b12a97c7c68f8594b0354372ca17))
|
16
|
+
|
17
|
+
- **toolbar**: Widget actions are more compact
|
18
|
+
([`ef36a71`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/ef36a7124d54319c2cd592433c95e4f7513e982e))
|
19
|
+
|
20
|
+
### Features
|
21
|
+
|
22
|
+
- **toolbar**: Switchabletoolbarbutton
|
23
|
+
([`333570b`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/333570ba2fe67cb51fdbab17718003dfdb7f7b55))
|
24
|
+
|
25
|
+
### Refactoring
|
26
|
+
|
27
|
+
- **toolbar**: Added dark mode button for testing appearance for the toolbar example
|
28
|
+
([`6b08f7c`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/6b08f7cfb2115609a6dc6f681631ecfae23fa899))
|
29
|
+
|
30
|
+
### Testing
|
31
|
+
|
32
|
+
- **toolbar**: Blocking tests fixed
|
33
|
+
([`6ae33a2`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/6ae33a23a62eafb7c820e1fde9d6d91ec1796e55))
|
34
|
+
|
35
|
+
|
36
|
+
## v1.21.4 (2025-02-19)
|
37
|
+
|
38
|
+
### Bug Fixes
|
39
|
+
|
40
|
+
- **colors**: Pyqtgraph styling updated on the app level
|
41
|
+
([`ae18279`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/ae182796855719437bdf911c2e969e3f438d6982))
|
42
|
+
|
43
|
+
- **plot_base**: Mouse interactions default state fetch to toolbar
|
44
|
+
([`97c0ed5`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/97c0ed53df21053fef9811c3dea3b79020137030))
|
45
|
+
|
46
|
+
### Refactoring
|
47
|
+
|
48
|
+
- **plot_base**: Change the PlotWidget to GraphicalLayoutWidget
|
49
|
+
([`ff8e282`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/ff8e282034f0970b69cf0447fc5f88b4f30bf470))
|
50
|
+
|
51
|
+
|
4
52
|
## v1.21.3 (2025-02-19)
|
5
53
|
|
6
54
|
### Bug Fixes
|
PKG-INFO
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import pyqtgraph as pg
|
2
2
|
from qtpy.QtCore import Property
|
3
|
-
from qtpy.QtWidgets import QApplication, QFrame, QVBoxLayout, QWidget
|
3
|
+
from qtpy.QtWidgets import QApplication, QFrame, QHBoxLayout, QVBoxLayout, QWidget
|
4
4
|
|
5
5
|
from bec_widgets.utils.bec_widget import BECWidget
|
6
6
|
from bec_widgets.widgets.utility.visual.dark_mode_button.dark_mode_button import DarkModeButton
|
@@ -31,22 +31,20 @@ class RoundedFrame(BECWidget, QFrame):
|
|
31
31
|
# Apply rounded frame styling
|
32
32
|
self.setProperty("skip_settings", True)
|
33
33
|
self.setObjectName("roundedFrame")
|
34
|
-
self.update_style()
|
35
34
|
|
36
35
|
# Create a layout for the frame
|
37
|
-
layout =
|
38
|
-
layout.setContentsMargins(5, 5, 5, 5) # Set 5px margin
|
36
|
+
self.layout = QHBoxLayout(self)
|
37
|
+
self.layout.setContentsMargins(5, 5, 5, 5) # Set 5px margin
|
39
38
|
|
40
39
|
# Add the content widget to the layout
|
41
40
|
if content_widget:
|
42
|
-
layout.addWidget(content_widget)
|
41
|
+
self.layout.addWidget(content_widget)
|
43
42
|
|
44
43
|
# Store reference to the content widget
|
45
44
|
self.content_widget = content_widget
|
46
45
|
|
47
|
-
# Automatically apply initial styles to the
|
48
|
-
|
49
|
-
self.apply_plot_widget_style()
|
46
|
+
# Automatically apply initial styles to the GraphicalLayoutWidget if applicable
|
47
|
+
self.apply_plot_widget_style()
|
50
48
|
|
51
49
|
self._connect_to_theme_change()
|
52
50
|
|
@@ -65,10 +63,6 @@ class RoundedFrame(BECWidget, QFrame):
|
|
65
63
|
|
66
64
|
self.update_style()
|
67
65
|
|
68
|
-
# Update PlotWidget's background color and axis styles if applicable
|
69
|
-
if isinstance(self.content_widget, pg.PlotWidget):
|
70
|
-
self.apply_plot_widget_style()
|
71
|
-
|
72
66
|
@Property(int)
|
73
67
|
def radius(self):
|
74
68
|
"""Radius of the rounded corners."""
|
@@ -92,6 +86,7 @@ class RoundedFrame(BECWidget, QFrame):
|
|
92
86
|
}}
|
93
87
|
"""
|
94
88
|
)
|
89
|
+
self.apply_plot_widget_style()
|
95
90
|
|
96
91
|
def apply_plot_widget_style(self, border: str = "none"):
|
97
92
|
"""
|
@@ -100,35 +95,16 @@ class RoundedFrame(BECWidget, QFrame):
|
|
100
95
|
Args:
|
101
96
|
border (str): Border style (e.g., 'none', '1px solid red').
|
102
97
|
"""
|
103
|
-
if isinstance(self.content_widget, pg.
|
104
|
-
# Sync PlotWidget's background color with the RoundedFrame's background color
|
105
|
-
self.content_widget.setBackground(self.background_color)
|
106
|
-
|
107
|
-
# Calculate contrast-optimized axis and label colors
|
108
|
-
if self.background_color == "#e9ecef": # Light mode
|
109
|
-
label_color = "#000000"
|
110
|
-
axis_color = "#666666"
|
111
|
-
else: # Dark mode
|
112
|
-
label_color = "#FFFFFF"
|
113
|
-
axis_color = "#CCCCCC"
|
114
|
-
|
115
|
-
# Apply axis label and tick colors
|
116
|
-
plot_item = self.content_widget.getPlotItem()
|
117
|
-
for axis in ["left", "right", "top", "bottom"]:
|
118
|
-
plot_item.getAxis(axis).setPen(pg.mkPen(color=axis_color))
|
119
|
-
plot_item.getAxis(axis).setTextPen(pg.mkPen(color=label_color))
|
120
|
-
|
121
|
-
# Change title color
|
122
|
-
plot_item.titleLabel.setText(plot_item.titleLabel.text, color=label_color)
|
123
|
-
|
98
|
+
if isinstance(self.content_widget, pg.GraphicsLayoutWidget):
|
124
99
|
# Apply border style via stylesheet
|
125
100
|
self.content_widget.setStyleSheet(
|
126
101
|
f"""
|
127
|
-
|
102
|
+
GraphicsLayoutWidget {{
|
128
103
|
border: {border}; /* Explicitly set the border */
|
129
104
|
}}
|
130
105
|
"""
|
131
106
|
)
|
107
|
+
self.content_widget.setBackground(self.background_color)
|
132
108
|
|
133
109
|
|
134
110
|
class ExampleApp(QWidget): # pragma: no cover
|
@@ -142,26 +118,27 @@ class ExampleApp(QWidget): # pragma: no cover
|
|
142
118
|
dark_button = DarkModeButton()
|
143
119
|
|
144
120
|
# Create PlotWidgets
|
145
|
-
plot1 = pg.
|
146
|
-
|
121
|
+
plot1 = pg.GraphicsLayoutWidget()
|
122
|
+
plot_item_1 = pg.PlotItem()
|
123
|
+
plot_item_1.plot([1, 3, 2, 4, 6, 5], pen="r")
|
124
|
+
plot1.plot_item = plot_item_1
|
147
125
|
|
148
|
-
plot2 = pg.
|
149
|
-
|
126
|
+
plot2 = pg.GraphicsLayoutWidget()
|
127
|
+
plot_item_2 = pg.PlotItem()
|
128
|
+
plot_item_2.plot([1, 2, 4, 8, 16, 32], pen="r")
|
129
|
+
plot2.plot_item = plot_item_2
|
150
130
|
|
151
131
|
# Wrap PlotWidgets in RoundedFrame
|
152
132
|
rounded_plot1 = RoundedFrame(content_widget=plot1, theme_update=True)
|
153
133
|
rounded_plot2 = RoundedFrame(content_widget=plot2, theme_update=True)
|
154
|
-
round = RoundedFrame()
|
155
134
|
|
156
135
|
# Add to layout
|
157
136
|
layout.addWidget(dark_button)
|
158
137
|
layout.addWidget(rounded_plot1)
|
159
138
|
layout.addWidget(rounded_plot2)
|
160
|
-
layout.addWidget(round)
|
161
139
|
|
162
140
|
self.setLayout(layout)
|
163
141
|
|
164
|
-
# Simulate theme change after 2 seconds
|
165
142
|
from qtpy.QtCore import QTimer
|
166
143
|
|
167
144
|
def change_theme():
|
bec_widgets/qt_utils/toolbar.py
CHANGED
@@ -8,7 +8,7 @@ from collections import defaultdict
|
|
8
8
|
from typing import Dict, List, Literal, Tuple
|
9
9
|
|
10
10
|
from bec_qthemes._icon.material_icons import material_icon
|
11
|
-
from qtpy.QtCore import QSize, Qt
|
11
|
+
from qtpy.QtCore import QSize, Qt, QTimer
|
12
12
|
from qtpy.QtGui import QAction, QColor, QIcon
|
13
13
|
from qtpy.QtWidgets import (
|
14
14
|
QApplication,
|
@@ -18,15 +18,54 @@ from qtpy.QtWidgets import (
|
|
18
18
|
QMainWindow,
|
19
19
|
QMenu,
|
20
20
|
QSizePolicy,
|
21
|
+
QStyle,
|
21
22
|
QToolBar,
|
22
23
|
QToolButton,
|
24
|
+
QVBoxLayout,
|
23
25
|
QWidget,
|
24
26
|
)
|
25
27
|
|
26
28
|
import bec_widgets
|
29
|
+
from bec_widgets.utils.colors import set_theme
|
30
|
+
from bec_widgets.widgets.utility.visual.dark_mode_button.dark_mode_button import DarkModeButton
|
27
31
|
|
28
32
|
MODULE_PATH = os.path.dirname(bec_widgets.__file__)
|
29
33
|
|
34
|
+
# Ensure that icons are shown in menus (especially on macOS)
|
35
|
+
QApplication.setAttribute(Qt.AA_DontShowIconsInMenus, False)
|
36
|
+
|
37
|
+
|
38
|
+
class LongPressToolButton(QToolButton):
|
39
|
+
def __init__(self, *args, long_press_threshold=500, **kwargs):
|
40
|
+
super().__init__(*args, **kwargs)
|
41
|
+
self.long_press_threshold = long_press_threshold
|
42
|
+
self._long_press_timer = QTimer(self)
|
43
|
+
self._long_press_timer.setSingleShot(True)
|
44
|
+
self._long_press_timer.timeout.connect(self.handleLongPress)
|
45
|
+
self._pressed = False
|
46
|
+
self._longPressed = False
|
47
|
+
|
48
|
+
def mousePressEvent(self, event):
|
49
|
+
self._pressed = True
|
50
|
+
self._longPressed = False
|
51
|
+
self._long_press_timer.start(self.long_press_threshold)
|
52
|
+
super().mousePressEvent(event)
|
53
|
+
|
54
|
+
def mouseReleaseEvent(self, event):
|
55
|
+
self._pressed = False
|
56
|
+
if self._longPressed:
|
57
|
+
self._longPressed = False
|
58
|
+
self._long_press_timer.stop()
|
59
|
+
event.accept() # Prevent normal click action after a long press
|
60
|
+
return
|
61
|
+
self._long_press_timer.stop()
|
62
|
+
super().mouseReleaseEvent(event)
|
63
|
+
|
64
|
+
def handleLongPress(self):
|
65
|
+
if self._pressed:
|
66
|
+
self._longPressed = True
|
67
|
+
self.showMenu()
|
68
|
+
|
30
69
|
|
31
70
|
class ToolBarAction(ABC):
|
32
71
|
"""
|
@@ -84,6 +123,21 @@ class IconAction(ToolBarAction):
|
|
84
123
|
toolbar.addAction(self.action)
|
85
124
|
|
86
125
|
|
126
|
+
class QtIconAction(ToolBarAction):
|
127
|
+
def __init__(self, standard_icon, tooltip=None, checkable=False, parent=None):
|
128
|
+
super().__init__(icon_path=None, tooltip=tooltip, checkable=checkable)
|
129
|
+
self.standard_icon = standard_icon
|
130
|
+
self.icon = QApplication.style().standardIcon(standard_icon)
|
131
|
+
self.action = QAction(self.icon, self.tooltip, parent)
|
132
|
+
self.action.setCheckable(self.checkable)
|
133
|
+
|
134
|
+
def add_to_toolbar(self, toolbar, target):
|
135
|
+
toolbar.addAction(self.action)
|
136
|
+
|
137
|
+
def get_icon(self):
|
138
|
+
return self.icon
|
139
|
+
|
140
|
+
|
87
141
|
class MaterialIconAction(ToolBarAction):
|
88
142
|
"""
|
89
143
|
Action with a Material icon for the toolbar.
|
@@ -111,7 +165,7 @@ class MaterialIconAction(ToolBarAction):
|
|
111
165
|
self.icon_name = icon_name
|
112
166
|
self.filled = filled
|
113
167
|
self.color = color
|
114
|
-
# Generate the icon
|
168
|
+
# Generate the icon using the material_icon helper
|
115
169
|
self.icon = material_icon(
|
116
170
|
self.icon_name,
|
117
171
|
size=(20, 20),
|
@@ -119,7 +173,6 @@ class MaterialIconAction(ToolBarAction):
|
|
119
173
|
filled=self.filled,
|
120
174
|
color=self.color,
|
121
175
|
)
|
122
|
-
# Immediately create an QAction with the given parent
|
123
176
|
self.action = QAction(self.icon, self.tooltip, parent=parent)
|
124
177
|
self.action.setCheckable(self.checkable)
|
125
178
|
|
@@ -152,7 +205,7 @@ class DeviceSelectionAction(ToolBarAction):
|
|
152
205
|
device_combobox (DeviceComboBox): The combobox for selecting the device.
|
153
206
|
"""
|
154
207
|
|
155
|
-
def __init__(self, label: str, device_combobox):
|
208
|
+
def __init__(self, label: str | None = None, device_combobox=None):
|
156
209
|
super().__init__()
|
157
210
|
self.label = label
|
158
211
|
self.device_combobox = device_combobox
|
@@ -161,15 +214,99 @@ class DeviceSelectionAction(ToolBarAction):
|
|
161
214
|
def add_to_toolbar(self, toolbar, target):
|
162
215
|
widget = QWidget()
|
163
216
|
layout = QHBoxLayout(widget)
|
164
|
-
|
165
|
-
layout.
|
166
|
-
|
167
|
-
|
217
|
+
layout.setContentsMargins(0, 0, 0, 0)
|
218
|
+
layout.setSpacing(0)
|
219
|
+
if self.label is not None:
|
220
|
+
label = QLabel(f"{self.label}")
|
221
|
+
layout.addWidget(label)
|
222
|
+
if self.device_combobox is not None:
|
223
|
+
layout.addWidget(self.device_combobox)
|
224
|
+
toolbar.addWidget(widget)
|
168
225
|
|
169
226
|
def set_combobox_style(self, color: str):
|
170
227
|
self.device_combobox.setStyleSheet(f"QComboBox {{ background-color: {color}; }}")
|
171
228
|
|
172
229
|
|
230
|
+
class SwitchableToolBarAction(ToolBarAction):
|
231
|
+
"""
|
232
|
+
A split toolbar action that combines a main action and a drop-down menu for additional actions.
|
233
|
+
|
234
|
+
The main button displays the currently selected action's icon and tooltip. Clicking on the main button
|
235
|
+
triggers that action. Clicking on the drop-down arrow displays a menu with alternative actions. When an
|
236
|
+
alternative action is selected, it becomes the new default and its callback is immediately executed.
|
237
|
+
|
238
|
+
This design mimics the behavior seen in Adobe Photoshop or Affinity Designer toolbars.
|
239
|
+
|
240
|
+
Args:
|
241
|
+
actions (dict): A dictionary mapping a unique key to a ToolBarAction instance.
|
242
|
+
initial_action (str, optional): The key of the initial default action. If not provided, the first action is used.
|
243
|
+
tooltip (str, optional): An optional tooltip for the split action; if provided, it overrides the default action's tooltip.
|
244
|
+
checkable (bool, optional): Whether the action is checkable. Defaults to True.
|
245
|
+
parent (QWidget, optional): Parent widget for the underlying QAction.
|
246
|
+
"""
|
247
|
+
|
248
|
+
def __init__(
|
249
|
+
self,
|
250
|
+
actions: Dict[str, ToolBarAction],
|
251
|
+
initial_action: str = None,
|
252
|
+
tooltip: str = None,
|
253
|
+
checkable: bool = True,
|
254
|
+
parent=None,
|
255
|
+
):
|
256
|
+
super().__init__(icon_path=None, tooltip=tooltip, checkable=checkable)
|
257
|
+
self.actions = actions
|
258
|
+
self.current_key = initial_action if initial_action is not None else next(iter(actions))
|
259
|
+
self.parent = parent
|
260
|
+
self.checkable = checkable
|
261
|
+
self.main_button = None
|
262
|
+
self.menu_actions: Dict[str, QAction] = {}
|
263
|
+
|
264
|
+
def add_to_toolbar(self, toolbar: QToolBar, target: QWidget):
|
265
|
+
"""
|
266
|
+
Adds the split action to the toolbar.
|
267
|
+
|
268
|
+
Args:
|
269
|
+
toolbar (QToolBar): The toolbar to add the action to.
|
270
|
+
target (QWidget): The target widget for the action.
|
271
|
+
"""
|
272
|
+
self.main_button = LongPressToolButton(toolbar)
|
273
|
+
self.main_button.setPopupMode(QToolButton.MenuButtonPopup)
|
274
|
+
self.main_button.setCheckable(self.checkable)
|
275
|
+
default_action = self.actions[self.current_key]
|
276
|
+
self.main_button.setIcon(default_action.get_icon())
|
277
|
+
self.main_button.setToolTip(default_action.tooltip)
|
278
|
+
self.main_button.clicked.connect(self._trigger_current_action)
|
279
|
+
menu = QMenu(self.main_button)
|
280
|
+
self.menu_actions = {}
|
281
|
+
for key, action_obj in self.actions.items():
|
282
|
+
menu_action = QAction(action_obj.get_icon(), action_obj.tooltip, self.main_button)
|
283
|
+
menu_action.setIconVisibleInMenu(True)
|
284
|
+
menu_action.setCheckable(self.checkable)
|
285
|
+
menu_action.setChecked(key == self.current_key)
|
286
|
+
menu_action.triggered.connect(lambda checked, k=key: self._set_default_action(k))
|
287
|
+
menu.addAction(menu_action)
|
288
|
+
self.menu_actions[key] = menu_action
|
289
|
+
self.main_button.setMenu(menu)
|
290
|
+
toolbar.addWidget(self.main_button)
|
291
|
+
|
292
|
+
def _trigger_current_action(self):
|
293
|
+
action_obj = self.actions[self.current_key]
|
294
|
+
action_obj.action.trigger()
|
295
|
+
|
296
|
+
def _set_default_action(self, key: str):
|
297
|
+
self.current_key = key
|
298
|
+
new_action = self.actions[self.current_key]
|
299
|
+
self.main_button.setIcon(new_action.get_icon())
|
300
|
+
self.main_button.setToolTip(new_action.tooltip)
|
301
|
+
# Update check state of menu items
|
302
|
+
for k, menu_act in self.menu_actions.items():
|
303
|
+
menu_act.setChecked(k == key)
|
304
|
+
new_action.action.trigger()
|
305
|
+
|
306
|
+
def get_icon(self) -> QIcon:
|
307
|
+
return self.actions[self.current_key].get_icon()
|
308
|
+
|
309
|
+
|
173
310
|
class WidgetAction(ToolBarAction):
|
174
311
|
"""
|
175
312
|
Action for adding any widget to the toolbar.
|
@@ -180,15 +317,23 @@ class WidgetAction(ToolBarAction):
|
|
180
317
|
"""
|
181
318
|
|
182
319
|
def __init__(self, label: str | None = None, widget: QWidget = None, parent=None):
|
183
|
-
super().__init__(
|
320
|
+
super().__init__(icon_path=None, tooltip=label, checkable=False)
|
184
321
|
self.label = label
|
185
322
|
self.widget = widget
|
323
|
+
self.container = None
|
186
324
|
|
187
325
|
def add_to_toolbar(self, toolbar: QToolBar, target: QWidget):
|
188
|
-
|
189
|
-
|
326
|
+
"""
|
327
|
+
Adds the widget to the toolbar.
|
328
|
+
|
329
|
+
Args:
|
330
|
+
toolbar (QToolBar): The toolbar to add the widget to.
|
331
|
+
target (QWidget): The target widget for the action.
|
332
|
+
"""
|
333
|
+
self.container = QWidget()
|
334
|
+
layout = QHBoxLayout(self.container)
|
190
335
|
layout.setContentsMargins(0, 0, 0, 0)
|
191
|
-
layout.setSpacing(
|
336
|
+
layout.setSpacing(0)
|
192
337
|
|
193
338
|
if self.label is not None:
|
194
339
|
label_widget = QLabel(f"{self.label}")
|
@@ -209,19 +354,12 @@ class WidgetAction(ToolBarAction):
|
|
209
354
|
|
210
355
|
layout.addWidget(self.widget)
|
211
356
|
|
212
|
-
toolbar.addWidget(container)
|
357
|
+
toolbar.addWidget(self.container)
|
358
|
+
# Store the container as the action to allow toggling visibility.
|
359
|
+
self.action = self.container
|
213
360
|
|
214
361
|
@staticmethod
|
215
362
|
def calculate_minimum_width(combo_box: QComboBox) -> int:
|
216
|
-
"""
|
217
|
-
Calculate the minimum width required to display the longest item in the combo box.
|
218
|
-
|
219
|
-
Args:
|
220
|
-
combo_box (QComboBox): The combo box to calculate the width for.
|
221
|
-
|
222
|
-
Returns:
|
223
|
-
int: The calculated minimum width in pixels.
|
224
|
-
"""
|
225
363
|
font_metrics = combo_box.fontMetrics()
|
226
364
|
max_width = max(font_metrics.width(combo_box.itemText(i)) for i in range(combo_box.count()))
|
227
365
|
return max_width + 60
|
@@ -261,12 +399,15 @@ class ExpandableMenuAction(ToolBarAction):
|
|
261
399
|
menu = QMenu(button)
|
262
400
|
for action_id, action in self.actions.items():
|
263
401
|
sub_action = QAction(action.tooltip, target)
|
264
|
-
|
402
|
+
sub_action.setIconVisibleInMenu(True)
|
403
|
+
if action.icon_path:
|
265
404
|
icon = QIcon()
|
266
405
|
icon.addFile(action.icon_path, size=QSize(20, 20))
|
267
406
|
sub_action.setIcon(icon)
|
268
|
-
elif hasattr(action, "get_icon"):
|
269
|
-
|
407
|
+
elif hasattr(action, "get_icon") and callable(action.get_icon):
|
408
|
+
sub_icon = action.get_icon()
|
409
|
+
if sub_icon and not sub_icon.isNull():
|
410
|
+
sub_action.setIcon(sub_icon)
|
270
411
|
sub_action.setCheckable(action.checkable)
|
271
412
|
menu.addAction(sub_action)
|
272
413
|
self.widgets[action_id] = sub_action
|
@@ -289,7 +430,6 @@ class ToolbarBundle:
|
|
289
430
|
self.bundle_id = bundle_id
|
290
431
|
self._actions: dict[str, ToolBarAction] = {}
|
291
432
|
|
292
|
-
# If you passed in a list of tuples, load them into the dictionary
|
293
433
|
if actions is not None:
|
294
434
|
for action_id, action in actions:
|
295
435
|
self._actions[action_id] = action
|
@@ -331,7 +471,7 @@ class ModularToolBar(QToolBar):
|
|
331
471
|
actions (dict, optional): A dictionary of action creators to populate the toolbar. Defaults to None.
|
332
472
|
target_widget (QWidget, optional): The widget that the actions will target. Defaults to None.
|
333
473
|
orientation (Literal["horizontal", "vertical"], optional): The initial orientation of the toolbar. Defaults to "horizontal".
|
334
|
-
background_color (str, optional): The background color of the toolbar. Defaults to "rgba(0, 0, 0, 0)"
|
474
|
+
background_color (str, optional): The background color of the toolbar. Defaults to "rgba(0, 0, 0, 0)".
|
335
475
|
"""
|
336
476
|
|
337
477
|
def __init__(
|
@@ -378,7 +518,7 @@ class ModularToolBar(QToolBar):
|
|
378
518
|
Sets the background color and other appearance settings.
|
379
519
|
|
380
520
|
Args:
|
381
|
-
color(str): The background color of the toolbar.
|
521
|
+
color (str): The background color of the toolbar.
|
382
522
|
"""
|
383
523
|
self.setIconSize(QSize(20, 20))
|
384
524
|
self.setMovable(False)
|
@@ -402,100 +542,133 @@ class ModularToolBar(QToolBar):
|
|
402
542
|
|
403
543
|
def update_material_icon_colors(self, new_color: str | tuple | QColor):
|
404
544
|
"""
|
405
|
-
Updates the color of all MaterialIconAction icons
|
545
|
+
Updates the color of all MaterialIconAction icons.
|
406
546
|
|
407
547
|
Args:
|
408
|
-
new_color (str | tuple | QColor): The new color
|
548
|
+
new_color (str | tuple | QColor): The new color.
|
409
549
|
"""
|
410
550
|
for action in self.widgets.values():
|
411
551
|
if isinstance(action, MaterialIconAction):
|
412
552
|
action.color = new_color
|
413
|
-
# Refresh the icon
|
414
553
|
updated_icon = action.get_icon()
|
415
554
|
action.action.setIcon(updated_icon)
|
416
555
|
|
417
556
|
def add_action(self, action_id: str, action: ToolBarAction, target_widget: QWidget):
|
418
557
|
"""
|
419
|
-
Adds a new standalone action
|
558
|
+
Adds a new standalone action dynamically.
|
420
559
|
|
421
560
|
Args:
|
422
|
-
action_id (str): Unique identifier
|
423
|
-
action (ToolBarAction): The action to add
|
424
|
-
target_widget (QWidget): The target widget
|
561
|
+
action_id (str): Unique identifier.
|
562
|
+
action (ToolBarAction): The action to add.
|
563
|
+
target_widget (QWidget): The target widget.
|
425
564
|
"""
|
426
565
|
if action_id in self.widgets:
|
427
566
|
raise ValueError(f"Action with ID '{action_id}' already exists.")
|
428
567
|
action.add_to_toolbar(self, target_widget)
|
429
568
|
self.widgets[action_id] = action
|
430
569
|
self.toolbar_items.append(("action", action_id))
|
431
|
-
self.update_separators()
|
570
|
+
self.update_separators()
|
432
571
|
|
433
572
|
def hide_action(self, action_id: str):
|
434
573
|
"""
|
435
|
-
Hides a specific action
|
574
|
+
Hides a specific action.
|
436
575
|
|
437
576
|
Args:
|
438
|
-
action_id (str): Unique identifier
|
577
|
+
action_id (str): Unique identifier.
|
439
578
|
"""
|
440
579
|
if action_id not in self.widgets:
|
441
580
|
raise ValueError(f"Action with ID '{action_id}' does not exist.")
|
442
581
|
action = self.widgets[action_id]
|
443
|
-
if hasattr(action, "action") and
|
582
|
+
if hasattr(action, "action") and action.action is not None:
|
444
583
|
action.action.setVisible(False)
|
445
|
-
self.update_separators()
|
584
|
+
self.update_separators()
|
446
585
|
|
447
586
|
def show_action(self, action_id: str):
|
448
587
|
"""
|
449
|
-
Shows a specific action
|
588
|
+
Shows a specific action.
|
450
589
|
|
451
590
|
Args:
|
452
|
-
action_id (str): Unique identifier
|
591
|
+
action_id (str): Unique identifier.
|
453
592
|
"""
|
454
593
|
if action_id not in self.widgets:
|
455
594
|
raise ValueError(f"Action with ID '{action_id}' does not exist.")
|
456
595
|
action = self.widgets[action_id]
|
457
|
-
if hasattr(action, "action") and
|
596
|
+
if hasattr(action, "action") and action.action is not None:
|
458
597
|
action.action.setVisible(True)
|
459
|
-
self.update_separators()
|
598
|
+
self.update_separators()
|
460
599
|
|
461
600
|
def add_bundle(self, bundle: ToolbarBundle, target_widget: QWidget):
|
462
601
|
"""
|
463
|
-
Adds a bundle of actions
|
602
|
+
Adds a bundle of actions, separated by a separator.
|
464
603
|
|
465
604
|
Args:
|
466
|
-
bundle (ToolbarBundle): The bundle
|
467
|
-
target_widget (QWidget): The target widget
|
605
|
+
bundle (ToolbarBundle): The bundle.
|
606
|
+
target_widget (QWidget): The target widget.
|
468
607
|
"""
|
469
608
|
if bundle.bundle_id in self.bundles:
|
470
609
|
raise ValueError(f"ToolbarBundle with ID '{bundle.bundle_id}' already exists.")
|
471
610
|
|
472
|
-
# Add a separator before the bundle (but not to first one)
|
473
611
|
if self.toolbar_items:
|
474
612
|
sep = SeparatorAction()
|
475
613
|
sep.add_to_toolbar(self, target_widget)
|
476
614
|
self.toolbar_items.append(("separator", None))
|
477
615
|
|
478
|
-
# Add each action in the bundle
|
479
616
|
for action_id, action_obj in bundle.actions.items():
|
480
617
|
action_obj.add_to_toolbar(self, target_widget)
|
481
618
|
self.widgets[action_id] = action_obj
|
482
619
|
|
483
|
-
# Register the bundle
|
484
620
|
self.bundles[bundle.bundle_id] = list(bundle.actions.keys())
|
485
621
|
self.toolbar_items.append(("bundle", bundle.bundle_id))
|
622
|
+
self.update_separators()
|
623
|
+
|
624
|
+
def add_action_to_bundle(self, bundle_id: str, action_id: str, action, target_widget: QWidget):
|
625
|
+
"""
|
626
|
+
Dynamically adds an action to an existing bundle.
|
627
|
+
|
628
|
+
Args:
|
629
|
+
bundle_id (str): The bundle ID.
|
630
|
+
action_id (str): Unique identifier.
|
631
|
+
action (ToolBarAction): The action to add.
|
632
|
+
target_widget (QWidget): The target widget.
|
633
|
+
"""
|
634
|
+
if bundle_id not in self.bundles:
|
635
|
+
raise ValueError(f"Bundle '{bundle_id}' does not exist.")
|
636
|
+
if action_id in self.widgets:
|
637
|
+
raise ValueError(f"Action with ID '{action_id}' already exists.")
|
638
|
+
|
639
|
+
action.add_to_toolbar(self, target_widget)
|
640
|
+
new_qaction = action.action
|
641
|
+
self.removeAction(new_qaction)
|
642
|
+
|
643
|
+
bundle_action_ids = self.bundles[bundle_id]
|
644
|
+
if bundle_action_ids:
|
645
|
+
last_bundle_action = self.widgets[bundle_action_ids[-1]].action
|
646
|
+
actions_list = self.actions()
|
647
|
+
try:
|
648
|
+
index = actions_list.index(last_bundle_action)
|
649
|
+
except ValueError:
|
650
|
+
self.addAction(new_qaction)
|
651
|
+
else:
|
652
|
+
if index + 1 < len(actions_list):
|
653
|
+
before_action = actions_list[index + 1]
|
654
|
+
self.insertAction(before_action, new_qaction)
|
655
|
+
else:
|
656
|
+
self.addAction(new_qaction)
|
657
|
+
else:
|
658
|
+
self.addAction(new_qaction)
|
486
659
|
|
487
|
-
self.
|
660
|
+
self.widgets[action_id] = action
|
661
|
+
self.bundles[bundle_id].append(action_id)
|
662
|
+
self.update_separators()
|
488
663
|
|
489
664
|
def contextMenuEvent(self, event):
|
490
665
|
"""
|
491
|
-
Overrides the context menu event to show
|
666
|
+
Overrides the context menu event to show toolbar actions with checkboxes and icons.
|
492
667
|
|
493
668
|
Args:
|
494
|
-
event(QContextMenuEvent): The context menu event.
|
669
|
+
event (QContextMenuEvent): The context menu event.
|
495
670
|
"""
|
496
671
|
menu = QMenu(self)
|
497
|
-
|
498
|
-
# Iterate through the toolbar items in order
|
499
672
|
for item_type, identifier in self.toolbar_items:
|
500
673
|
if item_type == "separator":
|
501
674
|
menu.addSeparator()
|
@@ -503,18 +676,16 @@ class ModularToolBar(QToolBar):
|
|
503
676
|
self.handle_bundle_context_menu(menu, identifier)
|
504
677
|
elif item_type == "action":
|
505
678
|
self.handle_action_context_menu(menu, identifier)
|
506
|
-
|
507
|
-
# Connect the triggered signal after all actions are added
|
508
679
|
menu.triggered.connect(self.handle_menu_triggered)
|
509
680
|
menu.exec_(event.globalPos())
|
510
681
|
|
511
682
|
def handle_bundle_context_menu(self, menu: QMenu, bundle_id: str):
|
512
683
|
"""
|
513
|
-
Adds
|
684
|
+
Adds bundle actions to the context menu.
|
514
685
|
|
515
686
|
Args:
|
516
|
-
menu (QMenu): The context menu
|
517
|
-
bundle_id (str): The identifier
|
687
|
+
menu (QMenu): The context menu.
|
688
|
+
bundle_id (str): The bundle identifier.
|
518
689
|
"""
|
519
690
|
action_ids = self.bundles.get(bundle_id, [])
|
520
691
|
for act_id in action_ids:
|
@@ -535,7 +706,6 @@ class ModularToolBar(QToolBar):
|
|
535
706
|
# Set the icon if available
|
536
707
|
if qaction.icon() and not qaction.icon().isNull():
|
537
708
|
menu_action.setIcon(qaction.icon())
|
538
|
-
|
539
709
|
menu.addAction(menu_action)
|
540
710
|
|
541
711
|
def handle_action_context_menu(self, menu: QMenu, action_id: str):
|
@@ -565,73 +735,95 @@ class ModularToolBar(QToolBar):
|
|
565
735
|
menu.addAction(menu_action)
|
566
736
|
|
567
737
|
def handle_menu_triggered(self, action):
|
568
|
-
"""
|
738
|
+
"""
|
739
|
+
Handles the triggered signal from the context menu.
|
740
|
+
|
741
|
+
Args:
|
742
|
+
action: Action triggered.
|
743
|
+
"""
|
569
744
|
action_id = action.data()
|
570
745
|
if action_id:
|
571
746
|
self.toggle_action_visibility(action_id, action.isChecked())
|
572
747
|
|
573
748
|
def toggle_action_visibility(self, action_id: str, visible: bool):
|
574
749
|
"""
|
575
|
-
Toggles the visibility of a specific action
|
750
|
+
Toggles the visibility of a specific action.
|
576
751
|
|
577
752
|
Args:
|
578
|
-
action_id(str): Unique identifier
|
579
|
-
visible(bool): Whether the action should be visible.
|
753
|
+
action_id (str): Unique identifier.
|
754
|
+
visible (bool): Whether the action should be visible.
|
580
755
|
"""
|
581
756
|
if action_id not in self.widgets:
|
582
757
|
return
|
583
|
-
|
584
758
|
tool_action = self.widgets[action_id]
|
585
|
-
if hasattr(tool_action, "action") and
|
759
|
+
if hasattr(tool_action, "action") and tool_action.action is not None:
|
586
760
|
tool_action.action.setVisible(visible)
|
587
761
|
self.update_separators()
|
588
762
|
|
589
763
|
def update_separators(self):
|
590
764
|
"""
|
591
|
-
Hide separators that are adjacent to another separator or have no actions
|
765
|
+
Hide separators that are adjacent to another separator or have no non-separator actions between them.
|
592
766
|
"""
|
593
767
|
toolbar_actions = self.actions()
|
594
|
-
|
768
|
+
# First pass: set visibility based on surrounding non-separator actions.
|
595
769
|
for i, action in enumerate(toolbar_actions):
|
596
770
|
if not action.isSeparator():
|
597
771
|
continue
|
598
|
-
# Find the previous visible action
|
599
772
|
prev_visible = None
|
600
773
|
for j in range(i - 1, -1, -1):
|
601
774
|
if toolbar_actions[j].isVisible():
|
602
775
|
prev_visible = toolbar_actions[j]
|
603
776
|
break
|
604
|
-
|
605
|
-
# Find the next visible action
|
606
777
|
next_visible = None
|
607
778
|
for j in range(i + 1, len(toolbar_actions)):
|
608
779
|
if toolbar_actions[j].isVisible():
|
609
780
|
next_visible = toolbar_actions[j]
|
610
781
|
break
|
611
|
-
|
612
|
-
# Determine if the separator should be hidden
|
613
|
-
# Hide if both previous and next visible actions are separators or non-existent
|
614
782
|
if (prev_visible is None or prev_visible.isSeparator()) and (
|
615
783
|
next_visible is None or next_visible.isSeparator()
|
616
784
|
):
|
617
785
|
action.setVisible(False)
|
618
786
|
else:
|
619
787
|
action.setVisible(True)
|
788
|
+
# Second pass: ensure no two visible separators are adjacent.
|
789
|
+
prev = None
|
790
|
+
for action in toolbar_actions:
|
791
|
+
if action.isVisible() and action.isSeparator():
|
792
|
+
if prev and prev.isSeparator():
|
793
|
+
action.setVisible(False)
|
794
|
+
else:
|
795
|
+
prev = action
|
796
|
+
else:
|
797
|
+
if action.isVisible():
|
798
|
+
prev = action
|
620
799
|
|
621
800
|
|
622
801
|
class MainWindow(QMainWindow): # pragma: no cover
|
623
802
|
def __init__(self):
|
624
803
|
super().__init__()
|
625
804
|
self.setWindowTitle("Toolbar / ToolbarBundle Demo")
|
626
|
-
|
627
805
|
self.central_widget = QWidget()
|
628
806
|
self.setCentralWidget(self.central_widget)
|
807
|
+
self.test_label = QLabel(text="This is a test label.")
|
808
|
+
self.central_widget.layout = QVBoxLayout(self.central_widget)
|
809
|
+
self.central_widget.layout.addWidget(self.test_label)
|
629
810
|
|
630
|
-
# Create a modular toolbar
|
631
811
|
self.toolbar = ModularToolBar(parent=self, target_widget=self)
|
632
812
|
self.addToolBar(self.toolbar)
|
633
813
|
|
634
|
-
|
814
|
+
self.add_switchable_button_checkable()
|
815
|
+
self.add_switchable_button_non_checkable()
|
816
|
+
self.add_widget_actions()
|
817
|
+
self.add_bundles()
|
818
|
+
self.add_menus()
|
819
|
+
|
820
|
+
# For theme testing
|
821
|
+
|
822
|
+
self.dark_button = DarkModeButton(toolbar=True)
|
823
|
+
dark_mode_action = WidgetAction(label=None, widget=self.dark_button)
|
824
|
+
self.toolbar.add_action("dark_mode", dark_mode_action, self)
|
825
|
+
|
826
|
+
def add_bundles(self):
|
635
827
|
home_action = MaterialIconAction(
|
636
828
|
icon_name="home", tooltip="Home", checkable=True, parent=self
|
637
829
|
)
|
@@ -651,12 +843,11 @@ class MainWindow(QMainWindow): # pragma: no cover
|
|
651
843
|
)
|
652
844
|
self.toolbar.add_bundle(main_actions_bundle, target_widget=self)
|
653
845
|
|
654
|
-
# Another bundle
|
655
846
|
search_action = MaterialIconAction(
|
656
|
-
icon_name="search", tooltip="Search", checkable=
|
847
|
+
icon_name="search", tooltip="Search", checkable=False, parent=self
|
657
848
|
)
|
658
849
|
help_action = MaterialIconAction(
|
659
|
-
icon_name="help", tooltip="Help", checkable=
|
850
|
+
icon_name="help", tooltip="Help", checkable=False, parent=self
|
660
851
|
)
|
661
852
|
second_bundle = ToolbarBundle(
|
662
853
|
bundle_id="secondary_actions",
|
@@ -664,9 +855,101 @@ class MainWindow(QMainWindow): # pragma: no cover
|
|
664
855
|
)
|
665
856
|
self.toolbar.add_bundle(second_bundle, target_widget=self)
|
666
857
|
|
858
|
+
new_action = MaterialIconAction(
|
859
|
+
icon_name="counter_1", tooltip="New Action", checkable=True, parent=self
|
860
|
+
)
|
861
|
+
self.toolbar.add_action_to_bundle(
|
862
|
+
"main_actions", "new_action", new_action, target_widget=self
|
863
|
+
)
|
864
|
+
|
865
|
+
def add_menus(self):
|
866
|
+
menu_material_actions = {
|
867
|
+
"mat1": MaterialIconAction(
|
868
|
+
icon_name="home", tooltip="Material Home", checkable=True, parent=self
|
869
|
+
),
|
870
|
+
"mat2": MaterialIconAction(
|
871
|
+
icon_name="settings", tooltip="Material Settings", checkable=True, parent=self
|
872
|
+
),
|
873
|
+
"mat3": MaterialIconAction(
|
874
|
+
icon_name="info", tooltip="Material Info", checkable=True, parent=self
|
875
|
+
),
|
876
|
+
}
|
877
|
+
menu_qt_actions = {
|
878
|
+
"qt1": QtIconAction(
|
879
|
+
standard_icon=QStyle.SP_FileIcon, tooltip="Qt File", checkable=True, parent=self
|
880
|
+
),
|
881
|
+
"qt2": QtIconAction(
|
882
|
+
standard_icon=QStyle.SP_DirIcon, tooltip="Qt Directory", checkable=True, parent=self
|
883
|
+
),
|
884
|
+
"qt3": QtIconAction(
|
885
|
+
standard_icon=QStyle.SP_TrashIcon, tooltip="Qt Trash", checkable=True, parent=self
|
886
|
+
),
|
887
|
+
}
|
888
|
+
expandable_menu_material = ExpandableMenuAction(
|
889
|
+
label="Material Menu", actions=menu_material_actions
|
890
|
+
)
|
891
|
+
expandable_menu_qt = ExpandableMenuAction(label="Qt Menu", actions=menu_qt_actions)
|
892
|
+
|
893
|
+
self.toolbar.add_action("material_menu", expandable_menu_material, self)
|
894
|
+
self.toolbar.add_action("qt_menu", expandable_menu_qt, self)
|
895
|
+
|
896
|
+
def add_switchable_button_checkable(self):
|
897
|
+
action1 = MaterialIconAction(
|
898
|
+
icon_name="counter_1", tooltip="Action 1", checkable=True, parent=self
|
899
|
+
)
|
900
|
+
action2 = MaterialIconAction(
|
901
|
+
icon_name="counter_2", tooltip="Action 2", checkable=True, parent=self
|
902
|
+
)
|
903
|
+
|
904
|
+
switchable_action = SwitchableToolBarAction(
|
905
|
+
actions={"action1": action1, "action2": action2},
|
906
|
+
initial_action="action1",
|
907
|
+
tooltip="Switchable Action",
|
908
|
+
checkable=True,
|
909
|
+
parent=self,
|
910
|
+
)
|
911
|
+
self.toolbar.add_action("switchable_action", switchable_action, self)
|
912
|
+
|
913
|
+
action1.action.toggled.connect(
|
914
|
+
lambda checked: self.test_label.setText(f"Action 1 triggered, checked = {checked}")
|
915
|
+
)
|
916
|
+
action2.action.toggled.connect(
|
917
|
+
lambda checked: self.test_label.setText(f"Action 2 triggered, checked = {checked}")
|
918
|
+
)
|
919
|
+
|
920
|
+
def add_switchable_button_non_checkable(self):
|
921
|
+
action1 = MaterialIconAction(
|
922
|
+
icon_name="counter_1", tooltip="Action 1", checkable=False, parent=self
|
923
|
+
)
|
924
|
+
action2 = MaterialIconAction(
|
925
|
+
icon_name="counter_2", tooltip="Action 2", checkable=False, parent=self
|
926
|
+
)
|
927
|
+
|
928
|
+
switchable_action = SwitchableToolBarAction(
|
929
|
+
actions={"action1": action1, "action2": action2},
|
930
|
+
initial_action="action1",
|
931
|
+
tooltip="Switchable Action",
|
932
|
+
checkable=False,
|
933
|
+
parent=self,
|
934
|
+
)
|
935
|
+
self.toolbar.add_action("switchable_action_no_toggle", switchable_action, self)
|
936
|
+
|
937
|
+
action1.action.triggered.connect(
|
938
|
+
lambda checked: self.test_label.setText(f"Action 1 triggered, checked = {checked}")
|
939
|
+
)
|
940
|
+
action2.action.triggered.connect(
|
941
|
+
lambda checked: self.test_label.setText(f"Action 2 triggered, checked = {checked}")
|
942
|
+
)
|
943
|
+
|
944
|
+
def add_widget_actions(self):
|
945
|
+
combo = QComboBox()
|
946
|
+
combo.addItems(["Option 1", "Option 2", "Option 3"])
|
947
|
+
self.toolbar.add_action("device_combo", WidgetAction(label="Device:", widget=combo), self)
|
948
|
+
|
667
949
|
|
668
950
|
if __name__ == "__main__": # pragma: no cover
|
669
951
|
app = QApplication(sys.argv)
|
952
|
+
set_theme("light")
|
670
953
|
main_window = MainWindow()
|
671
954
|
main_window.show()
|
672
955
|
sys.exit(app.exec_())
|
bec_widgets/utils/bec_widget.py
CHANGED
@@ -66,7 +66,7 @@ class BECWidget(BECConnector):
|
|
66
66
|
if hasattr(qapp, "theme_signal"):
|
67
67
|
qapp.theme_signal.theme_updated.connect(self._update_theme)
|
68
68
|
|
69
|
-
def _update_theme(self, theme: str):
|
69
|
+
def _update_theme(self, theme: str | None = None):
|
70
70
|
"""Update the theme."""
|
71
71
|
if theme is None:
|
72
72
|
qapp = QApplication.instance()
|
bec_widgets/utils/colors.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
import itertools
|
4
3
|
import re
|
5
4
|
from typing import TYPE_CHECKING, Literal
|
6
5
|
|
@@ -71,15 +70,64 @@ def apply_theme(theme: Literal["dark", "light"]):
|
|
71
70
|
Apply the theme to all pyqtgraph widgets. Do not use this function directly. Use set_theme instead.
|
72
71
|
"""
|
73
72
|
app = QApplication.instance()
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
73
|
+
graphic_layouts = [
|
74
|
+
child
|
75
|
+
for top in app.topLevelWidgets()
|
76
|
+
for child in top.findChildren(pg.GraphicsLayoutWidget)
|
77
|
+
]
|
78
|
+
|
79
|
+
plot_items = [
|
80
|
+
item
|
81
|
+
for gl in graphic_layouts
|
82
|
+
for item in gl.ci.items.keys() # ci is internal pg.GraphicsLayout that hosts all items
|
83
|
+
if isinstance(item, pg.PlotItem)
|
84
|
+
]
|
85
|
+
|
86
|
+
histograms = [
|
87
|
+
item
|
88
|
+
for gl in graphic_layouts
|
89
|
+
for item in gl.ci.items.keys() # ci is internal pg.GraphicsLayout that hosts all items
|
90
|
+
if isinstance(item, pg.HistogramLUTItem)
|
91
|
+
]
|
92
|
+
|
93
|
+
# Update background color based on the theme
|
94
|
+
if theme == "light":
|
95
|
+
background_color = "#e9ecef" # Subtle contrast for light mode
|
96
|
+
foreground_color = "#141414"
|
97
|
+
label_color = "#000000"
|
98
|
+
axis_color = "#666666"
|
99
|
+
else:
|
100
|
+
background_color = "#141414" # Dark mode
|
101
|
+
foreground_color = "#e9ecef"
|
102
|
+
label_color = "#FFFFFF"
|
103
|
+
axis_color = "#CCCCCC"
|
104
|
+
|
105
|
+
# update GraphicsLayoutWidget
|
106
|
+
pg.setConfigOptions(foreground=foreground_color, background=background_color)
|
107
|
+
for pg_widget in graphic_layouts:
|
108
|
+
pg_widget.setBackground(background_color)
|
109
|
+
|
110
|
+
# update PlotItems
|
111
|
+
for plot_item in plot_items:
|
112
|
+
for axis in ["left", "right", "top", "bottom"]:
|
113
|
+
plot_item.getAxis(axis).setPen(pg.mkPen(color=axis_color))
|
114
|
+
plot_item.getAxis(axis).setTextPen(pg.mkPen(color=label_color))
|
115
|
+
|
116
|
+
# Change title color
|
117
|
+
plot_item.titleLabel.setText(plot_item.titleLabel.text, color=label_color)
|
118
|
+
|
119
|
+
# Change legend color
|
120
|
+
if hasattr(plot_item, "legend") and plot_item.legend is not None:
|
121
|
+
plot_item.legend.setLabelTextColor(label_color)
|
122
|
+
# if legend is in plot item and theme is changed, has to be like that because of pg opt logic
|
123
|
+
for sample, label in plot_item.legend.items:
|
124
|
+
label_text = label.text
|
125
|
+
label.setText(label_text, color=label_color)
|
126
|
+
|
127
|
+
# update HistogramLUTItem
|
128
|
+
for histogram in histograms:
|
129
|
+
histogram.axis.setPen(pg.mkPen(color=axis_color))
|
130
|
+
histogram.axis.setTextPen(pg.mkPen(color=label_color))
|
83
131
|
|
84
132
|
# now define stylesheet according to theme and apply it
|
85
133
|
style = bec_qthemes.load_stylesheet(theme)
|
@@ -11,7 +11,6 @@ from bec_widgets.qt_utils.side_panel import SidePanel
|
|
11
11
|
from bec_widgets.qt_utils.toolbar import MaterialIconAction, ModularToolBar, SeparatorAction
|
12
12
|
from bec_widgets.utils import ConnectionConfig, Crosshair, EntryValidator
|
13
13
|
from bec_widgets.utils.bec_widget import BECWidget
|
14
|
-
from bec_widgets.utils.colors import set_theme
|
15
14
|
from bec_widgets.utils.fps_counter import FPSCounter
|
16
15
|
from bec_widgets.utils.widget_state_manager import WidgetStateManager
|
17
16
|
from bec_widgets.widgets.containers.layout_manager.layout_manager import LayoutManagerWidget
|
@@ -20,7 +19,7 @@ from bec_widgets.widgets.plots_next_gen.toolbar_bundles.mouse_interactions impor
|
|
20
19
|
MouseInteractionToolbarBundle,
|
21
20
|
)
|
22
21
|
from bec_widgets.widgets.plots_next_gen.toolbar_bundles.plot_export import PlotExportBundle
|
23
|
-
from bec_widgets.widgets.plots_next_gen.toolbar_bundles.
|
22
|
+
from bec_widgets.widgets.plots_next_gen.toolbar_bundles.roi_bundle import ROIBundle
|
24
23
|
from bec_widgets.widgets.utility.visual.dark_mode_button.dark_mode_button import DarkModeButton
|
25
24
|
|
26
25
|
logger = bec_logger.logger
|
@@ -83,8 +82,9 @@ class PlotBase(BECWidget, QWidget):
|
|
83
82
|
self.entry_validator = EntryValidator(self.dev)
|
84
83
|
|
85
84
|
# Base widgets elements
|
85
|
+
self.plot_widget = pg.GraphicsLayoutWidget(parent=self)
|
86
86
|
self.plot_item = pg.PlotItem(viewBox=BECViewBox(enableMenu=True))
|
87
|
-
self.plot_widget
|
87
|
+
self.plot_widget.addItem(self.plot_item)
|
88
88
|
self.side_panel = SidePanel(self, orientation="left", panel_max_width=280)
|
89
89
|
self.toolbar = ModularToolBar(target_widget=self, orientation="horizontal")
|
90
90
|
self.init_toolbar()
|
@@ -94,13 +94,20 @@ class PlotBase(BECWidget, QWidget):
|
|
94
94
|
self.crosshair = None
|
95
95
|
self.fps_monitor = None
|
96
96
|
self.fps_label = QLabel(alignment=Qt.AlignmentFlag.AlignRight)
|
97
|
+
self._user_x_label = ""
|
98
|
+
self._x_label_suffix = ""
|
97
99
|
|
98
100
|
self._init_ui()
|
99
101
|
|
102
|
+
self._connect_to_theme_change()
|
103
|
+
self._update_theme()
|
104
|
+
|
105
|
+
def apply_theme(self, theme: str):
|
106
|
+
self.round_plot_widget.apply_theme(theme)
|
107
|
+
|
100
108
|
def _init_ui(self):
|
101
109
|
self.layout.addWidget(self.layout_manager)
|
102
110
|
self.round_plot_widget = RoundedFrame(content_widget=self.plot_widget, theme_update=True)
|
103
|
-
self.round_plot_widget.apply_theme("dark")
|
104
111
|
|
105
112
|
self.layout_manager.add_widget(self.round_plot_widget)
|
106
113
|
self.layout_manager.add_widget_relative(self.fps_label, self.round_plot_widget, "top")
|
@@ -117,19 +124,15 @@ class PlotBase(BECWidget, QWidget):
|
|
117
124
|
|
118
125
|
self.plot_export_bundle = PlotExportBundle("plot_export", target_widget=self)
|
119
126
|
self.mouse_bundle = MouseInteractionToolbarBundle("mouse_interaction", target_widget=self)
|
120
|
-
self.state_export_bundle = SaveStateBundle("state_export", target_widget=self)
|
127
|
+
# self.state_export_bundle = SaveStateBundle("state_export", target_widget=self) #TODO ATM disabled, cannot be used in DockArea, which is exposed to the user
|
128
|
+
self.roi_bundle = ROIBundle("roi", target_widget=self)
|
121
129
|
|
122
130
|
# Add elements to toolbar
|
123
131
|
self.toolbar.add_bundle(self.plot_export_bundle, target_widget=self)
|
124
|
-
self.toolbar.add_bundle(self.state_export_bundle, target_widget=self)
|
132
|
+
# self.toolbar.add_bundle(self.state_export_bundle, target_widget=self) #TODO ATM disabled, cannot be used in DockArea, which is exposed to the user
|
125
133
|
self.toolbar.add_bundle(self.mouse_bundle, target_widget=self)
|
134
|
+
self.toolbar.add_bundle(self.roi_bundle, target_widget=self)
|
126
135
|
|
127
|
-
self.toolbar.add_action("separator_0", SeparatorAction(), target_widget=self)
|
128
|
-
self.toolbar.add_action(
|
129
|
-
"crosshair",
|
130
|
-
MaterialIconAction(icon_name="point_scan", tooltip="Show Crosshair", checkable=True),
|
131
|
-
target_widget=self,
|
132
|
-
)
|
133
136
|
self.toolbar.add_action("separator_1", SeparatorAction(), target_widget=self)
|
134
137
|
self.toolbar.add_action(
|
135
138
|
"fps_monitor",
|
@@ -141,7 +144,6 @@ class PlotBase(BECWidget, QWidget):
|
|
141
144
|
self.toolbar.widgets["fps_monitor"].action.toggled.connect(
|
142
145
|
lambda checked: setattr(self, "enable_fps_monitor", checked)
|
143
146
|
)
|
144
|
-
self.toolbar.widgets["crosshair"].action.toggled.connect(self.toggle_crosshair)
|
145
147
|
|
146
148
|
def add_side_menus(self):
|
147
149
|
"""Adds multiple menus to the side panel."""
|
@@ -256,12 +258,45 @@ class PlotBase(BECWidget, QWidget):
|
|
256
258
|
|
257
259
|
@SafeProperty(str, doc="The text of the x label")
|
258
260
|
def x_label(self) -> str:
|
259
|
-
return self.
|
261
|
+
return self._user_x_label
|
260
262
|
|
261
263
|
@x_label.setter
|
262
264
|
def x_label(self, value: str):
|
263
|
-
self.
|
264
|
-
self.
|
265
|
+
self._user_x_label = value
|
266
|
+
self._apply_x_label()
|
267
|
+
self.property_changed.emit("x_label", self._user_x_label)
|
268
|
+
|
269
|
+
@property
|
270
|
+
def x_label_suffix(self) -> str:
|
271
|
+
"""
|
272
|
+
A read-only (or internal) suffix automatically appended to the user label.
|
273
|
+
Not settable by the user directly from the UI.
|
274
|
+
"""
|
275
|
+
return self._x_label_suffix
|
276
|
+
|
277
|
+
def set_x_label_suffix(self, suffix: str):
|
278
|
+
"""
|
279
|
+
Public or protected method to update the suffix.
|
280
|
+
The user code or subclass (Waveform) can call this
|
281
|
+
when x_mode changes, but the AxisSettings won't show it.
|
282
|
+
"""
|
283
|
+
self._x_label_suffix = suffix
|
284
|
+
self._apply_x_label()
|
285
|
+
|
286
|
+
@property
|
287
|
+
def x_label_combined(self) -> str:
|
288
|
+
"""
|
289
|
+
The final label shown on the axis = user portion + suffix.
|
290
|
+
"""
|
291
|
+
return self._user_x_label + self._x_label_suffix
|
292
|
+
|
293
|
+
def _apply_x_label(self):
|
294
|
+
"""
|
295
|
+
Actually updates the pyqtgraph axis label text to
|
296
|
+
the combined label. Called whenever user label or suffix changes.
|
297
|
+
"""
|
298
|
+
final_label = self.x_label_combined
|
299
|
+
self.plot_item.setLabel("bottom", text=final_label)
|
265
300
|
|
266
301
|
@SafeProperty(str, doc="The text of the y label")
|
267
302
|
def y_label(self) -> str:
|
@@ -545,6 +580,7 @@ class PlotBase(BECWidget, QWidget):
|
|
545
580
|
self.unhook_crosshair()
|
546
581
|
self.unhook_fps_monitor(delete_label=True)
|
547
582
|
self.cleanup_pyqtgraph()
|
583
|
+
self.rpc_register.remove_rpc(self)
|
548
584
|
|
549
585
|
def cleanup_pyqtgraph(self):
|
550
586
|
"""Cleanup pyqtgraph items."""
|
@@ -561,7 +597,6 @@ if __name__ == "__main__": # pragma: no cover:
|
|
561
597
|
from qtpy.QtWidgets import QApplication
|
562
598
|
|
563
599
|
app = QApplication(sys.argv)
|
564
|
-
set_theme("dark")
|
565
600
|
widget = PlotBase()
|
566
601
|
widget.show()
|
567
602
|
# Just some example data and parameters to test
|
@@ -55,6 +55,27 @@ class MouseInteractionToolbarBundle(ToolbarBundle):
|
|
55
55
|
auto.action.triggered.connect(self.autorange_plot)
|
56
56
|
aspect_ratio.action.toggled.connect(self.lock_aspect_ratio)
|
57
57
|
|
58
|
+
mode = self.get_viewbox_mode()
|
59
|
+
if mode == "PanMode":
|
60
|
+
drag.action.setChecked(True)
|
61
|
+
elif mode == "RectMode":
|
62
|
+
rect.action.setChecked(True)
|
63
|
+
|
64
|
+
def get_viewbox_mode(self) -> str:
|
65
|
+
"""
|
66
|
+
Returns the current interaction mode of a PyQtGraph ViewBox.
|
67
|
+
|
68
|
+
Returns:
|
69
|
+
str: "PanMode" if pan is enabled, "RectMode" if zoom is enabled, "Unknown" otherwise.
|
70
|
+
"""
|
71
|
+
if self.target_widget:
|
72
|
+
viewbox = self.target_widget.plot_item.getViewBox()
|
73
|
+
if viewbox.getState()["mouseMode"] == 3:
|
74
|
+
return "PanMode"
|
75
|
+
elif viewbox.getState()["mouseMode"] == 1:
|
76
|
+
return "RectMode"
|
77
|
+
return "Unknown"
|
78
|
+
|
58
79
|
@SafeSlot(bool)
|
59
80
|
def enable_mouse_rectangle_mode(self, checked: bool):
|
60
81
|
"""
|
@@ -0,0 +1,26 @@
|
|
1
|
+
from bec_widgets.qt_utils.toolbar import MaterialIconAction, ToolbarBundle
|
2
|
+
|
3
|
+
|
4
|
+
class ROIBundle(ToolbarBundle):
|
5
|
+
"""
|
6
|
+
A bundle of actions that are hooked in this constructor itself,
|
7
|
+
so that you can immediately connect the signals and toggle states.
|
8
|
+
|
9
|
+
This bundle is for a toolbar that controls crosshair and ROI interaction.
|
10
|
+
"""
|
11
|
+
|
12
|
+
def __init__(self, bundle_id="roi", target_widget=None, **kwargs):
|
13
|
+
super().__init__(bundle_id=bundle_id, actions=[], **kwargs)
|
14
|
+
self.target_widget = target_widget
|
15
|
+
|
16
|
+
# Create each MaterialIconAction with a parent
|
17
|
+
# so the signals can fire even if the toolbar isn't added yet.
|
18
|
+
crosshair = MaterialIconAction(
|
19
|
+
icon_name="point_scan", tooltip="Show Crosshair", checkable=True
|
20
|
+
)
|
21
|
+
|
22
|
+
# Add them to the bundle
|
23
|
+
self.add_action("crosshair", crosshair)
|
24
|
+
|
25
|
+
# Immediately connect signals
|
26
|
+
crosshair.action.toggled.connect(self.target_widget.toggle_crosshair)
|
@@ -2,11 +2,11 @@
|
|
2
2
|
.gitlab-ci.yml,sha256=PuL-FmkTHm7qs467Mh9D8quWcEj4tgEA-UUGDieMuWk,8774
|
3
3
|
.pylintrc,sha256=eeY8YwSI74oFfq6IYIbCqnx3Vk8ZncKaatv96n_Y8Rs,18544
|
4
4
|
.readthedocs.yaml,sha256=aSOc277LqXcsTI6lgvm_JY80lMlr69GbPKgivua2cS0,603
|
5
|
-
CHANGELOG.md,sha256=
|
5
|
+
CHANGELOG.md,sha256=I0VXV76hN6hH8JX7l9F5OXVqbuenWClNEslZF43a6yU,230360
|
6
6
|
LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
|
7
|
-
PKG-INFO,sha256=
|
7
|
+
PKG-INFO,sha256=AoFfQtO1XAwk8aXaYDJs2ZBxHxPbxVKUbwOVXpQAkJw,1173
|
8
8
|
README.md,sha256=KgdKusjlvEvFtdNZCeDMO91y77MWK2iDcYMDziksOr4,2553
|
9
|
-
pyproject.toml,sha256=
|
9
|
+
pyproject.toml,sha256=HVH5bV4UEM4smaSNq_bv0FAjv14NavzQ0xGE3K1r_sk,2540
|
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
|
@@ -52,10 +52,10 @@ bec_widgets/qt_utils/compact_popup.py,sha256=3yeb-GJ1PUla5Q_hT0XDKqvyIEH9yV_eGid
|
|
52
52
|
bec_widgets/qt_utils/error_popups.py,sha256=7bL-Lil1G8reQPaRZo8NfG_7Cq2Y0HkF3KREJUE0ZlQ,11545
|
53
53
|
bec_widgets/qt_utils/palette_viewer.py,sha256=--B0x7aE7bniHIeuuLY_pH8yBDrTTXaE0IDrC_AM1mo,6326
|
54
54
|
bec_widgets/qt_utils/redis_message_waiter.py,sha256=fvL_QgC0cTDv_FPJdRyp5AKjf401EJU4z3r38p47ydY,1745
|
55
|
-
bec_widgets/qt_utils/round_frame.py,sha256=
|
55
|
+
bec_widgets/qt_utils/round_frame.py,sha256=ldLQ4BaWygFRrRv1s0w--5qZ4sj8MAqhdXuzho50xMg,4860
|
56
56
|
bec_widgets/qt_utils/settings_dialog.py,sha256=NhtzTer_xzlB2lLLrGklkI1QYLJEWQpJoZbCz4o5daI,3645
|
57
57
|
bec_widgets/qt_utils/side_panel.py,sha256=H2Ko7FPYwQ2Nemrud-q-rmqzHGO8vly_pzE_ySEfoGQ,12569
|
58
|
-
bec_widgets/qt_utils/toolbar.py,sha256=
|
58
|
+
bec_widgets/qt_utils/toolbar.py,sha256=AizwnTiY2RkRJfouJu4nCiFoWd0wEgS_nDF36EiUQjk,35779
|
59
59
|
bec_widgets/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
60
60
|
bec_widgets/tests/utils.py,sha256=GbQtN7qf9n-8FoAfNddZ4aAqA7oBo_hGAlnKELd6Xzw,6943
|
61
61
|
bec_widgets/utils/__init__.py,sha256=1930ji1Jj6dVuY81Wd2kYBhHYNV-2R0bN_L4o9zBj1U,533
|
@@ -64,8 +64,8 @@ bec_widgets/utils/bec_designer.py,sha256=XBy38NbNMoRDpvRx5lGP2XnJNG34YKZ7I-ARFkn
|
|
64
64
|
bec_widgets/utils/bec_dispatcher.py,sha256=OFmkx9vOz4pA4Sdc14QreyDZ870QYskJ4B5daVVeYg4,6325
|
65
65
|
bec_widgets/utils/bec_signal_proxy.py,sha256=56lgrHmKEDxzvhLw5DJcyBVJJUcrPSqQIU1EFQmwutw,3268
|
66
66
|
bec_widgets/utils/bec_table.py,sha256=nA2b8ukSeUfquFMAxGrUVOqdrzMoDYD6O_4EYbOG2zk,717
|
67
|
-
bec_widgets/utils/bec_widget.py,sha256=
|
68
|
-
bec_widgets/utils/colors.py,sha256=
|
67
|
+
bec_widgets/utils/bec_widget.py,sha256=O3JSaKXL3l62EU9sbpsRL8lgnE-aZHkwccuWTSiP7zk,3344
|
68
|
+
bec_widgets/utils/colors.py,sha256=8HKIIwQX4P7ND7ITtGWcN8ozG2xT3IBTeYMkneaPBC8,18288
|
69
69
|
bec_widgets/utils/container_utils.py,sha256=0wr3ZfuMiAFKCrQHVjxjw-Vuk8wsHdridqcjy2eY840,1531
|
70
70
|
bec_widgets/utils/crosshair.py,sha256=baT68rFZjAOoQDC9V3p7vfYhIy1FIMnPNtbnppvjYiM,18141
|
71
71
|
bec_widgets/utils/entry_validator.py,sha256=3skJIsUwTYicT76AMHm_M78RiWtUgyD2zb-Rxo2HdHQ,1313
|
@@ -276,14 +276,15 @@ bec_widgets/widgets/plots/waveform/waveform_popups/curve_dialog/curve_dialog.py,
|
|
276
276
|
bec_widgets/widgets/plots/waveform/waveform_popups/curve_dialog/curve_dialog.ui,sha256=vaOfrygcQp3-H82AkMUHgV2v0Y_TmRO5KLui-bWoado,11056
|
277
277
|
bec_widgets/widgets/plots/waveform/waveform_popups/dap_summary_dialog/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
278
278
|
bec_widgets/widgets/plots/waveform/waveform_popups/dap_summary_dialog/dap_summary_dialog.py,sha256=78vAXX_x5vi1XMtncOHvTvfgGnZJNSkRjOAnCOBE26U,1047
|
279
|
-
bec_widgets/widgets/plots_next_gen/plot_base.py,sha256=
|
279
|
+
bec_widgets/widgets/plots_next_gen/plot_base.py,sha256=epwxSv6vH_RutDPV8jRlA9kq7tc4qUYOsLoeSS9iKAo,22113
|
280
280
|
bec_widgets/widgets/plots_next_gen/setting_menus/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
281
281
|
bec_widgets/widgets/plots_next_gen/setting_menus/axis_settings.py,sha256=GpgTIYa7t4q9G8hF0rg_qJW2FgbrkvnsMMuqFKJR1fA,3567
|
282
282
|
bec_widgets/widgets/plots_next_gen/setting_menus/axis_settings_horizontal.ui,sha256=ye-guaRU_jhu7sHZS-9AjBjLrCtA1msOD0dszu4o9x8,11785
|
283
283
|
bec_widgets/widgets/plots_next_gen/setting_menus/axis_settings_vertical.ui,sha256=VR0BgkNAZWRXZPCvX3kMQwM0GLahgM_k2XYmSNbHM64,11235
|
284
284
|
bec_widgets/widgets/plots_next_gen/toolbar_bundles/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
285
|
-
bec_widgets/widgets/plots_next_gen/toolbar_bundles/mouse_interactions.py,sha256=
|
285
|
+
bec_widgets/widgets/plots_next_gen/toolbar_bundles/mouse_interactions.py,sha256=tc7v50yS_BYJ_uBFm5lTYJX7gIOZnAr4DRC0wIWXDSk,3924
|
286
286
|
bec_widgets/widgets/plots_next_gen/toolbar_bundles/plot_export.py,sha256=9xbpMtaBuY8kvzTSkEeFF52qJloEexHtPbGzU2f2n3s,2361
|
287
|
+
bec_widgets/widgets/plots_next_gen/toolbar_bundles/roi_bundle.py,sha256=kgEPk1MYJ0ighSm5T1ri8Hw1t82J4SKMvJLj_witnoQ,979
|
287
288
|
bec_widgets/widgets/plots_next_gen/toolbar_bundles/save_state.py,sha256=a7yCJQ_8HVAYN2dHcbxAjo3nJOSUUQZ7RJuUuGCXDBU,1740
|
288
289
|
bec_widgets/widgets/progress/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
289
290
|
bec_widgets/widgets/progress/bec_progressbar/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -355,8 +356,8 @@ bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.py,sha256=Z
|
|
355
356
|
bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.pyproject,sha256=Lbi9zb6HNlIq14k6hlzR-oz6PIFShBuF7QxE6d87d64,34
|
356
357
|
bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button_plugin.py,sha256=CzChz2SSETYsR8-36meqWnsXCT-FIy_J_xeU5coWDY8,1350
|
357
358
|
bec_widgets/widgets/utility/visual/dark_mode_button/register_dark_mode_button.py,sha256=rMpZ1CaoucwobgPj1FuKTnt07W82bV1GaSYdoqcdMb8,521
|
358
|
-
bec_widgets-1.
|
359
|
-
bec_widgets-1.
|
360
|
-
bec_widgets-1.
|
361
|
-
bec_widgets-1.
|
362
|
-
bec_widgets-1.
|
359
|
+
bec_widgets-1.22.0.dist-info/METADATA,sha256=AoFfQtO1XAwk8aXaYDJs2ZBxHxPbxVKUbwOVXpQAkJw,1173
|
360
|
+
bec_widgets-1.22.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
361
|
+
bec_widgets-1.22.0.dist-info/entry_points.txt,sha256=dItMzmwA1wizJ1Itx15qnfJ0ZzKVYFLVJ1voxT7K7D4,214
|
362
|
+
bec_widgets-1.22.0.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
|
363
|
+
bec_widgets-1.22.0.dist-info/RECORD,,
|
pyproject.toml
CHANGED
File without changes
|
File without changes
|
File without changes
|