bec-widgets 2.18.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.
- .github/workflows/formatter.yml +1 -1
- CHANGELOG.md +27 -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/widgets/services/device_browser/device_item/device_item.py +25 -5
- bec_widgets/widgets/services/device_browser/device_item/device_signal_display.py +102 -0
- bec_widgets/widgets/utility/signal_label/signal_label.py +24 -10
- {bec_widgets-2.18.0.dist-info → bec_widgets-2.19.1.dist-info}/METADATA +1 -1
- {bec_widgets-2.18.0.dist-info → bec_widgets-2.19.1.dist-info}/RECORD +15 -13
- pyproject.toml +1 -1
- {bec_widgets-2.18.0.dist-info → bec_widgets-2.19.1.dist-info}/WHEEL +0 -0
- {bec_widgets-2.18.0.dist-info → bec_widgets-2.19.1.dist-info}/entry_points.txt +0 -0
- {bec_widgets-2.18.0.dist-info → bec_widgets-2.19.1.dist-info}/licenses/LICENSE +0 -0
.github/workflows/formatter.yml
CHANGED
@@ -21,7 +21,7 @@ jobs:
|
|
21
21
|
isort --check --diff ./
|
22
22
|
|
23
23
|
- name: Check for disallowed imports from PySide
|
24
|
-
run: '! grep -re "from PySide6\." bec_widgets/ | grep -v -e "PySide6.QtDesigner" -e "PySide6.scripts"'
|
24
|
+
run: '! grep -re "from PySide6\." bec_widgets/ tests/ | grep -v -e "PySide6.QtDesigner" -e "PySide6.scripts"'
|
25
25
|
|
26
26
|
Pylint:
|
27
27
|
runs-on: ubuntu-latest
|
CHANGELOG.md
CHANGED
@@ -1,6 +1,33 @@
|
|
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
|
+
|
12
|
+
## v2.19.0 (2025-06-23)
|
13
|
+
|
14
|
+
### Bug Fixes
|
15
|
+
|
16
|
+
- **ci**: Extend check for pyside import to tests
|
17
|
+
([`d5a40da`](https://github.com/bec-project/bec_widgets/commit/d5a40dabc74753acad05e3eb6b121499fc1e03d7))
|
18
|
+
|
19
|
+
### Features
|
20
|
+
|
21
|
+
- (#494) add signal display to device browser
|
22
|
+
([`f3da6e9`](https://github.com/bec-project/bec_widgets/commit/f3da6e959e0416827ee5d02e34e6ad0ecfc8e5e7))
|
23
|
+
|
24
|
+
- (#494) add tabbed layout for device item
|
25
|
+
([`3378051`](https://github.com/bec-project/bec_widgets/commit/337805125098c3e028a17b74ef6d9ae4b9ba3d6d))
|
26
|
+
|
27
|
+
- (#494) display device signals
|
28
|
+
([`3a10341`](https://github.com/bec-project/bec_widgets/commit/3a103410e7448256a56b59bb3276fee056ec42a0))
|
29
|
+
|
30
|
+
|
4
31
|
## v2.18.0 (2025-06-22)
|
5
32
|
|
6
33
|
### 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
|
|
@@ -8,7 +8,7 @@ from bec_lib.logger import bec_logger
|
|
8
8
|
from bec_qthemes import material_icon
|
9
9
|
from qtpy.QtCore import QMimeData, QSize, Qt, Signal
|
10
10
|
from qtpy.QtGui import QDrag
|
11
|
-
from qtpy.QtWidgets import QApplication, QHBoxLayout, QToolButton, QWidget
|
11
|
+
from qtpy.QtWidgets import QApplication, QHBoxLayout, QTabWidget, QToolButton, QVBoxLayout, QWidget
|
12
12
|
|
13
13
|
from bec_widgets.utils.error_popups import SafeSlot
|
14
14
|
from bec_widgets.utils.expandable_frame import ExpandableGroupFrame
|
@@ -18,6 +18,9 @@ from bec_widgets.widgets.services.device_browser.device_item.device_config_dialo
|
|
18
18
|
from bec_widgets.widgets.services.device_browser.device_item.device_config_form import (
|
19
19
|
DeviceConfigForm,
|
20
20
|
)
|
21
|
+
from bec_widgets.widgets.services.device_browser.device_item.device_signal_display import (
|
22
|
+
SignalDisplay,
|
23
|
+
)
|
21
24
|
|
22
25
|
if TYPE_CHECKING: # pragma: no cover
|
23
26
|
from qtpy.QtGui import QMouseEvent
|
@@ -38,10 +41,25 @@ class DeviceItem(ExpandableGroupFrame):
|
|
38
41
|
self._expanded_first_time = False
|
39
42
|
self._data = None
|
40
43
|
self.device = device
|
41
|
-
layout = QHBoxLayout()
|
42
|
-
layout.setContentsMargins(0, 0, 0, 0)
|
43
|
-
self.set_layout(layout)
|
44
44
|
|
45
|
+
self._layout = QHBoxLayout()
|
46
|
+
self._layout.setContentsMargins(0, 0, 0, 0)
|
47
|
+
self._tab_widget = QTabWidget(tabShape=QTabWidget.TabShape.Rounded)
|
48
|
+
self._tab_widget.setDocumentMode(True)
|
49
|
+
self._layout.addWidget(self._tab_widget)
|
50
|
+
|
51
|
+
self.set_layout(self._layout)
|
52
|
+
|
53
|
+
self._form_page = QWidget()
|
54
|
+
self._form_page_layout = QVBoxLayout()
|
55
|
+
self._form_page.setLayout(self._form_page_layout)
|
56
|
+
|
57
|
+
self._signal_page = QWidget()
|
58
|
+
self._signal_page_layout = QVBoxLayout()
|
59
|
+
self._signal_page.setLayout(self._signal_page_layout)
|
60
|
+
|
61
|
+
self._tab_widget.addTab(self._form_page, "Configuration")
|
62
|
+
self._tab_widget.addTab(self._signal_page, "Signals")
|
45
63
|
self.adjustSize()
|
46
64
|
|
47
65
|
def _create_title_layout(self, title: str, icon: str):
|
@@ -64,7 +82,9 @@ class DeviceItem(ExpandableGroupFrame):
|
|
64
82
|
if not self.expanded and not self._expanded_first_time:
|
65
83
|
self._expanded_first_time = True
|
66
84
|
self.form = DeviceConfigForm(parent=self, pretty_display=True)
|
67
|
-
self.
|
85
|
+
self._form_page_layout.addWidget(self.form)
|
86
|
+
self.signals = SignalDisplay(parent=self, device=self.device)
|
87
|
+
self._signal_page_layout.addWidget(self.signals)
|
68
88
|
self._reload_config()
|
69
89
|
self.broadcast_size_hint.emit(self.sizeHint())
|
70
90
|
super().switch_expanded_state()
|
@@ -0,0 +1,102 @@
|
|
1
|
+
from bec_qthemes import material_icon
|
2
|
+
from qtpy.QtCore import Qt
|
3
|
+
from qtpy.QtWidgets import QHBoxLayout, QLabel, QToolButton, QVBoxLayout, QWidget
|
4
|
+
|
5
|
+
from bec_widgets.utils.bec_connector import ConnectionConfig
|
6
|
+
from bec_widgets.utils.bec_widget import BECWidget
|
7
|
+
from bec_widgets.utils.error_popups import SafeProperty, SafeSlot
|
8
|
+
from bec_widgets.widgets.containers.dock.dock import BECDock
|
9
|
+
from bec_widgets.widgets.utility.signal_label.signal_label import SignalLabel
|
10
|
+
|
11
|
+
|
12
|
+
class SignalDisplay(BECWidget, QWidget):
|
13
|
+
RPC = False
|
14
|
+
|
15
|
+
def __init__(
|
16
|
+
self,
|
17
|
+
client=None,
|
18
|
+
device: str = "",
|
19
|
+
config: ConnectionConfig = None,
|
20
|
+
gui_id: str | None = None,
|
21
|
+
theme_update: bool = False,
|
22
|
+
parent_dock: BECDock | None = None,
|
23
|
+
**kwargs,
|
24
|
+
):
|
25
|
+
"""A widget to display all the signals from a given device, and allow getting
|
26
|
+
a fresh reading."""
|
27
|
+
super().__init__(client, config, gui_id, theme_update, parent_dock, **kwargs)
|
28
|
+
self.get_bec_shortcuts()
|
29
|
+
self._layout = QVBoxLayout()
|
30
|
+
self.setLayout(self._layout)
|
31
|
+
self._content = QWidget()
|
32
|
+
self._layout.addWidget(self._content)
|
33
|
+
self._device = device
|
34
|
+
self.device = device
|
35
|
+
|
36
|
+
@SafeSlot()
|
37
|
+
def _refresh(self):
|
38
|
+
if self.device in self.dev:
|
39
|
+
self.dev.get(self.device).read(cached=False)
|
40
|
+
self.dev.get(self.device).read_configuration(cached=False)
|
41
|
+
|
42
|
+
def _add_refresh_button(self):
|
43
|
+
button_holder = QWidget()
|
44
|
+
button_holder.setLayout(QHBoxLayout())
|
45
|
+
button_holder.layout().setAlignment(Qt.AlignmentFlag.AlignRight)
|
46
|
+
button_holder.layout().setContentsMargins(0, 0, 0, 0)
|
47
|
+
refresh_button = QToolButton()
|
48
|
+
refresh_button.setIcon(
|
49
|
+
material_icon(icon_name="refresh", size=(20, 20), convert_to_pixmap=False)
|
50
|
+
)
|
51
|
+
refresh_button.clicked.connect(self._refresh)
|
52
|
+
button_holder.layout().addWidget(refresh_button)
|
53
|
+
self._content_layout.addWidget(button_holder)
|
54
|
+
|
55
|
+
def _populate(self):
|
56
|
+
self._content.deleteLater()
|
57
|
+
self._content = QWidget()
|
58
|
+
self._layout.addWidget(self._content)
|
59
|
+
self._content_layout = QVBoxLayout()
|
60
|
+
self._content_layout.setContentsMargins(0, 0, 0, 0)
|
61
|
+
self._content.setLayout(self._content_layout)
|
62
|
+
|
63
|
+
self._add_refresh_button()
|
64
|
+
|
65
|
+
if self._device in self.dev:
|
66
|
+
for sig in self.dev[self.device]._info.get("signals", {}).keys():
|
67
|
+
self._content_layout.addWidget(
|
68
|
+
SignalLabel(
|
69
|
+
device=self._device,
|
70
|
+
signal=sig,
|
71
|
+
show_select_button=False,
|
72
|
+
show_default_units=True,
|
73
|
+
)
|
74
|
+
)
|
75
|
+
self._content_layout.addStretch(1)
|
76
|
+
else:
|
77
|
+
self._content_layout.addWidget(
|
78
|
+
QLabel(f"Device {self.device} not found in device manager!")
|
79
|
+
)
|
80
|
+
|
81
|
+
@SafeProperty(str)
|
82
|
+
def device(self):
|
83
|
+
return self._device
|
84
|
+
|
85
|
+
@device.setter
|
86
|
+
def device(self, value: str):
|
87
|
+
self._device = value
|
88
|
+
self._populate()
|
89
|
+
|
90
|
+
|
91
|
+
if __name__ == "__main__": # pragma: no cover
|
92
|
+
import sys
|
93
|
+
|
94
|
+
from qtpy.QtWidgets import QApplication
|
95
|
+
|
96
|
+
from bec_widgets.utils.colors import set_theme
|
97
|
+
|
98
|
+
app = QApplication(sys.argv)
|
99
|
+
set_theme("light")
|
100
|
+
widget = SignalDisplay(device="samx")
|
101
|
+
widget.show()
|
102
|
+
sys.exit(app.exec_())
|
@@ -16,7 +16,6 @@ from qtpy.QtWidgets import (
|
|
16
16
|
QGroupBox,
|
17
17
|
QHBoxLayout,
|
18
18
|
QLabel,
|
19
|
-
QLineEdit,
|
20
19
|
QToolButton,
|
21
20
|
QVBoxLayout,
|
22
21
|
QWidget,
|
@@ -180,6 +179,7 @@ class SignalLabel(BECWidget, QWidget):
|
|
180
179
|
self._custom_units: str = custom_units
|
181
180
|
self._show_default_units: bool = show_default_units
|
182
181
|
self._decimal_places = 3
|
182
|
+
self._dtype = None
|
183
183
|
|
184
184
|
self._show_hinted_signals: bool = True
|
185
185
|
self._show_normal_signals: bool = False
|
@@ -241,8 +241,10 @@ class SignalLabel(BECWidget, QWidget):
|
|
241
241
|
"""Subscribe to the Redis topic for the device to display"""
|
242
242
|
if not self._connected and self._device and self._device in self.dev:
|
243
243
|
self._connected = True
|
244
|
-
self.
|
245
|
-
self.
|
244
|
+
self._read_endpoint = MessageEndpoints.device_read(self._device)
|
245
|
+
self._read_config_endpoint = MessageEndpoints.device_read_configuration(self._device)
|
246
|
+
self.bec_dispatcher.connect_slot(self.on_device_readback, self._read_endpoint)
|
247
|
+
self.bec_dispatcher.connect_slot(self.on_device_readback, self._read_config_endpoint)
|
246
248
|
self._manual_read()
|
247
249
|
self.set_display_value(self._value)
|
248
250
|
|
@@ -250,7 +252,8 @@ class SignalLabel(BECWidget, QWidget):
|
|
250
252
|
"""Unsubscribe from the Redis topic for the device to display"""
|
251
253
|
if self._connected:
|
252
254
|
self._connected = False
|
253
|
-
self.bec_dispatcher.disconnect_slot(self.on_device_readback, self.
|
255
|
+
self.bec_dispatcher.disconnect_slot(self.on_device_readback, self._read_endpoint)
|
256
|
+
self.bec_dispatcher.disconnect_slot(self.on_device_readback, self._read_config_endpoint)
|
254
257
|
|
255
258
|
def _manual_read(self):
|
256
259
|
if self._device is None or not isinstance(
|
@@ -259,8 +262,13 @@ class SignalLabel(BECWidget, QWidget):
|
|
259
262
|
self._units = ""
|
260
263
|
self._value = "__"
|
261
264
|
return
|
262
|
-
signal
|
263
|
-
|
265
|
+
signal, info = (
|
266
|
+
(
|
267
|
+
getattr(device, self.signal, None),
|
268
|
+
device._info.get("signals", {}).get(self._signal, {}).get("describe", {}),
|
269
|
+
)
|
270
|
+
if isinstance(device, Device)
|
271
|
+
else (device, device.describe().get(self._device))
|
264
272
|
)
|
265
273
|
if not isinstance(signal, Signal): # Avoid getting other attributes of device, e.g. methods
|
266
274
|
signal = None
|
@@ -269,7 +277,8 @@ class SignalLabel(BECWidget, QWidget):
|
|
269
277
|
self._value = "__"
|
270
278
|
return
|
271
279
|
self._value = signal.get()
|
272
|
-
self._units =
|
280
|
+
self._units = info.get("egu", "")
|
281
|
+
self._dtype = info.get("dtype", "float")
|
273
282
|
|
274
283
|
@SafeSlot(dict, dict)
|
275
284
|
def on_device_readback(self, msg: dict, metadata: dict) -> None:
|
@@ -278,8 +287,10 @@ class SignalLabel(BECWidget, QWidget):
|
|
278
287
|
"""
|
279
288
|
try:
|
280
289
|
signal_to_read = self._patch_hinted_signal()
|
281
|
-
|
282
|
-
|
290
|
+
_value = msg["signals"].get(signal_to_read, {}).get("value")
|
291
|
+
if _value is not None:
|
292
|
+
self._value = _value
|
293
|
+
self.set_display_value(self._value)
|
283
294
|
except Exception as e:
|
284
295
|
self._display.setText("ERROR!")
|
285
296
|
self._display.setToolTip(
|
@@ -401,7 +412,10 @@ class SignalLabel(BECWidget, QWidget):
|
|
401
412
|
if self._decimal_places == 0:
|
402
413
|
return value
|
403
414
|
try:
|
404
|
-
|
415
|
+
if self._dtype in ("integer", "float"):
|
416
|
+
return f"{float(value):0.{self._decimal_places}f}"
|
417
|
+
else:
|
418
|
+
return str(value)
|
405
419
|
except ValueError:
|
406
420
|
return value
|
407
421
|
|
@@ -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
|
@@ -18,7 +18,7 @@ pyproject.toml,sha256=fkRRDfPjHIHnke8FkImzcWn2zwiPeOYhhQE5lzMuUD4,2837
|
|
18
18
|
.github/workflows/check_pr.yml,sha256=jKMtYBJTNRAdw_MAS4JzdKVSoNrv8nxUQMqobsYFAXw,903
|
19
19
|
.github/workflows/ci.yml,sha256=OVZt0UxN4wQInuMucuQcKsvHDiz27sLeuQRskxjtkY0,1863
|
20
20
|
.github/workflows/end2end-conda.yml,sha256=yTH-CS8xxQ7kMo4BDpWwOeef1xmsV6gyrgqnFPHTo30,2278
|
21
|
-
.github/workflows/formatter.yml,sha256=
|
21
|
+
.github/workflows/formatter.yml,sha256=yldGjVF8zckPXho9bgN2_YNCq-5ZWN8pxTYrJNDvCbY,1925
|
22
22
|
.github/workflows/generate-cli-check.yml,sha256=b6TcK8F5Hy0sFjgXpk0w3BO9eMDZw9WysTl3P7zEPuQ,1742
|
23
23
|
.github/workflows/pytest-matrix.yml,sha256=0gL5wNPJKJF1JapqstlYNYiJ44ko05uaTD7epa7smVw,1834
|
24
24
|
.github/workflows/pytest.yml,sha256=hYOB7XK_79MaiELaTH7zDT-WRw-pRDe4mHyB_WfcGDc,1747
|
@@ -28,7 +28,7 @@ pyproject.toml,sha256=fkRRDfPjHIHnke8FkImzcWn2zwiPeOYhhQE5lzMuUD4,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
|
@@ -365,7 +366,8 @@ bec_widgets/widgets/services/device_browser/util.py,sha256=aDtRa53L4-CGn4rM9IKqU
|
|
365
366
|
bec_widgets/widgets/services/device_browser/device_item/__init__.py,sha256=VGY-uNVCnpcY-q-gijteB2N8KxFNgYR-qQ209MVu1QI,36
|
366
367
|
bec_widgets/widgets/services/device_browser/device_item/device_config_dialog.py,sha256=183LABi4767PptQHy8mfV1I6MFlDGmC1vekFqjtkOuk,8748
|
367
368
|
bec_widgets/widgets/services/device_browser/device_item/device_config_form.py,sha256=Bw2sxvszs2qPe5WCUV_J4P6iuWSEQc0I37cyr9Wat30,2138
|
368
|
-
bec_widgets/widgets/services/device_browser/device_item/device_item.py,sha256=
|
369
|
+
bec_widgets/widgets/services/device_browser/device_item/device_item.py,sha256=OdBPwIzqKSeeOWZ86sct5d_m17HdIy5aQlCL-Hic754,6135
|
370
|
+
bec_widgets/widgets/services/device_browser/device_item/device_signal_display.py,sha256=Olt5AHl7qEHKAiNvjYanA_3ZuZyC0ZMswmlwGGoOPkI,3457
|
369
371
|
bec_widgets/widgets/utility/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
370
372
|
bec_widgets/widgets/utility/logpanel/__init__.py,sha256=HldSvPLYgrqBjCgIQj0f7Wa4slkSMksk4bsRJOQi__Y,91
|
371
373
|
bec_widgets/widgets/utility/logpanel/_util.py,sha256=GqzHbdOTmWBru9OR4weeYdziWj_cWxqSJhS4_6W3Qjg,1836
|
@@ -374,7 +376,7 @@ bec_widgets/widgets/utility/logpanel/log_panel_plugin.py,sha256=KY7eS1uGZzLYtDAd
|
|
374
376
|
bec_widgets/widgets/utility/logpanel/logpanel.py,sha256=5c59r1Z368mqIZhS_0075P4gg2G1sK5NvPFMK5B1DuQ,20861
|
375
377
|
bec_widgets/widgets/utility/logpanel/register_log_panel.py,sha256=LFUE5JzCYvIwJQtTqZASLVAHYy3gO1nrHzPVH_kpCEY,470
|
376
378
|
bec_widgets/widgets/utility/signal_label/register_signal_label.py,sha256=wDB4Q3dSbZ51hsxnuB74oXdMRoLgDRd-XfhaomYY2OA,483
|
377
|
-
bec_widgets/widgets/utility/signal_label/signal_label.py,sha256=
|
379
|
+
bec_widgets/widgets/utility/signal_label/signal_label.py,sha256=ErIyoqKmmga65wBJeXhvWt1FhTdybfkKkqMFzdVl_ig,16571
|
378
380
|
bec_widgets/widgets/utility/signal_label/signal_label.pyproject,sha256=DXgt__AGnPCqXo5A92aTPH0SxbWlvgyNzKraWUuumWg,30
|
379
381
|
bec_widgets/widgets/utility/signal_label/signal_label_plugin.py,sha256=ZXR8oYl4NkPcXJ8pgDcdXcg3teB0MHtoNZoiGDmgCoU,1298
|
380
382
|
bec_widgets/widgets/utility/spinbox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -418,8 +420,8 @@ bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.py,sha256=O
|
|
418
420
|
bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.pyproject,sha256=Lbi9zb6HNlIq14k6hlzR-oz6PIFShBuF7QxE6d87d64,34
|
419
421
|
bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button_plugin.py,sha256=CzChz2SSETYsR8-36meqWnsXCT-FIy_J_xeU5coWDY8,1350
|
420
422
|
bec_widgets/widgets/utility/visual/dark_mode_button/register_dark_mode_button.py,sha256=rMpZ1CaoucwobgPj1FuKTnt07W82bV1GaSYdoqcdMb8,521
|
421
|
-
bec_widgets-2.
|
422
|
-
bec_widgets-2.
|
423
|
-
bec_widgets-2.
|
424
|
-
bec_widgets-2.
|
425
|
-
bec_widgets-2.
|
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
|