bec-widgets 2.19.0__py3-none-any.whl → 2.19.1__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,14 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## v2.19.1 (2025-06-23)
5
+
6
+ ### Bug Fixes
7
+
8
+ - **launch_window**: Number of remaining connections extended to 4
9
+ ([`7484f51`](https://github.com/bec-project/bec_widgets/commit/7484f5160c8c6d632fd27996035ff6c0dda2e657))
10
+
11
+
4
12
  ## v2.19.0 (2025-06-23)
5
13
 
6
14
  ### Bug Fixes
PKG-INFO CHANGED
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bec_widgets
3
- Version: 2.19.0
3
+ Version: 2.19.1
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
@@ -542,7 +542,7 @@ class LaunchWindow(BECMainWindow):
542
542
  remaining_connections = [
543
543
  connection for connection in connections.values() if connection.parent_id != self.gui_id
544
544
  ]
545
- return len(remaining_connections) <= 2
545
+ return len(remaining_connections) <= 4
546
546
 
547
547
  def _turn_off_the_lights(self, connections: dict):
548
548
  """
@@ -0,0 +1,115 @@
1
+ import sys
2
+
3
+ from qtpy.QtCore import QPoint, Qt
4
+ from qtpy.QtWidgets import QApplication, QHBoxLayout, QLabel, QProgressBar, QVBoxLayout, QWidget
5
+
6
+
7
+ class WidgetTooltip(QWidget):
8
+ """Frameless, always-on-top window that behaves like a tooltip."""
9
+
10
+ def __init__(self, content: QWidget) -> None:
11
+ super().__init__(None, Qt.ToolTip | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
12
+ self.setAttribute(Qt.WA_ShowWithoutActivating)
13
+ self.setMouseTracking(True)
14
+ self.content = content
15
+
16
+ layout = QVBoxLayout(self)
17
+ layout.setContentsMargins(6, 6, 6, 6)
18
+ layout.addWidget(self.content)
19
+ self.adjustSize()
20
+
21
+ def leaveEvent(self, _event) -> None:
22
+ self.hide()
23
+
24
+ def show_above(self, global_pos: QPoint, offset: int = 8) -> None:
25
+ self.adjustSize()
26
+ screen = QApplication.screenAt(global_pos) or QApplication.primaryScreen()
27
+ screen_geo = screen.availableGeometry()
28
+ geom = self.geometry()
29
+
30
+ x = global_pos.x() - geom.width() // 2
31
+ y = global_pos.y() - geom.height() - offset
32
+
33
+ x = max(screen_geo.left(), min(x, screen_geo.right() - geom.width()))
34
+ y = max(screen_geo.top(), min(y, screen_geo.bottom() - geom.height()))
35
+
36
+ self.move(x, y)
37
+ self.show()
38
+
39
+
40
+ class HoverWidget(QWidget):
41
+
42
+ def __init__(self, parent: QWidget | None = None, *, simple: QWidget, full: QWidget):
43
+ super().__init__(parent)
44
+ self._simple = simple
45
+ self._full = full
46
+ self._full.setVisible(False)
47
+ self._tooltip = None
48
+
49
+ lay = QVBoxLayout(self)
50
+ lay.setContentsMargins(0, 0, 0, 0)
51
+ lay.addWidget(simple)
52
+
53
+ def enterEvent(self, event):
54
+ # suppress empty-label tooltips for labels
55
+ if isinstance(self._full, QLabel) and not self._full.text():
56
+ return
57
+
58
+ if self._tooltip is None: # first time only
59
+ self._tooltip = WidgetTooltip(self._full)
60
+ self._full.setVisible(True)
61
+
62
+ centre = self.mapToGlobal(self.rect().center())
63
+ self._tooltip.show_above(centre)
64
+ super().enterEvent(event)
65
+
66
+ def leaveEvent(self, event):
67
+ if self._tooltip and self._tooltip.isVisible():
68
+ self._tooltip.hide()
69
+ super().leaveEvent(event)
70
+
71
+ def close(self):
72
+ if self._tooltip:
73
+ self._tooltip.close()
74
+ self._tooltip.deleteLater()
75
+ self._tooltip = None
76
+ super().close()
77
+
78
+
79
+ ################################################################################
80
+ # Demo
81
+ # Just a simple example to show how the HoverWidget can be used to display
82
+ # a tooltip with a full widget inside (two different widgets are used
83
+ # for the simple and full versions).
84
+ ################################################################################
85
+
86
+
87
+ class DemoSimpleWidget(QLabel): # pragma: no cover
88
+ """A simple widget to be used as a trigger for the tooltip."""
89
+
90
+ def __init__(self) -> None:
91
+ super().__init__()
92
+ self.setText("Hover me for a preview!")
93
+
94
+
95
+ class DemoFullWidget(QProgressBar): # pragma: no cover
96
+ """A full widget to be shown in the tooltip."""
97
+
98
+ def __init__(self) -> None:
99
+ super().__init__()
100
+ self.setRange(0, 100)
101
+ self.setValue(75)
102
+ self.setFixedWidth(320)
103
+ self.setFixedHeight(30)
104
+
105
+
106
+ if __name__ == "__main__": # pragma: no cover
107
+ app = QApplication(sys.argv)
108
+
109
+ window = QWidget()
110
+ window.layout = QHBoxLayout(window)
111
+ hover_widget = HoverWidget(simple=DemoSimpleWidget(), full=DemoFullWidget())
112
+ window.layout.addWidget(hover_widget)
113
+ window.show()
114
+
115
+ sys.exit(app.exec_())
@@ -3,15 +3,7 @@ from __future__ import annotations
3
3
  import os
4
4
 
5
5
  from bec_lib.endpoints import MessageEndpoints
6
- from qtpy.QtCore import (
7
- QAbstractAnimation,
8
- QEasingCurve,
9
- QEvent,
10
- QPropertyAnimation,
11
- QSize,
12
- Qt,
13
- QTimer,
14
- )
6
+ from qtpy.QtCore import QEasingCurve, QEvent, QPropertyAnimation, QSize, Qt, QTimer
15
7
  from qtpy.QtGui import QAction, QActionGroup, QIcon
16
8
  from qtpy.QtWidgets import (
17
9
  QApplication,
@@ -30,6 +22,7 @@ from bec_widgets.utils.bec_widget import BECWidget
30
22
  from bec_widgets.utils.colors import apply_theme
31
23
  from bec_widgets.utils.error_popups import SafeSlot
32
24
  from bec_widgets.utils.widget_io import WidgetHierarchy
25
+ from bec_widgets.widgets.containers.main_window.addons.hover_widget import HoverWidget
33
26
  from bec_widgets.widgets.containers.main_window.addons.scroll_label import ScrollLabel
34
27
  from bec_widgets.widgets.containers.main_window.addons.web_links import BECWebLinksMixin
35
28
  from bec_widgets.widgets.progress.scan_progressbar.scan_progressbar import ScanProgressBar
@@ -96,33 +89,60 @@ class BECMainWindow(BECWidget, QMainWindow):
96
89
  self._add_separator()
97
90
 
98
91
  # Centre: Client‑info label (stretch=1 so it expands)
99
- self._client_info_label = ScrollLabel()
92
+ self._add_client_info_label()
93
+
94
+ # Add scan_progress bar with display logic
95
+ self._add_scan_progress_bar()
96
+
97
+ ################################################################################
98
+ # Client message status bar widget helpers
99
+
100
+ def _add_client_info_label(self):
101
+ """
102
+ Add a client info label to the status bar.
103
+ This label will display messages from the BEC dispatcher.
104
+ """
105
+
106
+ # Scroll label for client info in Status Bar
107
+ self._client_info_label = ScrollLabel(self)
100
108
  self._client_info_label.setAlignment(
101
109
  Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter
102
110
  )
103
- self.status_bar.addWidget(self._client_info_label, 1)
111
+ # Full label used in the hover widget
112
+ self._client_info_label_full = QLabel(self)
113
+ self._client_info_label_full.setAlignment(
114
+ Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter
115
+ )
116
+ # Hover widget to show the full client info label
117
+ self._client_info_hover = HoverWidget(
118
+ self, simple=self._client_info_label, full=self._client_info_label_full
119
+ )
120
+ self.status_bar.addWidget(self._client_info_hover, 1)
104
121
 
105
122
  # Timer to automatically clear client messages once they expire
106
123
  self._client_info_expire_timer = QTimer(self)
107
124
  self._client_info_expire_timer.setSingleShot(True)
108
125
  self._client_info_expire_timer.timeout.connect(lambda: self._client_info_label.setText(""))
109
-
110
- # Add scan_progress bar with display logic
111
- self._add_scan_progress_bar()
126
+ self._client_info_expire_timer.timeout.connect(
127
+ lambda: self._client_info_label_full.setText("")
128
+ )
112
129
 
113
130
  ################################################################################
114
131
  # Progress‑bar helpers
115
132
  def _add_scan_progress_bar(self):
116
133
 
117
- # --- Progress bar -------------------------------------------------
118
- # Scan progress bar minimalistic design setup
119
- self._scan_progress_bar = ScanProgressBar(self, one_line_design=True)
120
- self._scan_progress_bar.show_elapsed_time = False
121
- self._scan_progress_bar.show_remaining_time = False
122
- self._scan_progress_bar.show_source_label = False
123
- self._scan_progress_bar.progressbar.label_template = ""
124
- self._scan_progress_bar.progressbar.setFixedHeight(8)
125
- self._scan_progress_bar.progressbar.setFixedWidth(80)
134
+ # Setting HoverWidget for the scan progress bar - minimal and full version
135
+ self._scan_progress_bar_simple = ScanProgressBar(self, one_line_design=True)
136
+ self._scan_progress_bar_simple.show_elapsed_time = False
137
+ self._scan_progress_bar_simple.show_remaining_time = False
138
+ self._scan_progress_bar_simple.show_source_label = False
139
+ self._scan_progress_bar_simple.progressbar.label_template = ""
140
+ self._scan_progress_bar_simple.progressbar.setFixedHeight(8)
141
+ self._scan_progress_bar_simple.progressbar.setFixedWidth(80)
142
+ self._scan_progress_bar_full = ScanProgressBar(self)
143
+ self._scan_progress_hover = HoverWidget(
144
+ self, simple=self._scan_progress_bar_simple, full=self._scan_progress_bar_full
145
+ )
126
146
 
127
147
  # Bundle the progress bar with a separator
128
148
  separator = self._add_separator(separate_object=True)
@@ -133,7 +153,7 @@ class BECMainWindow(BECWidget, QMainWindow):
133
153
  self._scan_progress_bar_with_separator.layout.setContentsMargins(0, 0, 0, 0)
134
154
  self._scan_progress_bar_with_separator.layout.setSpacing(0)
135
155
  self._scan_progress_bar_with_separator.layout.addWidget(separator)
136
- self._scan_progress_bar_with_separator.layout.addWidget(self._scan_progress_bar)
156
+ self._scan_progress_bar_with_separator.layout.addWidget(self._scan_progress_hover)
137
157
 
138
158
  # Set Size
139
159
  self._scan_progress_bar_target_width = self.SCAN_PROGRESS_WIDTH
@@ -152,8 +172,8 @@ class BECMainWindow(BECWidget, QMainWindow):
152
172
  self._scan_progress_hide_timer.timeout.connect(self._animate_hide_scan_progress_bar)
153
173
 
154
174
  # Show / hide behaviour
155
- self._scan_progress_bar.progress_started.connect(self._show_scan_progress_bar)
156
- self._scan_progress_bar.progress_finished.connect(self._delay_hide_scan_progress_bar)
175
+ self._scan_progress_bar_simple.progress_started.connect(self._show_scan_progress_bar)
176
+ self._scan_progress_bar_simple.progress_finished.connect(self._delay_hide_scan_progress_bar)
157
177
 
158
178
  def _show_scan_progress_bar(self):
159
179
  if self._scan_progress_hide_timer.isActive():
@@ -342,10 +362,10 @@ class BECMainWindow(BECWidget, QMainWindow):
342
362
  msg(dict): The message to display, should contain:
343
363
  meta(dict): Metadata about the message, usually empty.
344
364
  """
345
- # self._client_info_label.setText("")
346
365
  message = msg.get("message", "")
347
366
  expiration = msg.get("expire", 0) # 0 → never expire
348
367
  self._client_info_label.setText(message)
368
+ self._client_info_label_full.setText(message)
349
369
 
350
370
  # Restart the expiration timer if necessary
351
371
  if hasattr(self, "_client_info_expire_timer") and self._client_info_expire_timer.isActive():
@@ -393,10 +413,20 @@ class BECMainWindow(BECWidget, QMainWindow):
393
413
  if hasattr(self, "_scan_progress_hide_timer") and self._scan_progress_hide_timer.isActive():
394
414
  self._scan_progress_hide_timer.stop()
395
415
 
416
+ ########################################
396
417
  # Status bar widgets cleanup
418
+
419
+ # Client info label cleanup
397
420
  self._client_info_label.cleanup()
398
- self._scan_progress_bar.close()
399
- self._scan_progress_bar.deleteLater()
421
+ self._client_info_hover.close()
422
+ self._client_info_hover.deleteLater()
423
+ # Scan progress bar cleanup
424
+ self._scan_progress_bar_simple.close()
425
+ self._scan_progress_bar_simple.deleteLater()
426
+ self._scan_progress_bar_full.close()
427
+ self._scan_progress_bar_full.deleteLater()
428
+ self._scan_progress_hover.close()
429
+ self._scan_progress_hover.deleteLater()
400
430
  super().cleanup()
401
431
 
402
432
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bec_widgets
3
- Version: 2.19.0
3
+ Version: 2.19.1
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=ivqg3HTaOxNbEW3bzWh9MXAkrekuGoNdj0Mj3SdRYuw,639
5
- CHANGELOG.md,sha256=TiLkAVElyNh0hZUpQhRMUL2ia_4XUKViiXQ1tXGbUQc,308560
5
+ CHANGELOG.md,sha256=cgljCv9qwHDRGSH8ZR2OgXnJcjHWFaBJPykgoZPfRGc,308777
6
6
  LICENSE,sha256=Daeiu871NcAp8uYi4eB_qHgvypG-HX0ioRQyQxFwjeg,1531
7
- PKG-INFO,sha256=FfdkFV0tAjMMbo_ISSEu8ttPkPCUnvuyTKflfEfOibY,1256
7
+ PKG-INFO,sha256=OWmeyV7UILojOYZKa4X1xvw0OtxLZATOEfDb-RZWXUE,1256
8
8
  README.md,sha256=oY5Jc1uXehRASuwUJ0umin2vfkFh7tHF-LLruHTaQx0,3560
9
- pyproject.toml,sha256=lynGhtdSMDjC80at55Wf9uEKKQFkvXfd6S1Gb20bQ70,2837
9
+ pyproject.toml,sha256=n4otjWlEQp1vBhXY8Aqi0GQK7KwHlyQ4V8BHQWgWwAE,2837
10
10
  .git_hooks/pre-commit,sha256=n3RofIZHJl8zfJJIUomcMyYGFi_rwq4CC19z0snz3FI,286
11
11
  .github/pull_request_template.md,sha256=F_cJXzooWMFgMGtLK-7KeGcQt0B4AYFse5oN0zQ9p6g,801
12
12
  .github/ISSUE_TEMPLATE/bug_report.yml,sha256=WdRnt7HGxvsIBLzhkaOWNfg8IJQYa_oV9_F08Ym6znQ,1081
@@ -28,7 +28,7 @@ pyproject.toml,sha256=lynGhtdSMDjC80at55Wf9uEKKQFkvXfd6S1Gb20bQ70,2837
28
28
  bec_widgets/__init__.py,sha256=mZhbU6zfFt8-A7q_do74ie89budSevwpKZ6FKtEBdmo,170
29
29
  bec_widgets/applications/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
30
  bec_widgets/applications/bw_launch.py,sha256=4lngXb8Ht_cvQtCwjjbAoqPNuE2V0Ja5WIPHpwgjcRI,687
31
- bec_widgets/applications/launch_window.py,sha256=dXe4FCWMdTfBmJz0hNaQsZQPGzGIG5ItBgm-ti6Z2Yw,21878
31
+ bec_widgets/applications/launch_window.py,sha256=KoUDoUr3gnZU4G075v4QkPUDW_gSpACwiqARlDD4_wQ,21878
32
32
  bec_widgets/assets/app_icons/BEC-General-App.png,sha256=hc2ktly53DZAbl_rE3cb-vdRa5gtdCmBEjfwm2y5P4g,447581
33
33
  bec_widgets/assets/app_icons/alignment_1d.png,sha256=5VouaWieb4lVv3wUBNHaO5ovUW2Fk25aTKYQzOWy0mg,2071069
34
34
  bec_widgets/assets/app_icons/auto_update.png,sha256=YKoAJTWAlWzreYvOm0BttDRFr29tulolZgKirRhAFlA,2197066
@@ -121,8 +121,9 @@ bec_widgets/widgets/containers/dock/register_dock_area.py,sha256=L7BL4qknCjtqsDP
121
121
  bec_widgets/widgets/containers/layout_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
122
122
  bec_widgets/widgets/containers/layout_manager/layout_manager.py,sha256=V7s8mtB3VLPstyGVaR9YKcoTVlfMMOYNpIJUsw2WQVc,35198
123
123
  bec_widgets/widgets/containers/main_window/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
124
- bec_widgets/widgets/containers/main_window/main_window.py,sha256=qW0AwMTTVAPWMcFUetZ2LAZbwSOEyyWS3_dvzQColvI,15326
124
+ bec_widgets/widgets/containers/main_window/main_window.py,sha256=mYNqS7ncMXZxoxWBjBvgvixB8a1_RyQwOC0dbCz0RY0,16972
125
125
  bec_widgets/widgets/containers/main_window/addons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
126
+ bec_widgets/widgets/containers/main_window/addons/hover_widget.py,sha256=KO639YbTx2Ru1yzQhB3PS6Y_shS4BGcj64pqPCYdEV8,3644
126
127
  bec_widgets/widgets/containers/main_window/addons/scroll_label.py,sha256=RDiLWDkIrXFAdCXNMm6eE6U9h3zjfPhJDqVwdV8aSL8,3743
127
128
  bec_widgets/widgets/containers/main_window/addons/web_links.py,sha256=d5OgzgI9zb-NAC0pOGanOtJX3nZoe4x8QuQTw-_hK_8,434
128
129
  bec_widgets/widgets/control/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -419,8 +420,8 @@ bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.py,sha256=O
419
420
  bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.pyproject,sha256=Lbi9zb6HNlIq14k6hlzR-oz6PIFShBuF7QxE6d87d64,34
420
421
  bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button_plugin.py,sha256=CzChz2SSETYsR8-36meqWnsXCT-FIy_J_xeU5coWDY8,1350
421
422
  bec_widgets/widgets/utility/visual/dark_mode_button/register_dark_mode_button.py,sha256=rMpZ1CaoucwobgPj1FuKTnt07W82bV1GaSYdoqcdMb8,521
422
- bec_widgets-2.19.0.dist-info/METADATA,sha256=FfdkFV0tAjMMbo_ISSEu8ttPkPCUnvuyTKflfEfOibY,1256
423
- bec_widgets-2.19.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
424
- bec_widgets-2.19.0.dist-info/entry_points.txt,sha256=dItMzmwA1wizJ1Itx15qnfJ0ZzKVYFLVJ1voxT7K7D4,214
425
- bec_widgets-2.19.0.dist-info/licenses/LICENSE,sha256=Daeiu871NcAp8uYi4eB_qHgvypG-HX0ioRQyQxFwjeg,1531
426
- bec_widgets-2.19.0.dist-info/RECORD,,
423
+ bec_widgets-2.19.1.dist-info/METADATA,sha256=OWmeyV7UILojOYZKa4X1xvw0OtxLZATOEfDb-RZWXUE,1256
424
+ bec_widgets-2.19.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
425
+ bec_widgets-2.19.1.dist-info/entry_points.txt,sha256=dItMzmwA1wizJ1Itx15qnfJ0ZzKVYFLVJ1voxT7K7D4,214
426
+ bec_widgets-2.19.1.dist-info/licenses/LICENSE,sha256=Daeiu871NcAp8uYi4eB_qHgvypG-HX0ioRQyQxFwjeg,1531
427
+ bec_widgets-2.19.1.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.19.0"
7
+ version = "2.19.1"
8
8
  description = "BEC Widgets"
9
9
  requires-python = ">=3.10"
10
10
  classifiers = [