bec-widgets 1.8.0__py3-none-any.whl → 1.9.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 CHANGED
@@ -1,6 +1,19 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## v1.9.0 (2024-12-10)
5
+
6
+ ### Features
7
+
8
+ - **side_menu**: Side menu with stack widget added
9
+ ([`c7d7c6d`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/c7d7c6d9ed7c2dcc42b33fcd590f1f27499322c1))
10
+
11
+ ### Testing
12
+
13
+ - **side_panel**: Tests added
14
+ ([`9b95b5d`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/9b95b5d6164ff42673dbbc3031e5b1f45fbcde0a))
15
+
16
+
4
17
  ## v1.8.0 (2024-12-10)
5
18
 
6
19
  ### Features
@@ -197,16 +210,3 @@ Depending on the test, auto-updates are enabled or not.
197
210
 
198
211
  - Update outdated text in docs
199
212
  ([`4f0693c`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/4f0693cae34b391d75884837e1ae6353a0501868))
200
-
201
-
202
- ## v1.3.2 (2024-11-05)
203
-
204
- ### Bug Fixes
205
-
206
- - **plot_base**: Legend text color is changed when changing dark-light theme
207
- ([`2304c9f`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/2304c9f8497c1ab1492f3e6690bb79b0464c0df8))
208
-
209
- ### Build System
210
-
211
- - Pyside6 version fixed 6.7.2
212
- ([`c6e48ec`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/c6e48ec1fe5aaee6a7c7a6f930f1520cd439cdb2))
PKG-INFO CHANGED
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bec_widgets
3
- Version: 1.8.0
3
+ Version: 1.9.0
4
4
  Summary: BEC Widgets
5
5
  Project-URL: Bug Tracker, https://gitlab.psi.ch/bec/bec_widgets/issues
6
6
  Project-URL: Homepage, https://gitlab.psi.ch/bec/bec_widgets
@@ -0,0 +1,386 @@
1
+ import sys
2
+ from typing import Literal, Optional
3
+
4
+ from qtpy.QtCore import Property, QEasingCurve, QPropertyAnimation
5
+ from qtpy.QtGui import QAction
6
+ from qtpy.QtWidgets import (
7
+ QApplication,
8
+ QHBoxLayout,
9
+ QLabel,
10
+ QMainWindow,
11
+ QSizePolicy,
12
+ QSpacerItem,
13
+ QStackedWidget,
14
+ QVBoxLayout,
15
+ QWidget,
16
+ )
17
+
18
+ from bec_widgets.qt_utils.toolbar import MaterialIconAction, ModularToolBar
19
+ from bec_widgets.widgets.plots.waveform.waveform_widget import BECWaveformWidget
20
+
21
+
22
+ class SidePanel(QWidget):
23
+ """
24
+ Side panel widget that can be placed on the left, right, top, or bottom of the main widget.
25
+ """
26
+
27
+ def __init__(
28
+ self,
29
+ parent=None,
30
+ orientation: Literal["left", "right", "top", "bottom"] = "left",
31
+ panel_max_width: int = 200,
32
+ animation_duration: int = 200,
33
+ animations_enabled: bool = True,
34
+ ):
35
+ super().__init__(parent=parent)
36
+
37
+ self._orientation = orientation
38
+ self._panel_max_width = panel_max_width
39
+ self._animation_duration = animation_duration
40
+ self._animations_enabled = animations_enabled
41
+ self._orientation = orientation
42
+
43
+ self._panel_width = 0
44
+ self._panel_height = 0
45
+ self.panel_visible = False
46
+ self.current_action: Optional[QAction] = None
47
+ self.current_index: Optional[int] = None
48
+ self.switching_actions = False
49
+
50
+ self._init_ui()
51
+
52
+ def _init_ui(self):
53
+ """
54
+ Initialize the UI elements.
55
+ """
56
+ if self._orientation in ("left", "right"):
57
+ self.main_layout = QHBoxLayout(self)
58
+ self.main_layout.setContentsMargins(0, 0, 0, 0)
59
+ self.main_layout.setSpacing(0)
60
+
61
+ self.toolbar = ModularToolBar(target_widget=self, orientation="vertical")
62
+
63
+ self.container = QWidget()
64
+ self.container.layout = QVBoxLayout(self.container)
65
+ self.container.layout.setContentsMargins(0, 0, 0, 0)
66
+ self.container.layout.setSpacing(0)
67
+
68
+ self.stack_widget = QStackedWidget()
69
+ self.stack_widget.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)
70
+ self.stack_widget.setMinimumWidth(5)
71
+
72
+ if self._orientation == "left":
73
+ self.main_layout.addWidget(self.toolbar)
74
+ self.main_layout.addWidget(self.container)
75
+ else:
76
+ self.main_layout.addWidget(self.container)
77
+ self.main_layout.addWidget(self.toolbar)
78
+
79
+ self.container.layout.addWidget(self.stack_widget)
80
+ self.stack_widget.setMaximumWidth(self._panel_max_width)
81
+
82
+ else:
83
+ self.main_layout = QVBoxLayout(self)
84
+ self.main_layout.setContentsMargins(0, 0, 0, 0)
85
+ self.main_layout.setSpacing(0)
86
+
87
+ self.toolbar = ModularToolBar(target_widget=self, orientation="horizontal")
88
+
89
+ self.container = QWidget()
90
+ self.container.layout = QVBoxLayout(self.container)
91
+ self.container.layout.setContentsMargins(0, 0, 0, 0)
92
+ self.container.layout.setSpacing(0)
93
+
94
+ self.stack_widget = QStackedWidget()
95
+ self.stack_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
96
+ self.stack_widget.setMinimumHeight(5)
97
+
98
+ if self._orientation == "top":
99
+ self.main_layout.addWidget(self.toolbar)
100
+ self.main_layout.addWidget(self.container)
101
+ else:
102
+ self.main_layout.addWidget(self.container)
103
+ self.main_layout.addWidget(self.toolbar)
104
+
105
+ self.container.layout.addWidget(self.stack_widget)
106
+ self.stack_widget.setMaximumHeight(self._panel_max_width)
107
+
108
+ if self._orientation in ("left", "right"):
109
+ self.menu_anim = QPropertyAnimation(self, b"panel_width")
110
+ else:
111
+ self.menu_anim = QPropertyAnimation(self, b"panel_height")
112
+
113
+ self.menu_anim.setDuration(self._animation_duration)
114
+ self.menu_anim.setEasingCurve(QEasingCurve.InOutQuad)
115
+
116
+ if self._orientation in ("left", "right"):
117
+ self.panel_width = 0
118
+ else:
119
+ self.panel_height = 0
120
+
121
+ @Property(int)
122
+ def panel_width(self):
123
+ """
124
+ Get the panel width.
125
+ """
126
+ return self._panel_width
127
+
128
+ @panel_width.setter
129
+ def panel_width(self, width: int):
130
+ """
131
+ Set the panel width.
132
+
133
+ Args:
134
+ width(int): The width of the panel.
135
+ """
136
+ self._panel_width = width
137
+ if self._orientation in ("left", "right"):
138
+ self.stack_widget.setFixedWidth(width)
139
+
140
+ @Property(int)
141
+ def panel_height(self):
142
+ """
143
+ Get the panel height.
144
+ """
145
+ return self._panel_height
146
+
147
+ @panel_height.setter
148
+ def panel_height(self, height: int):
149
+ """
150
+ Set the panel height.
151
+
152
+ Args:
153
+ height(int): The height of the panel.
154
+ """
155
+ self._panel_height = height
156
+ if self._orientation in ("top", "bottom"):
157
+ self.stack_widget.setFixedHeight(height)
158
+
159
+ @Property(int)
160
+ def panel_max_width(self):
161
+ """
162
+ Get the maximum width of the panel.
163
+ """
164
+ return self._panel_max_width
165
+
166
+ @panel_max_width.setter
167
+ def panel_max_width(self, size: int):
168
+ """
169
+ Set the maximum width of the panel.
170
+
171
+ Args:
172
+ size(int): The maximum width of the panel.
173
+ """
174
+ self._panel_max_width = size
175
+ if self._orientation in ("left", "right"):
176
+ self.stack_widget.setMaximumWidth(self._panel_max_width)
177
+ else:
178
+ self.stack_widget.setMaximumHeight(self._panel_max_width)
179
+
180
+ @Property(int)
181
+ def animation_duration(self):
182
+ """
183
+ Get the duration of the animation.
184
+ """
185
+ return self._animation_duration
186
+
187
+ @animation_duration.setter
188
+ def animation_duration(self, duration: int):
189
+ """
190
+ Set the duration of the animation.
191
+
192
+ Args:
193
+ duration(int): The duration of the animation.
194
+ """
195
+ self._animation_duration = duration
196
+ self.menu_anim.setDuration(duration)
197
+
198
+ @Property(bool)
199
+ def animations_enabled(self):
200
+ """
201
+ Get the status of the animations.
202
+ """
203
+ return self._animations_enabled
204
+
205
+ @animations_enabled.setter
206
+ def animations_enabled(self, enabled: bool):
207
+ """
208
+ Set the status of the animations.
209
+
210
+ Args:
211
+ enabled(bool): The status of the animations.
212
+ """
213
+ self._animations_enabled = enabled
214
+
215
+ def show_panel(self, idx: int):
216
+ """
217
+ Show the side panel with animation and switch to idx.
218
+
219
+ Args:
220
+ idx(int): The index of the panel to show.
221
+ """
222
+ self.stack_widget.setCurrentIndex(idx)
223
+ self.panel_visible = True
224
+ self.current_index = idx
225
+
226
+ if self._orientation in ("left", "right"):
227
+ start_val, end_val = 0, self._panel_max_width
228
+ else:
229
+ start_val, end_val = 0, self._panel_max_width
230
+
231
+ if self._animations_enabled:
232
+ self.menu_anim.stop()
233
+ self.menu_anim.setStartValue(start_val)
234
+ self.menu_anim.setEndValue(end_val)
235
+ self.menu_anim.start()
236
+ else:
237
+ if self._orientation in ("left", "right"):
238
+ self.panel_width = end_val
239
+ else:
240
+ self.panel_height = end_val
241
+
242
+ def hide_panel(self):
243
+ """
244
+ Hide the side panel with animation.
245
+ """
246
+ self.panel_visible = False
247
+ self.current_index = None
248
+
249
+ if self._orientation in ("left", "right"):
250
+ start_val, end_val = self._panel_max_width, 0
251
+ else:
252
+ start_val, end_val = self._panel_max_width, 0
253
+
254
+ if self._animations_enabled:
255
+ self.menu_anim.stop()
256
+ self.menu_anim.setStartValue(start_val)
257
+ self.menu_anim.setEndValue(end_val)
258
+ self.menu_anim.start()
259
+ else:
260
+ if self._orientation in ("left", "right"):
261
+ self.panel_width = end_val
262
+ else:
263
+ self.panel_height = end_val
264
+
265
+ def switch_to(self, idx: int):
266
+ """
267
+ Switch to the specified index without animation.
268
+
269
+ Args:
270
+ idx(int): The index of the panel to switch to.
271
+ """
272
+ if self.current_index != idx:
273
+ self.stack_widget.setCurrentIndex(idx)
274
+ self.current_index = idx
275
+
276
+ def add_menu(self, action_id: str, icon_name: str, tooltip: str, widget: QWidget, title: str):
277
+ """
278
+ Add a menu to the side panel.
279
+
280
+ Args:
281
+ action_id(str): The ID of the action.
282
+ icon_name(str): The name of the icon.
283
+ tooltip(str): The tooltip for the action.
284
+ widget(QWidget): The widget to add to the panel.
285
+ title(str): The title of the panel.
286
+ """
287
+ container_widget = QWidget()
288
+ container_layout = QVBoxLayout(container_widget)
289
+ container_widget.setStyleSheet("background-color: rgba(0,0,0,0);")
290
+ title_label = QLabel(f"<b>{title}</b>")
291
+ title_label.setStyleSheet("font-size: 16px;")
292
+ spacer = QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding)
293
+ container_layout.addWidget(title_label)
294
+ container_layout.addWidget(widget)
295
+ container_layout.addItem(spacer)
296
+ container_layout.setContentsMargins(5, 5, 5, 5)
297
+ container_layout.setSpacing(5)
298
+
299
+ index = self.stack_widget.count()
300
+ self.stack_widget.addWidget(container_widget)
301
+
302
+ action = MaterialIconAction(icon_name=icon_name, tooltip=tooltip, checkable=True)
303
+ self.toolbar.add_action(action_id, action, target_widget=self)
304
+
305
+ def on_action_toggled(checked: bool):
306
+ if self.switching_actions:
307
+ return
308
+
309
+ if checked:
310
+ if self.current_action and self.current_action != action.action:
311
+ self.switching_actions = True
312
+ self.current_action.setChecked(False)
313
+ self.switching_actions = False
314
+
315
+ self.current_action = action.action
316
+
317
+ if not self.panel_visible:
318
+ self.show_panel(index)
319
+ else:
320
+ self.switch_to(index)
321
+ else:
322
+ if self.current_action == action.action:
323
+ self.current_action = None
324
+ self.hide_panel()
325
+
326
+ action.action.toggled.connect(on_action_toggled)
327
+
328
+
329
+ class ExampleApp(QMainWindow): # pragma: no cover
330
+ def __init__(self):
331
+ super().__init__()
332
+ self.setWindowTitle("Side Panel Example")
333
+
334
+ central_widget = QWidget()
335
+ self.setCentralWidget(central_widget)
336
+
337
+ self.side_panel = SidePanel(self, orientation="left")
338
+
339
+ self.layout = QHBoxLayout(central_widget)
340
+
341
+ self.layout.addWidget(self.side_panel)
342
+ self.plot = BECWaveformWidget()
343
+ self.layout.addWidget(self.plot)
344
+ self.add_side_menus()
345
+
346
+ def add_side_menus(self):
347
+ widget1 = QWidget()
348
+ widget1_layout = QVBoxLayout(widget1)
349
+ widget1_layout.addWidget(QLabel("This is Widget 1"))
350
+ self.side_panel.add_menu(
351
+ action_id="widget1",
352
+ icon_name="counter_1",
353
+ tooltip="Show Widget 1",
354
+ widget=widget1,
355
+ title="Widget 1 Panel",
356
+ )
357
+
358
+ widget2 = QWidget()
359
+ widget2_layout = QVBoxLayout(widget2)
360
+ widget2_layout.addWidget(QLabel("This is Widget 2"))
361
+ self.side_panel.add_menu(
362
+ action_id="widget2",
363
+ icon_name="counter_2",
364
+ tooltip="Show Widget 2",
365
+ widget=widget2,
366
+ title="Widget 2 Panel",
367
+ )
368
+
369
+ widget3 = QWidget()
370
+ widget3_layout = QVBoxLayout(widget3)
371
+ widget3_layout.addWidget(QLabel("This is Widget 3"))
372
+ self.side_panel.add_menu(
373
+ action_id="widget3",
374
+ icon_name="counter_3",
375
+ tooltip="Show Widget 3",
376
+ widget=widget3,
377
+ title="Widget 3 Panel",
378
+ )
379
+
380
+
381
+ if __name__ == "__main__": # pragma: no cover
382
+ app = QApplication(sys.argv)
383
+ window = ExampleApp()
384
+ window.resize(800, 600)
385
+ window.show()
386
+ sys.exit(app.exec())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bec_widgets
3
- Version: 1.8.0
3
+ Version: 1.9.0
4
4
  Summary: BEC Widgets
5
5
  Project-URL: Bug Tracker, https://gitlab.psi.ch/bec/bec_widgets/issues
6
6
  Project-URL: Homepage, https://gitlab.psi.ch/bec/bec_widgets
@@ -2,11 +2,11 @@
2
2
  .gitlab-ci.yml,sha256=bAWGX_NR9rQZmv_bmyLXkEMRreWp0JzVNpsNTxk0NwE,8637
3
3
  .pylintrc,sha256=eeY8YwSI74oFfq6IYIbCqnx3Vk8ZncKaatv96n_Y8Rs,18544
4
4
  .readthedocs.yaml,sha256=aSOc277LqXcsTI6lgvm_JY80lMlr69GbPKgivua2cS0,603
5
- CHANGELOG.md,sha256=tsLjli6mOqvULXg4ZhW3Un83h0N3EVMWlO8-XoDM6ac,7898
5
+ CHANGELOG.md,sha256=vk99JDb0HVTJZsEm6TJ8g-RemuqHi7tQsDKkk-GVF2k,7866
6
6
  LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
7
- PKG-INFO,sha256=PITOTEI21SfFd0AnvPLVX4xld5y48S79WCqdRBfqWUU,1308
7
+ PKG-INFO,sha256=QjMuyUkfdYQ7l0gazbGYeGbf96Sx3xRfbSaBsM9rcrY,1308
8
8
  README.md,sha256=Od69x-RS85Hph0-WwWACwal4yUd67XkEn4APEfHhHFw,2649
9
- pyproject.toml,sha256=ITOEvWKMjOJbMjqOR4e4b-zFRDu428jAgVN-KRAlWvM,2586
9
+ pyproject.toml,sha256=_ntcDbeIJWyJtMIlP5r3Hq9h1z4vuth-KYYJyK14PF0,2586
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
@@ -51,6 +51,7 @@ bec_widgets/qt_utils/palette_viewer.py,sha256=--B0x7aE7bniHIeuuLY_pH8yBDrTTXaE0I
51
51
  bec_widgets/qt_utils/redis_message_waiter.py,sha256=fvL_QgC0cTDv_FPJdRyp5AKjf401EJU4z3r38p47ydY,1745
52
52
  bec_widgets/qt_utils/round_frame.py,sha256=Ba_sTzYB_vYDepBBMPPqU8XDwKOAiU6ClZ3xUqiveK0,5734
53
53
  bec_widgets/qt_utils/settings_dialog.py,sha256=NhtzTer_xzlB2lLLrGklkI1QYLJEWQpJoZbCz4o5daI,3645
54
+ bec_widgets/qt_utils/side_panel.py,sha256=5XtHIGfEJJj5m7cvkm-Vaxzz1TQogwglrmBaVcmcngY,12332
54
55
  bec_widgets/qt_utils/toolbar.py,sha256=RcWoWjibhlpL26Bnbft-uWA1q2WCglJRnO6U3hGMBw8,13277
55
56
  bec_widgets/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
57
  bec_widgets/tests/utils.py,sha256=D1v3JLzzbnX3HXBQCjoFlNz5cYhjqrRkFcjx3yptMJA,6687
@@ -314,8 +315,8 @@ bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.py,sha256=Z
314
315
  bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.pyproject,sha256=Lbi9zb6HNlIq14k6hlzR-oz6PIFShBuF7QxE6d87d64,34
315
316
  bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button_plugin.py,sha256=CzChz2SSETYsR8-36meqWnsXCT-FIy_J_xeU5coWDY8,1350
316
317
  bec_widgets/widgets/utility/visual/dark_mode_button/register_dark_mode_button.py,sha256=rMpZ1CaoucwobgPj1FuKTnt07W82bV1GaSYdoqcdMb8,521
317
- bec_widgets-1.8.0.dist-info/METADATA,sha256=PITOTEI21SfFd0AnvPLVX4xld5y48S79WCqdRBfqWUU,1308
318
- bec_widgets-1.8.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
319
- bec_widgets-1.8.0.dist-info/entry_points.txt,sha256=dItMzmwA1wizJ1Itx15qnfJ0ZzKVYFLVJ1voxT7K7D4,214
320
- bec_widgets-1.8.0.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
321
- bec_widgets-1.8.0.dist-info/RECORD,,
318
+ bec_widgets-1.9.0.dist-info/METADATA,sha256=QjMuyUkfdYQ7l0gazbGYeGbf96Sx3xRfbSaBsM9rcrY,1308
319
+ bec_widgets-1.9.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
320
+ bec_widgets-1.9.0.dist-info/entry_points.txt,sha256=dItMzmwA1wizJ1Itx15qnfJ0ZzKVYFLVJ1voxT7K7D4,214
321
+ bec_widgets-1.9.0.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
322
+ bec_widgets-1.9.0.dist-info/RECORD,,
pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "bec_widgets"
7
- version = "1.8.0"
7
+ version = "1.9.0"
8
8
  description = "BEC Widgets"
9
9
  requires-python = ">=3.10"
10
10
  classifiers = [