bec-widgets 1.17.2__py3-none-any.whl → 1.18.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.
.gitlab-ci.yml CHANGED
@@ -78,9 +78,9 @@ formatter:
78
78
  stage: Formatter
79
79
  needs: []
80
80
  script:
81
- - pip install black isort
82
- - isort --check --diff ./
83
- - black --check --diff --color ./
81
+ - pip install bec_lib[dev]
82
+ - isort --check --diff --line-length=100 --profile=black --multi-line=3 --trailing-comma ./
83
+ - black --check --diff --color --line-length=100 --skip-magic-trailing-comma ./
84
84
  rules:
85
85
  - if: $CI_PROJECT_PATH == "bec/bec_widgets"
86
86
 
@@ -148,7 +148,7 @@ tests:
148
148
  - *clone-repos
149
149
  - *install-os-packages
150
150
  - *install-repos
151
- - pip install -e .[dev,pyqt6]
151
+ - pip install -e .[dev,pyside6]
152
152
  - coverage run --source=./bec_widgets -m pytest -v --junitxml=report.xml --maxfail=2 --random-order --full-trace ./tests/unit_tests
153
153
  - coverage report
154
154
  - coverage xml
@@ -172,7 +172,6 @@ test-matrix:
172
172
  - "3.12"
173
173
  QT_PCKG:
174
174
  - "pyside6"
175
- - "pyqt6"
176
175
 
177
176
  stage: AdditionalTests
178
177
  needs: []
@@ -211,7 +210,7 @@ end-2-end-conda:
211
210
  - cd ../
212
211
  - pip install -e ./ophyd_devices
213
212
 
214
- - pip install -e .[dev,pyqt6]
213
+ - pip install -e .[dev,pyside6]
215
214
  - cd ./tests/end-2-end
216
215
  - pytest -v --start-servers --flush-redis --random-order
217
216
 
CHANGELOG.md CHANGED
@@ -1,6 +1,30 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## v1.18.0 (2025-01-30)
5
+
6
+ ### Bug Fixes
7
+
8
+ - **generate_cli**: Widgets can be tagged with RPC=False, then they are excluded from client.py for
9
+ RPC
10
+ ([`48fc63d`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/48fc63d83e26889843b09b1eb4792612b53200ec))
11
+
12
+ ### Build System
13
+
14
+ - Pyqt6 support dropped
15
+ ([`a20935e`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/a20935e8625a9490e6c451a3b4012476e19317e5))
16
+
17
+ ### Continuous Integration
18
+
19
+ - Fix formatter 2024 versions
20
+ ([`4f8e683`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/4f8e6835fe2312151dc2b40f0ab9eb50a9173f7c))
21
+
22
+ ### Features
23
+
24
+ - **plot_base_next_gen**: New type of plot base inherited from QWidget
25
+ ([`e7c9729`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/e7c97290cd783d19128625567835d7ae9a414989))
26
+
27
+
4
28
  ## v1.17.2 (2025-01-28)
5
29
 
6
30
  ### Bug Fixes
PKG-INFO CHANGED
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bec_widgets
3
- Version: 1.17.2
3
+ Version: 1.18.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
@@ -28,8 +28,5 @@ Requires-Dist: pytest-random-order~=1.1; extra == 'dev'
28
28
  Requires-Dist: pytest-timeout~=2.2; extra == 'dev'
29
29
  Requires-Dist: pytest-xvfb~=3.0; extra == 'dev'
30
30
  Requires-Dist: pytest~=8.0; extra == 'dev'
31
- Provides-Extra: pyqt6
32
- Requires-Dist: pyqt6-webengine>=6.7; extra == 'pyqt6'
33
- Requires-Dist: pyqt6>=6.7; extra == 'pyqt6'
34
31
  Provides-Extra: pyside6
35
32
  Requires-Dist: pyside6==6.7.2; extra == 'pyside6'
README.md CHANGED
@@ -1,12 +1,17 @@
1
1
  # BEC Widgets
2
2
 
3
+ **⚠️ Important Notice:**
4
+
5
+ 🚨 **PyQt6 is no longer supported** due to incompatibilities with Qt Designer. Please use **PySide6** instead. 🚨
6
+
3
7
  BEC Widgets is a GUI framework designed for interaction with [BEC (Beamline Experiment Control)](https://gitlab.psi.ch/bec/bec).
8
+
4
9
  ## Installation
5
10
 
6
11
  Use the package manager [pip](https://pip.pypa.io/en/stable/) to install BEC Widgets:
7
12
 
8
13
  ```bash
9
- pip install bec_widgets PyQt6
14
+ pip install bec_widgets[pyside6]
10
15
  ```
11
16
 
12
17
  For development purposes, you can clone the repository and install the package locally in editable mode:
@@ -14,22 +19,12 @@ For development purposes, you can clone the repository and install the package l
14
19
  ```bash
15
20
  git clone https://gitlab.psi.ch/bec/bec-widgets
16
21
  cd bec_widgets
17
- pip install -e .[dev,pyqt6]
22
+ pip install -e .[dev,pyside6]
18
23
  ```
19
24
 
20
- BEC Widgets currently supports both Pyside6 and PyQt6, however, no default distribution is specified. As a result, users must install one of the supported
21
- Python Qt distributions manually.
22
-
23
- To select a specific Python Qt distribution, install the package with an additional tag:
24
-
25
- ```bash
26
- pip install bec_widgets[pyqt6]
27
- ```
28
- or
25
+ BEC Widgets now **only supports PySide6**. Users must manually install PySide6 as no default Qt distribution is
26
+ specified.
29
27
 
30
- ```bash
31
- pip install bec_widgets[pyside6]
32
- ```
33
28
  ## Documentation
34
29
 
35
30
  Documentation of BEC Widgets can be found [here](https://bec-widgets.readthedocs.io/en/latest/). The documentation of the BEC can be found [here](https://bec.readthedocs.io/en/latest/).
@@ -39,7 +34,7 @@ Documentation of BEC Widgets can be found [here](https://bec-widgets.readthedocs
39
34
  All commits should use the Angular commit scheme:
40
35
 
41
36
  > #### <a name="commit-header"></a>Angular Commit Message Header
42
- >
37
+ >
43
38
  > ```
44
39
  > <type>(<scope>): <short summary>
45
40
  > │ │ │
@@ -53,13 +48,13 @@ All commits should use the Angular commit scheme:
53
48
  > │
54
49
  > └─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|test
55
50
  > ```
56
- >
51
+ >
57
52
  > The `<type>` and `<summary>` fields are mandatory, the `(<scope>)` field is optional.
58
53
 
59
54
  > ##### Type
60
- >
55
+ >
61
56
  > Must be one of the following:
62
- >
57
+ >
63
58
  > * **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
64
59
  > * **ci**: Changes to our CI configuration files and scripts (examples: CircleCi, SauceLabs)
65
60
  > * **docs**: Documentation only changes
@@ -71,4 +66,5 @@ All commits should use the Angular commit scheme:
71
66
 
72
67
  ## License
73
68
 
74
- [BSD-3-Clause](https://choosealicense.com/licenses/bsd-3-clause/)
69
+ [BSD-3-Clause](https://choosealicense.com/licenses/bsd-3-clause/)
70
+
@@ -43,14 +43,21 @@ from bec_widgets.cli.rpc.rpc_base import RPCBase, rpc_call
43
43
 
44
44
  def generate_client(self, class_container: BECClassContainer):
45
45
  """
46
- Generate the client for the published classes.
46
+ Generate the client for the published classes, skipping any classes
47
+ that have `RPC = False`.
47
48
 
48
49
  Args:
49
50
  class_container: The class container with the classes to generate the client for.
50
51
  """
51
- rpc_top_level_classes = class_container.rpc_top_level_classes
52
+ # Filter out classes that explicitly have RPC=False
53
+ rpc_top_level_classes = [
54
+ cls for cls in class_container.rpc_top_level_classes if getattr(cls, "RPC", True)
55
+ ]
52
56
  rpc_top_level_classes.sort(key=lambda x: x.__name__)
53
- connector_classes = class_container.connector_classes
57
+
58
+ connector_classes = [
59
+ cls for cls in class_container.connector_classes if getattr(cls, "RPC", True)
60
+ ]
54
61
  connector_classes.sort(key=lambda x: x.__name__)
55
62
 
56
63
  self.write_client_enum(rpc_top_level_classes)
@@ -81,13 +88,13 @@ class Widgets(str, enum.Enum):
81
88
 
82
89
  class_name = cls.__name__
83
90
 
84
- # Generate the content
85
- if cls.__name__ == "BECDockArea":
91
+ if class_name == "BECDockArea":
86
92
  self.content += f"""
87
93
  class {class_name}(RPCBase):"""
88
94
  else:
89
95
  self.content += f"""
90
96
  class {class_name}(RPCBase):"""
97
+
91
98
  if not cls.USER_ACCESS:
92
99
  self.content += """...
93
100
  """
@@ -100,8 +107,10 @@ class {class_name}(RPCBase):"""
100
107
  method = method.split(".setter")[0]
101
108
  if obj is None:
102
109
  raise AttributeError(
103
- f"Method {method} not found in class {cls.__name__}. Please check the USER_ACCESS list."
110
+ f"Method {method} not found in class {cls.__name__}. "
111
+ f"Please check the USER_ACCESS list."
104
112
  )
113
+
105
114
  if isinstance(obj, (property, QtProperty)):
106
115
  # for the cli, we can map qt properties to regular properties
107
116
  if is_property_setter:
@@ -20,6 +20,7 @@ from bec_widgets.widgets.containers.dock import BECDockArea
20
20
  from bec_widgets.widgets.containers.figure import BECFigure
21
21
  from bec_widgets.widgets.containers.layout_manager.layout_manager import LayoutManagerWidget
22
22
  from bec_widgets.widgets.editors.jupyter_console.jupyter_console import BECJupyterConsole
23
+ from bec_widgets.widgets.plots_next_gen.plot_base import PlotBase
23
24
 
24
25
 
25
26
  class JupyterConsoleWindow(QWidget): # pragma: no cover:
@@ -62,6 +63,8 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
62
63
  "btn4": self.btn4,
63
64
  "btn5": self.btn5,
64
65
  "btn6": self.btn6,
66
+ "pb": self.pb,
67
+ "pi": self.pi,
65
68
  }
66
69
  )
67
70
 
@@ -92,6 +95,15 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
92
95
  third_tab_layout.addWidget(self.lm)
93
96
  tab_widget.addTab(third_tab, "Layout Manager Widget")
94
97
 
98
+ fourth_tab = QWidget()
99
+ fourth_tab_layout = QVBoxLayout(fourth_tab)
100
+ self.pb = PlotBase()
101
+ self.pi = self.pb.plot_item
102
+ fourth_tab_layout.addWidget(self.pb)
103
+ tab_widget.addTab(fourth_tab, "PltoBase")
104
+
105
+ tab_widget.setCurrentIndex(3)
106
+
95
107
  group_box = QGroupBox("Jupyter Console", splitter)
96
108
  group_box_layout = QVBoxLayout(group_box)
97
109
  self.console = BECJupyterConsole(inprocess=True)
@@ -114,10 +114,12 @@ class RoundedFrame(BECWidget, QFrame):
114
114
 
115
115
  # Apply axis label and tick colors
116
116
  plot_item = self.content_widget.getPlotItem()
117
- plot_item.getAxis("left").setPen(pg.mkPen(color=axis_color))
118
- plot_item.getAxis("bottom").setPen(pg.mkPen(color=axis_color))
119
- plot_item.getAxis("left").setTextPen(pg.mkPen(color=label_color))
120
- plot_item.getAxis("bottom").setTextPen(pg.mkPen(color=label_color))
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)
121
123
 
122
124
  # Apply border style via stylesheet
123
125
  self.content_widget.setStyleSheet(
@@ -5,18 +5,18 @@ from qtpy.QtCore import Property, QEasingCurve, QPropertyAnimation
5
5
  from qtpy.QtGui import QAction
6
6
  from qtpy.QtWidgets import (
7
7
  QApplication,
8
+ QFrame,
8
9
  QHBoxLayout,
9
10
  QLabel,
10
11
  QMainWindow,
12
+ QScrollArea,
11
13
  QSizePolicy,
12
- QSpacerItem,
13
14
  QStackedWidget,
14
15
  QVBoxLayout,
15
16
  QWidget,
16
17
  )
17
18
 
18
19
  from bec_widgets.qt_utils.toolbar import MaterialIconAction, ModularToolBar
19
- from bec_widgets.widgets.plots.waveform.waveform_widget import BECWaveformWidget
20
20
 
21
21
 
22
22
  class SidePanel(QWidget):
@@ -41,7 +41,6 @@ class SidePanel(QWidget):
41
41
  self._panel_max_width = panel_max_width
42
42
  self._animation_duration = animation_duration
43
43
  self._animations_enabled = animations_enabled
44
- self._orientation = orientation
45
44
 
46
45
  self._panel_width = 0
47
46
  self._panel_height = 0
@@ -71,6 +70,7 @@ class SidePanel(QWidget):
71
70
  self.stack_widget = QStackedWidget()
72
71
  self.stack_widget.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)
73
72
  self.stack_widget.setMinimumWidth(5)
73
+ self.stack_widget.setMaximumWidth(self._panel_max_width)
74
74
 
75
75
  if self._orientation == "left":
76
76
  self.main_layout.addWidget(self.toolbar)
@@ -80,7 +80,10 @@ class SidePanel(QWidget):
80
80
  self.main_layout.addWidget(self.toolbar)
81
81
 
82
82
  self.container.layout.addWidget(self.stack_widget)
83
- self.stack_widget.setMaximumWidth(self._panel_max_width)
83
+
84
+ self.menu_anim = QPropertyAnimation(self, b"panel_width")
85
+ self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)
86
+ self.panel_width = 0 # start hidden
84
87
 
85
88
  else:
86
89
  self.main_layout = QVBoxLayout(self)
@@ -97,6 +100,7 @@ class SidePanel(QWidget):
97
100
  self.stack_widget = QStackedWidget()
98
101
  self.stack_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
99
102
  self.stack_widget.setMinimumHeight(5)
103
+ self.stack_widget.setMaximumHeight(self._panel_max_width)
100
104
 
101
105
  if self._orientation == "top":
102
106
  self.main_layout.addWidget(self.toolbar)
@@ -106,74 +110,46 @@ class SidePanel(QWidget):
106
110
  self.main_layout.addWidget(self.toolbar)
107
111
 
108
112
  self.container.layout.addWidget(self.stack_widget)
109
- self.stack_widget.setMaximumHeight(self._panel_max_width)
110
113
 
111
- if self._orientation in ("left", "right"):
112
- self.menu_anim = QPropertyAnimation(self, b"panel_width")
113
- else:
114
114
  self.menu_anim = QPropertyAnimation(self, b"panel_height")
115
+ self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
116
+ self.panel_height = 0 # start hidden
115
117
 
116
118
  self.menu_anim.setDuration(self._animation_duration)
117
119
  self.menu_anim.setEasingCurve(QEasingCurve.InOutQuad)
118
120
 
119
- if self._orientation in ("left", "right"):
120
- self.panel_width = 0
121
- else:
122
- self.panel_height = 0
123
-
124
121
  @Property(int)
125
122
  def panel_width(self):
126
- """
127
- Get the panel width.
128
- """
123
+ """Get the panel width."""
129
124
  return self._panel_width
130
125
 
131
126
  @panel_width.setter
132
127
  def panel_width(self, width: int):
133
- """
134
- Set the panel width.
135
-
136
- Args:
137
- width(int): The width of the panel.
138
- """
128
+ """Set the panel width."""
139
129
  self._panel_width = width
140
130
  if self._orientation in ("left", "right"):
141
131
  self.stack_widget.setFixedWidth(width)
142
132
 
143
133
  @Property(int)
144
134
  def panel_height(self):
145
- """
146
- Get the panel height.
147
- """
135
+ """Get the panel height."""
148
136
  return self._panel_height
149
137
 
150
138
  @panel_height.setter
151
139
  def panel_height(self, height: int):
152
- """
153
- Set the panel height.
154
-
155
- Args:
156
- height(int): The height of the panel.
157
- """
140
+ """Set the panel height."""
158
141
  self._panel_height = height
159
142
  if self._orientation in ("top", "bottom"):
160
143
  self.stack_widget.setFixedHeight(height)
161
144
 
162
145
  @Property(int)
163
146
  def panel_max_width(self):
164
- """
165
- Get the maximum width of the panel.
166
- """
147
+ """Get the maximum width of the panel."""
167
148
  return self._panel_max_width
168
149
 
169
150
  @panel_max_width.setter
170
151
  def panel_max_width(self, size: int):
171
- """
172
- Set the maximum width of the panel.
173
-
174
- Args:
175
- size(int): The maximum width of the panel.
176
- """
152
+ """Set the maximum width of the panel."""
177
153
  self._panel_max_width = size
178
154
  if self._orientation in ("left", "right"):
179
155
  self.stack_widget.setMaximumWidth(self._panel_max_width)
@@ -182,45 +158,28 @@ class SidePanel(QWidget):
182
158
 
183
159
  @Property(int)
184
160
  def animation_duration(self):
185
- """
186
- Get the duration of the animation.
187
- """
161
+ """Get the duration of the animation."""
188
162
  return self._animation_duration
189
163
 
190
164
  @animation_duration.setter
191
165
  def animation_duration(self, duration: int):
192
- """
193
- Set the duration of the animation.
194
-
195
- Args:
196
- duration(int): The duration of the animation.
197
- """
166
+ """Set the duration of the animation."""
198
167
  self._animation_duration = duration
199
168
  self.menu_anim.setDuration(duration)
200
169
 
201
170
  @Property(bool)
202
171
  def animations_enabled(self):
203
- """
204
- Get the status of the animations.
205
- """
172
+ """Get the status of the animations."""
206
173
  return self._animations_enabled
207
174
 
208
175
  @animations_enabled.setter
209
176
  def animations_enabled(self, enabled: bool):
210
- """
211
- Set the status of the animations.
212
-
213
- Args:
214
- enabled(bool): The status of the animations.
215
- """
177
+ """Set the status of the animations."""
216
178
  self._animations_enabled = enabled
217
179
 
218
180
  def show_panel(self, idx: int):
219
181
  """
220
182
  Show the side panel with animation and switch to idx.
221
-
222
- Args:
223
- idx(int): The index of the panel to show.
224
183
  """
225
184
  self.stack_widget.setCurrentIndex(idx)
226
185
  self.panel_visible = True
@@ -268,9 +227,6 @@ class SidePanel(QWidget):
268
227
  def switch_to(self, idx: int):
269
228
  """
270
229
  Switch to the specified index without animation.
271
-
272
- Args:
273
- idx(int): The index of the panel to switch to.
274
230
  """
275
231
  if self.current_index != idx:
276
232
  self.stack_widget.setCurrentIndex(idx)
@@ -287,20 +243,35 @@ class SidePanel(QWidget):
287
243
  widget(QWidget): The widget to add to the panel.
288
244
  title(str): The title of the panel.
289
245
  """
246
+ # container_widget: top-level container for the stacked page
290
247
  container_widget = QWidget()
291
248
  container_layout = QVBoxLayout(container_widget)
249
+ container_layout.setContentsMargins(0, 0, 0, 0)
250
+ container_layout.setSpacing(5)
251
+
292
252
  title_label = QLabel(f"<b>{title}</b>")
293
253
  title_label.setStyleSheet("font-size: 16px;")
294
- spacer = QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding)
295
254
  container_layout.addWidget(title_label)
296
- container_layout.addWidget(widget)
297
- container_layout.addItem(spacer)
298
- container_layout.setContentsMargins(5, 5, 5, 5)
299
- container_layout.setSpacing(5)
300
255
 
256
+ # Create a QScrollArea for the actual widget to ensure scrolling if the widget inside is too large
257
+ scroll_area = QScrollArea()
258
+ scroll_area.setFrameShape(QFrame.NoFrame)
259
+ scroll_area.setWidgetResizable(True)
260
+ # Let the scroll area expand in both directions if there's room
261
+ scroll_area.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
262
+ scroll_area.setWidget(widget)
263
+
264
+ # Put the scroll area in the container layout
265
+ container_layout.addWidget(scroll_area)
266
+
267
+ # Optionally stretch the scroll area to fill vertical space
268
+ container_layout.setStretchFactor(scroll_area, 1)
269
+
270
+ # Add container_widget to the stacked widget
301
271
  index = self.stack_widget.count()
302
272
  self.stack_widget.addWidget(container_widget)
303
273
 
274
+ # Add an action to the toolbar
304
275
  action = MaterialIconAction(icon_name=icon_name, tooltip=tooltip, checkable=True)
305
276
  self.toolbar.add_action(action_id, action, target_widget=self)
306
277
 
@@ -328,6 +299,11 @@ class SidePanel(QWidget):
328
299
  action.action.toggled.connect(on_action_toggled)
329
300
 
330
301
 
302
+ ############################################
303
+ # DEMO APPLICATION
304
+ ############################################
305
+
306
+
331
307
  class ExampleApp(QMainWindow): # pragma: no cover
332
308
  def __init__(self):
333
309
  super().__init__()
@@ -335,20 +311,24 @@ class ExampleApp(QMainWindow): # pragma: no cover
335
311
 
336
312
  central_widget = QWidget()
337
313
  self.setCentralWidget(central_widget)
338
-
339
- self.side_panel = SidePanel(self, orientation="left")
340
-
341
314
  self.layout = QHBoxLayout(central_widget)
342
315
 
316
+ # Create side panel
317
+ self.side_panel = SidePanel(self, orientation="left", panel_max_width=250)
343
318
  self.layout.addWidget(self.side_panel)
319
+
320
+ from bec_widgets.widgets.plots.waveform.waveform_widget import BECWaveformWidget
321
+
344
322
  self.plot = BECWaveformWidget()
345
323
  self.layout.addWidget(self.plot)
324
+
346
325
  self.add_side_menus()
347
326
 
348
327
  def add_side_menus(self):
349
328
  widget1 = QWidget()
350
- widget1_layout = QVBoxLayout(widget1)
351
- widget1_layout.addWidget(QLabel("This is Widget 1"))
329
+ layout1 = QVBoxLayout(widget1)
330
+ for i in range(15):
331
+ layout1.addWidget(QLabel(f"Widget 1 label row {i}"))
352
332
  self.side_panel.add_menu(
353
333
  action_id="widget1",
354
334
  icon_name="counter_1",
@@ -358,8 +338,8 @@ class ExampleApp(QMainWindow): # pragma: no cover
358
338
  )
359
339
 
360
340
  widget2 = QWidget()
361
- widget2_layout = QVBoxLayout(widget2)
362
- widget2_layout.addWidget(QLabel("This is Widget 2"))
341
+ layout2 = QVBoxLayout(widget2)
342
+ layout2.addWidget(QLabel("Short widget 2 content"))
363
343
  self.side_panel.add_menu(
364
344
  action_id="widget2",
365
345
  icon_name="counter_2",
@@ -369,8 +349,9 @@ class ExampleApp(QMainWindow): # pragma: no cover
369
349
  )
370
350
 
371
351
  widget3 = QWidget()
372
- widget3_layout = QVBoxLayout(widget3)
373
- widget3_layout.addWidget(QLabel("This is Widget 3"))
352
+ layout3 = QVBoxLayout(widget3)
353
+ for i in range(10):
354
+ layout3.addWidget(QLabel(f"Line {i} for Widget 3"))
374
355
  self.side_panel.add_menu(
375
356
  action_id="widget3",
376
357
  icon_name="counter_3",
@@ -383,6 +364,6 @@ class ExampleApp(QMainWindow): # pragma: no cover
383
364
  if __name__ == "__main__": # pragma: no cover
384
365
  app = QApplication(sys.argv)
385
366
  window = ExampleApp()
386
- window.resize(800, 600)
367
+ window.resize(1000, 700)
387
368
  window.show()
388
369
  sys.exit(app.exec())