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 +8 -0
- PKG-INFO +1 -1
- bec_widgets/applications/launch_window.py +1 -1
- bec_widgets/widgets/containers/main_window/addons/hover_widget.py +115 -0
- bec_widgets/widgets/containers/main_window/main_window.py +59 -29
- {bec_widgets-2.19.0.dist-info → bec_widgets-2.19.1.dist-info}/METADATA +1 -1
- {bec_widgets-2.19.0.dist-info → bec_widgets-2.19.1.dist-info}/RECORD +11 -10
- pyproject.toml +1 -1
- {bec_widgets-2.19.0.dist-info → bec_widgets-2.19.1.dist-info}/WHEEL +0 -0
- {bec_widgets-2.19.0.dist-info → bec_widgets-2.19.1.dist-info}/entry_points.txt +0 -0
- {bec_widgets-2.19.0.dist-info → bec_widgets-2.19.1.dist-info}/licenses/LICENSE +0 -0
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
@@ -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) <=
|
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.
|
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
|
-
|
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
|
-
|
111
|
-
|
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
|
-
#
|
118
|
-
|
119
|
-
self.
|
120
|
-
self.
|
121
|
-
self.
|
122
|
-
self.
|
123
|
-
self.
|
124
|
-
self.
|
125
|
-
self.
|
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.
|
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.
|
156
|
-
self.
|
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.
|
399
|
-
self.
|
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
|
|
@@ -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=
|
5
|
+
CHANGELOG.md,sha256=cgljCv9qwHDRGSH8ZR2OgXnJcjHWFaBJPykgoZPfRGc,308777
|
6
6
|
LICENSE,sha256=Daeiu871NcAp8uYi4eB_qHgvypG-HX0ioRQyQxFwjeg,1531
|
7
|
-
PKG-INFO,sha256=
|
7
|
+
PKG-INFO,sha256=OWmeyV7UILojOYZKa4X1xvw0OtxLZATOEfDb-RZWXUE,1256
|
8
8
|
README.md,sha256=oY5Jc1uXehRASuwUJ0umin2vfkFh7tHF-LLruHTaQx0,3560
|
9
|
-
pyproject.toml,sha256=
|
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=
|
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=
|
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.
|
423
|
-
bec_widgets-2.19.
|
424
|
-
bec_widgets-2.19.
|
425
|
-
bec_widgets-2.19.
|
426
|
-
bec_widgets-2.19.
|
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
File without changes
|
File without changes
|
File without changes
|