bec-widgets 2.1.3__py3-none-any.whl → 2.3.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,27 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## v2.3.0 (2025-05-09)
5
+
6
+ ### Features
7
+
8
+ - **bec_connector**: Ability to change object name during runtime
9
+ ([`dc151cd`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/dc151cdfe39f1f0507eeee307a35c1677ae4d8c5))
10
+
11
+
12
+ ## v2.2.0 (2025-05-09)
13
+
14
+ ### Features
15
+
16
+ - **launcher**: Add support for launching plugin widget
17
+ ([`1fb680a`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/1fb680abb40668e72007c245f32c80112466c46e))
18
+
19
+ ### Refactoring
20
+
21
+ - **launch_window**: Widget tile added
22
+ ([`b9e56c9`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/b9e56c96cbae561beb893cedb7d18e9b6a7bfc76))
23
+
24
+
4
25
  ## v2.1.3 (2025-05-07)
5
26
 
6
27
  ### Bug Fixes
PKG-INFO CHANGED
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bec_widgets
3
- Version: 2.1.3
3
+ Version: 2.3.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,10 +2,10 @@ from __future__ import annotations
2
2
 
3
3
  import os
4
4
  import xml.etree.ElementTree as ET
5
- from typing import TYPE_CHECKING
5
+ from typing import TYPE_CHECKING, Callable
6
6
 
7
7
  from bec_lib.logger import bec_logger
8
- from qtpy.QtCore import Qt, Signal
8
+ from qtpy.QtCore import Qt, Signal # type: ignore
9
9
  from qtpy.QtGui import QPainter, QPainterPath, QPixmap
10
10
  from qtpy.QtWidgets import (
11
11
  QApplication,
@@ -21,8 +21,10 @@ from qtpy.QtWidgets import (
21
21
 
22
22
  import bec_widgets
23
23
  from bec_widgets.cli.rpc.rpc_register import RPCRegister
24
+ from bec_widgets.utils.bec_plugin_helper import get_all_plugin_widgets
24
25
  from bec_widgets.utils.container_utils import WidgetContainerUtils
25
26
  from bec_widgets.utils.error_popups import SafeSlot
27
+ from bec_widgets.utils.name_utils import pascal_to_snake
26
28
  from bec_widgets.utils.plugin_utils import get_plugin_auto_updates
27
29
  from bec_widgets.utils.round_frame import RoundedFrame
28
30
  from bec_widgets.utils.toolbar import ModularToolBar
@@ -35,6 +37,8 @@ from bec_widgets.widgets.utility.visual.dark_mode_button.dark_mode_button import
35
37
  if TYPE_CHECKING: # pragma: no cover
36
38
  from qtpy.QtCore import QObject
37
39
 
40
+ from bec_widgets.utils.bec_widget import BECWidget
41
+
38
42
  logger = bec_logger.logger
39
43
  MODULE_PATH = os.path.dirname(bec_widgets.__file__)
40
44
 
@@ -141,6 +145,7 @@ class LaunchWindow(BECMainWindow):
141
145
  super().__init__(parent=parent, gui_id=gui_id, window_title=window_title, **kwargs)
142
146
 
143
147
  self.app = QApplication.instance()
148
+ self.tiles: dict[str, LaunchTile] = {}
144
149
 
145
150
  # Toolbar
146
151
  self.dark_mode_button = DarkModeButton(parent=self, toolbar=True)
@@ -156,58 +161,105 @@ class LaunchWindow(BECMainWindow):
156
161
  self.central_widget.layout = QHBoxLayout(self.central_widget)
157
162
  self.setCentralWidget(self.central_widget)
158
163
 
159
- self.tile_dock_area = LaunchTile(
164
+ self.register_tile(
165
+ name="dock_area",
160
166
  icon_path=os.path.join(MODULE_PATH, "assets", "app_icons", "bec_widgets_icon.png"),
161
167
  top_label="Get started",
162
168
  main_label="BEC Dock Area",
163
169
  description="Highly flexible and customizable dock area application with modular widgets.",
170
+ action_button=lambda: self.launch("dock_area"),
171
+ show_selector=False,
164
172
  )
165
- self.tile_dock_area.setFixedSize(*self.TILE_SIZE)
166
173
 
167
- self.tile_auto_update = LaunchTile(
174
+ self.available_auto_updates: dict[str, type[AutoUpdates]] = (
175
+ self._update_available_auto_updates()
176
+ )
177
+ self.register_tile(
178
+ name="auto_update",
168
179
  icon_path=os.path.join(MODULE_PATH, "assets", "app_icons", "auto_update.png"),
169
180
  top_label="Get automated",
170
181
  main_label="BEC Auto Update Dock Area",
171
182
  description="Dock area with auto update functionality for BEC widgets plotting.",
183
+ action_button=self._open_auto_update,
172
184
  show_selector=True,
185
+ selector_items=list(self.available_auto_updates.keys()) + ["Default"],
173
186
  )
174
- self.tile_auto_update.setFixedSize(*self.TILE_SIZE)
175
187
 
176
- self.tile_ui_file = LaunchTile(
188
+ self.register_tile(
189
+ name="custom_ui_file",
177
190
  icon_path=os.path.join(MODULE_PATH, "assets", "app_icons", "ui_loader_tile.png"),
178
191
  top_label="Get customized",
179
192
  main_label="Launch Custom UI File",
180
193
  description="GUI application with custom UI file.",
194
+ action_button=self._open_custom_ui_file,
195
+ show_selector=False,
181
196
  )
182
- self.tile_ui_file.setFixedSize(*self.TILE_SIZE)
183
-
184
- # Add tiles to the main layout
185
- self.central_widget.layout.addWidget(self.tile_dock_area)
186
- self.central_widget.layout.addWidget(self.tile_auto_update)
187
- self.central_widget.layout.addWidget(self.tile_ui_file)
188
197
 
189
- # hacky solution no time to waste
190
- self.tiles = [self.tile_dock_area, self.tile_auto_update, self.tile_ui_file]
198
+ # plugin widgets
199
+ self.available_widgets: dict[str, BECWidget] = get_all_plugin_widgets()
200
+ if self.available_widgets:
201
+ plugin_repo_name = next(iter(self.available_widgets.values())).__module__.split(".")[0]
202
+ plugin_repo_name = plugin_repo_name.removesuffix("_bec").upper()
203
+ self.register_tile(
204
+ name="widget",
205
+ icon_path=os.path.join(
206
+ MODULE_PATH, "assets", "app_icons", "widget_launch_tile.png"
207
+ ),
208
+ top_label="Get quickly started",
209
+ main_label=f"Launch a {plugin_repo_name} Widget",
210
+ description=f"GUI application with one widget from the {plugin_repo_name} repository.",
211
+ action_button=self._open_widget,
212
+ show_selector=True,
213
+ selector_items=list(self.available_widgets.keys()),
214
+ )
191
215
 
192
- # Connect signals
193
- self.tile_dock_area.action_button.clicked.connect(lambda: self.launch("dock_area"))
194
- self.tile_auto_update.action_button.clicked.connect(self._open_auto_update)
195
- self.tile_ui_file.action_button.clicked.connect(self._open_custom_ui_file)
196
216
  self._update_theme()
197
217
 
198
- # Auto updates
199
- self.available_auto_updates: dict[str, type[AutoUpdates]] = (
200
- self._update_available_auto_updates()
201
- )
202
- if self.tile_auto_update.selector is not None:
203
- self.tile_auto_update.selector.addItems(
204
- list(self.available_auto_updates.keys()) + ["Default"]
205
- )
206
-
207
218
  self.register = RPCRegister()
208
219
  self.register.callbacks.append(self._turn_off_the_lights)
209
220
  self.register.broadcast()
210
221
 
222
+ def register_tile(
223
+ self,
224
+ name: str,
225
+ icon_path: str | None = None,
226
+ top_label: str | None = None,
227
+ main_label: str | None = None,
228
+ description: str | None = None,
229
+ action_button: Callable | None = None,
230
+ show_selector: bool = False,
231
+ selector_items: list[str] | None = None,
232
+ ):
233
+ """
234
+ Register a tile in the launcher window.
235
+
236
+ Args:
237
+ name(str): The name of the tile.
238
+ icon_path(str): The path to the icon.
239
+ top_label(str): The top label of the tile.
240
+ main_label(str): The main label of the tile.
241
+ description(str): The description of the tile.
242
+ action_button(callable): The action to be performed when the button is clicked.
243
+ show_selector(bool): Whether to show a selector or not.
244
+ selector_items(list[str]): The items to be shown in the selector.
245
+ """
246
+
247
+ tile = LaunchTile(
248
+ icon_path=icon_path,
249
+ top_label=top_label,
250
+ main_label=main_label,
251
+ description=description,
252
+ show_selector=show_selector,
253
+ )
254
+ tile.setFixedSize(*self.TILE_SIZE)
255
+ if action_button:
256
+ tile.action_button.clicked.connect(action_button)
257
+ if show_selector and selector_items:
258
+ tile.selector.addItems(selector_items)
259
+ self.central_widget.layout.addWidget(tile)
260
+
261
+ self.tiles[name] = tile
262
+
211
263
  def launch(
212
264
  self,
213
265
  launch_script: str,
@@ -256,6 +308,12 @@ class LaunchWindow(BECMainWindow):
256
308
  auto_update = kwargs.pop("auto_update", None)
257
309
  return self._launch_auto_update(auto_update)
258
310
 
311
+ if launch_script == "widget":
312
+ widget = kwargs.pop("widget", None)
313
+ if widget is None:
314
+ raise ValueError("Widget name must be provided.")
315
+ return self._launch_widget(widget)
316
+
259
317
  launch = getattr(bw_launch, launch_script, None)
260
318
  if launch is None:
261
319
  raise ValueError(f"Launch script {launch_script} not found.")
@@ -273,6 +331,7 @@ class LaunchWindow(BECMainWindow):
273
331
  else:
274
332
  window = BECMainWindow()
275
333
  window.setCentralWidget(result_widget)
334
+ window.setWindowTitle(f"BEC - {result_widget.objectName()}")
276
335
  window.show()
277
336
  return result_widget
278
337
 
@@ -321,11 +380,28 @@ class LaunchWindow(BECMainWindow):
321
380
  window.show()
322
381
  return window
323
382
 
383
+ def _launch_widget(self, widget: type[BECWidget]) -> QWidget:
384
+ name = pascal_to_snake(widget.__name__)
385
+
386
+ WidgetContainerUtils.raise_for_invalid_name(name)
387
+
388
+ window = BECMainWindow()
389
+
390
+ widget_instance = widget(root_widget=True, object_name=name)
391
+ assert isinstance(widget_instance, QWidget)
392
+ QApplication.processEvents()
393
+
394
+ window.setCentralWidget(widget_instance)
395
+ window.resize(window.minimumSizeHint())
396
+ window.setWindowTitle(f"BEC - {widget_instance.objectName()}")
397
+ window.show()
398
+ return window
399
+
324
400
  def apply_theme(self, theme: str):
325
401
  """
326
402
  Change the theme of the application.
327
403
  """
328
- for tile in self.tiles:
404
+ for tile in self.tiles.values():
329
405
  tile.apply_theme(theme)
330
406
 
331
407
  super().apply_theme(theme)
@@ -334,14 +410,25 @@ class LaunchWindow(BECMainWindow):
334
410
  """
335
411
  Open the auto update window.
336
412
  """
337
- if self.tile_auto_update.selector is None:
413
+ if self.tiles["auto_update"].selector is None:
338
414
  auto_update = None
339
415
  else:
340
- auto_update = self.tile_auto_update.selector.currentText()
416
+ auto_update = self.tiles["auto_update"].selector.currentText()
341
417
  if auto_update == "Default":
342
418
  auto_update = None
343
419
  return self.launch("auto_update", auto_update=auto_update)
344
420
 
421
+ def _open_widget(self):
422
+ """
423
+ Open a widget from the available widgets.
424
+ """
425
+ if self.tiles["widget"].selector is None:
426
+ return
427
+ widget = self.tiles["widget"].selector.currentText()
428
+ if widget not in self.available_widgets:
429
+ raise ValueError(f"Widget {widget} not found in available widgets.")
430
+ return self.launch("widget", widget=self.available_widgets[widget])
431
+
345
432
  @SafeSlot(popup_error=True)
346
433
  def _open_custom_ui_file(self):
347
434
  """
@@ -205,6 +205,17 @@ class BECConnector:
205
205
  f"This is not necessarily an error as the parent may be deleted before the child and includes already a cleanup. The following exception was raised:\n{content}"
206
206
  )
207
207
 
208
+ def change_object_name(self, name: str) -> None:
209
+ """
210
+ Change the object name of the widget. Unregister old name and register the new one.
211
+
212
+ Args:
213
+ name (str): The new object name.
214
+ """
215
+ self.rpc_register.remove_rpc(self)
216
+ self.setObjectName(name.replace("-", "_").replace(" ", "_"))
217
+ QTimer.singleShot(0, self._update_object_name)
218
+
208
219
  def _update_object_name(self) -> None:
209
220
  """
210
221
  Enforce a unique object name among siblings and register the object for RPC.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bec_widgets
3
- Version: 2.1.3
3
+ Version: 2.3.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=1nMYldzVk0tFkBWYTcUjumOrdSADASheWOAc0kOFDYs,9509
3
3
  .pylintrc,sha256=eeY8YwSI74oFfq6IYIbCqnx3Vk8ZncKaatv96n_Y8Rs,18544
4
4
  .readthedocs.yaml,sha256=aSOc277LqXcsTI6lgvm_JY80lMlr69GbPKgivua2cS0,603
5
- CHANGELOG.md,sha256=CQ4p9tYotE-eBhFhSkRIIUaDLVfwrwgfRRWl0qY-tbI,271638
5
+ CHANGELOG.md,sha256=84qXQAMmKo7pVz1-jy1-rsVvR4anW_XFW_sa-hfaVwQ,272212
6
6
  LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
7
- PKG-INFO,sha256=2vaRvuGO7whPtxpWzC6vor_q5_343FZEy_YP4-5YQ3s,1224
7
+ PKG-INFO,sha256=VLRYtZJrG4rhrc2HYV5vKW6xKbxmg4G-C-yAtPLHqrI,1224
8
8
  README.md,sha256=KgdKusjlvEvFtdNZCeDMO91y77MWK2iDcYMDziksOr4,2553
9
- pyproject.toml,sha256=Dryj1F5zorGqGgBIYF6ayR-7gpQhVnlEtOmKlMNuqmM,2568
9
+ pyproject.toml,sha256=Cpof5gMH5hRjnHv3I9uwUER3V86lyW_eHUUcYfgku_s,2568
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
@@ -15,12 +15,13 @@ pyproject.toml,sha256=Dryj1F5zorGqGgBIYF6ayR-7gpQhVnlEtOmKlMNuqmM,2568
15
15
  bec_widgets/__init__.py,sha256=mZhbU6zfFt8-A7q_do74ie89budSevwpKZ6FKtEBdmo,170
16
16
  bec_widgets/applications/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  bec_widgets/applications/bw_launch.py,sha256=4lngXb8Ht_cvQtCwjjbAoqPNuE2V0Ja5WIPHpwgjcRI,687
18
- bec_widgets/applications/launch_window.py,sha256=MGkhdLhNODDILuWgBkOhcxe92kWCLUfvemNE3029GDA,15594
18
+ bec_widgets/applications/launch_window.py,sha256=SKmbhquqggpF76e0wZKPtttotRkNQua3HxaxqFF4TWk,18918
19
19
  bec_widgets/assets/app_icons/BEC-General-App.png,sha256=hc2ktly53DZAbl_rE3cb-vdRa5gtdCmBEjfwm2y5P4g,447581
20
20
  bec_widgets/assets/app_icons/alignment_1d.png,sha256=5VouaWieb4lVv3wUBNHaO5ovUW2Fk25aTKYQzOWy0mg,2071069
21
21
  bec_widgets/assets/app_icons/auto_update.png,sha256=YKoAJTWAlWzreYvOm0BttDRFr29tulolZgKirRhAFlA,2197066
22
22
  bec_widgets/assets/app_icons/bec_widgets_icon.png,sha256=K8dgGwIjalDh9PRHUsSQBqgdX7a00nM3igZdc20pkYM,1747017
23
23
  bec_widgets/assets/app_icons/ui_loader_tile.png,sha256=qSK3XHqvnAVGV9Q0ulORcGFbXJ9LDq2uz8l9uTtMsNk,1812476
24
+ bec_widgets/assets/app_icons/widget_launch_tile.png,sha256=bWsICHFfSe9-ESUj3AwlE95dDOea-f6M-s9fBapsxB4,2252911
24
25
  bec_widgets/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
26
  bec_widgets/cli/client.py,sha256=2Z0ihxMONJChLuhMxaRon0gNsnbuhTz4jlsSg-xAKEk,80350
26
27
  bec_widgets/cli/client_utils.py,sha256=F2hyt--jL53bN8NoWifNUMqwwx5FbpS6I1apERdTRzM,18114
@@ -47,7 +48,7 @@ bec_widgets/examples/plugin_example_pyside/tictactoetaskmenu.py,sha256=SiJaoX3OY
47
48
  bec_widgets/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
49
  bec_widgets/tests/utils.py,sha256=GbQtN7qf9n-8FoAfNddZ4aAqA7oBo_hGAlnKELd6Xzw,6943
49
50
  bec_widgets/utils/__init__.py,sha256=1930ji1Jj6dVuY81Wd2kYBhHYNV-2R0bN_L4o9zBj1U,533
50
- bec_widgets/utils/bec_connector.py,sha256=Ao_DN6oiHlpbVDHDK0RLpAgz2zDbhmwYee8yc3y7m2g,18875
51
+ bec_widgets/utils/bec_connector.py,sha256=ATOSyZqryn1QHPc7aotiDnUtzFhlj_gmcukMT_pqjHQ,19272
51
52
  bec_widgets/utils/bec_designer.py,sha256=ehNl_i743rijmhPiIGNd1bihE7-l4oJzTVoa4yjPjls,5426
52
53
  bec_widgets/utils/bec_dispatcher.py,sha256=y9EFIgU3JqIs7R10XnLh0I1_Skts9sbPe3ijOVJumYs,9837
53
54
  bec_widgets/utils/bec_plugin_helper.py,sha256=efg97QDAqhZYeqE1wk3vEgkFuhNewg4rvJrtB7mRCAE,3139
@@ -377,8 +378,8 @@ bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.py,sha256=O
377
378
  bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.pyproject,sha256=Lbi9zb6HNlIq14k6hlzR-oz6PIFShBuF7QxE6d87d64,34
378
379
  bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button_plugin.py,sha256=CzChz2SSETYsR8-36meqWnsXCT-FIy_J_xeU5coWDY8,1350
379
380
  bec_widgets/widgets/utility/visual/dark_mode_button/register_dark_mode_button.py,sha256=rMpZ1CaoucwobgPj1FuKTnt07W82bV1GaSYdoqcdMb8,521
380
- bec_widgets-2.1.3.dist-info/METADATA,sha256=2vaRvuGO7whPtxpWzC6vor_q5_343FZEy_YP4-5YQ3s,1224
381
- bec_widgets-2.1.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
382
- bec_widgets-2.1.3.dist-info/entry_points.txt,sha256=dItMzmwA1wizJ1Itx15qnfJ0ZzKVYFLVJ1voxT7K7D4,214
383
- bec_widgets-2.1.3.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
384
- bec_widgets-2.1.3.dist-info/RECORD,,
381
+ bec_widgets-2.3.0.dist-info/METADATA,sha256=VLRYtZJrG4rhrc2HYV5vKW6xKbxmg4G-C-yAtPLHqrI,1224
382
+ bec_widgets-2.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
383
+ bec_widgets-2.3.0.dist-info/entry_points.txt,sha256=dItMzmwA1wizJ1Itx15qnfJ0ZzKVYFLVJ1voxT7K7D4,214
384
+ bec_widgets-2.3.0.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
385
+ bec_widgets-2.3.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 = "2.1.3"
7
+ version = "2.3.0"
8
8
  description = "BEC Widgets"
9
9
  requires-python = ">=3.10"
10
10
  classifiers = [