bec-widgets 2.2.0__py3-none-any.whl → 2.4.3__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/ISSUE_TEMPLATE/bug_report.md +26 -0
- .github/ISSUE_TEMPLATE/feature_request.md +48 -0
- .github/workflows/check_pr.yml +28 -0
- .github/workflows/ci.yml +36 -0
- .github/workflows/end2end-conda.yml +48 -0
- .github/workflows/formatter.yml +61 -0
- .github/workflows/generate-cli-check.yml +49 -0
- .github/workflows/pytest-matrix.yml +48 -0
- .github/workflows/pytest.yml +64 -0
- .github/workflows/semantic_release.yml +103 -0
- CHANGELOG.md +1720 -1545
- LICENSE +1 -1
- PKG-INFO +2 -1
- README.md +11 -0
- bec_widgets/cli/client.py +11 -0
- bec_widgets/tests/utils.py +3 -3
- bec_widgets/utils/bec_connector.py +11 -0
- bec_widgets/utils/entry_validator.py +13 -3
- bec_widgets/utils/side_panel.py +65 -39
- bec_widgets/utils/toolbar.py +79 -0
- bec_widgets/widgets/containers/layout_manager/layout_manager.py +34 -1
- bec_widgets/widgets/control/device_input/base_classes/device_input_base.py +1 -1
- bec_widgets/widgets/control/device_input/base_classes/device_signal_input_base.py +27 -31
- bec_widgets/widgets/control/device_input/device_combobox/device_combobox.py +1 -1
- bec_widgets/widgets/control/device_input/device_line_edit/device_line_edit.py +1 -1
- bec_widgets/widgets/editors/dict_backed_table.py +7 -0
- bec_widgets/widgets/editors/scan_metadata/scan_metadata.py +1 -0
- bec_widgets/widgets/editors/web_console/register_web_console.py +15 -0
- bec_widgets/widgets/editors/web_console/web_console.py +230 -0
- bec_widgets/widgets/editors/web_console/web_console.pyproject +1 -0
- bec_widgets/widgets/editors/web_console/web_console_plugin.py +54 -0
- bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_tree.py +11 -46
- bec_widgets/widgets/utility/visual/color_button_native/__init__.py +0 -0
- bec_widgets/widgets/utility/visual/color_button_native/color_button_native.py +58 -0
- bec_widgets/widgets/utility/visual/color_button_native/color_button_native.pyproject +1 -0
- bec_widgets/widgets/utility/visual/color_button_native/color_button_native_plugin.py +56 -0
- bec_widgets/widgets/utility/visual/color_button_native/register_color_button_native.py +17 -0
- {bec_widgets-2.2.0.dist-info → bec_widgets-2.4.3.dist-info}/METADATA +2 -1
- {bec_widgets-2.2.0.dist-info → bec_widgets-2.4.3.dist-info}/RECORD +43 -24
- {bec_widgets-2.2.0.dist-info → bec_widgets-2.4.3.dist-info}/licenses/LICENSE +1 -1
- pyproject.toml +17 -5
- {bec_widgets-2.2.0.dist-info → bec_widgets-2.4.3.dist-info}/WHEEL +0 -0
- {bec_widgets-2.2.0.dist-info → bec_widgets-2.4.3.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,230 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import secrets
|
4
|
+
import subprocess
|
5
|
+
import time
|
6
|
+
|
7
|
+
from bec_lib.logger import bec_logger
|
8
|
+
from louie.saferef import safe_ref
|
9
|
+
from qtpy.QtCore import QUrl, qInstallMessageHandler
|
10
|
+
from qtpy.QtWebEngineWidgets import QWebEnginePage, QWebEngineView
|
11
|
+
from qtpy.QtWidgets import QApplication, QVBoxLayout, QWidget
|
12
|
+
|
13
|
+
from bec_widgets.utils.bec_widget import BECWidget
|
14
|
+
|
15
|
+
logger = bec_logger.logger
|
16
|
+
|
17
|
+
|
18
|
+
class WebConsoleRegistry:
|
19
|
+
"""
|
20
|
+
A registry for the WebConsole class to manage its instances.
|
21
|
+
"""
|
22
|
+
|
23
|
+
def __init__(self):
|
24
|
+
"""
|
25
|
+
Initialize the registry.
|
26
|
+
"""
|
27
|
+
self._instances = {}
|
28
|
+
self._server_process = None
|
29
|
+
self._server_port = None
|
30
|
+
self._token = secrets.token_hex(16)
|
31
|
+
|
32
|
+
def register(self, instance: WebConsole):
|
33
|
+
"""
|
34
|
+
Register an instance of WebConsole.
|
35
|
+
"""
|
36
|
+
self._instances[instance.gui_id] = safe_ref(instance)
|
37
|
+
self.cleanup()
|
38
|
+
|
39
|
+
if self._server_process is None:
|
40
|
+
# Start the ttyd server if not already running
|
41
|
+
self.start_ttyd()
|
42
|
+
|
43
|
+
def start_ttyd(self, use_zsh: bool | None = None):
|
44
|
+
"""
|
45
|
+
Start the ttyd server
|
46
|
+
ttyd -q -W -t 'theme={"background": "black"}' zsh
|
47
|
+
|
48
|
+
Args:
|
49
|
+
use_zsh (bool): Whether to use zsh or bash. If None, it will try to detect if zsh is available.
|
50
|
+
"""
|
51
|
+
|
52
|
+
# First, check if ttyd is installed
|
53
|
+
try:
|
54
|
+
subprocess.run(["ttyd", "--version"], check=True, stdout=subprocess.PIPE)
|
55
|
+
except FileNotFoundError:
|
56
|
+
# pylint: disable=raise-missing-from
|
57
|
+
raise RuntimeError("ttyd is not installed. Please install it first.")
|
58
|
+
|
59
|
+
if use_zsh is None:
|
60
|
+
# Check if we can use zsh
|
61
|
+
try:
|
62
|
+
subprocess.run(["zsh", "--version"], check=True, stdout=subprocess.PIPE)
|
63
|
+
use_zsh = True
|
64
|
+
except FileNotFoundError:
|
65
|
+
use_zsh = False
|
66
|
+
|
67
|
+
command = [
|
68
|
+
"ttyd",
|
69
|
+
"-p",
|
70
|
+
"0",
|
71
|
+
"-W",
|
72
|
+
"-t",
|
73
|
+
'theme={"background": "black"}',
|
74
|
+
"-c",
|
75
|
+
f"user:{self._token}",
|
76
|
+
]
|
77
|
+
if use_zsh:
|
78
|
+
command.append("zsh")
|
79
|
+
else:
|
80
|
+
command.append("bash")
|
81
|
+
|
82
|
+
# Start the ttyd server
|
83
|
+
self._server_process = subprocess.Popen(
|
84
|
+
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
85
|
+
)
|
86
|
+
|
87
|
+
self._wait_for_server_port()
|
88
|
+
|
89
|
+
self._server_process.stdout.close()
|
90
|
+
self._server_process.stderr.close()
|
91
|
+
|
92
|
+
def _wait_for_server_port(self, timeout: float = 10):
|
93
|
+
"""
|
94
|
+
Wait for the ttyd server to start and get the port number.
|
95
|
+
|
96
|
+
Args:
|
97
|
+
timeout (float): The timeout in seconds to wait for the server to start.
|
98
|
+
"""
|
99
|
+
start_time = time.time()
|
100
|
+
while True:
|
101
|
+
output = self._server_process.stderr.readline()
|
102
|
+
if output == b"" and self._server_process.poll() is not None:
|
103
|
+
break
|
104
|
+
if not output:
|
105
|
+
continue
|
106
|
+
|
107
|
+
output = output.decode("utf-8").strip()
|
108
|
+
if "Listening on" in output:
|
109
|
+
# Extract the port number from the output
|
110
|
+
self._server_port = int(output.split(":")[-1])
|
111
|
+
logger.info(f"ttyd server started on port {self._server_port}")
|
112
|
+
break
|
113
|
+
if time.time() - start_time > timeout:
|
114
|
+
raise TimeoutError(
|
115
|
+
"Timeout waiting for ttyd server to start. Please check if ttyd is installed and available in your PATH."
|
116
|
+
)
|
117
|
+
|
118
|
+
def cleanup(self):
|
119
|
+
"""
|
120
|
+
Clean up the registry by removing any instances that are no longer valid.
|
121
|
+
"""
|
122
|
+
for gui_id, weak_ref in list(self._instances.items()):
|
123
|
+
if weak_ref() is None:
|
124
|
+
del self._instances[gui_id]
|
125
|
+
|
126
|
+
if not self._instances and self._server_process:
|
127
|
+
# If no instances are left, terminate the server process
|
128
|
+
self._server_process.terminate()
|
129
|
+
self._server_process = None
|
130
|
+
self._server_port = None
|
131
|
+
logger.info("ttyd server terminated")
|
132
|
+
|
133
|
+
def unregister(self, instance: WebConsole):
|
134
|
+
"""
|
135
|
+
Unregister an instance of WebConsole.
|
136
|
+
|
137
|
+
Args:
|
138
|
+
instance (WebConsole): The instance to unregister.
|
139
|
+
"""
|
140
|
+
if instance.gui_id in self._instances:
|
141
|
+
del self._instances[instance.gui_id]
|
142
|
+
|
143
|
+
self.cleanup()
|
144
|
+
|
145
|
+
|
146
|
+
_web_console_registry = WebConsoleRegistry()
|
147
|
+
|
148
|
+
|
149
|
+
def suppress_qt_messages(type_, context, msg):
|
150
|
+
if context.category in ["js", "default"]:
|
151
|
+
return
|
152
|
+
print(msg)
|
153
|
+
|
154
|
+
|
155
|
+
qInstallMessageHandler(suppress_qt_messages)
|
156
|
+
|
157
|
+
|
158
|
+
class BECWebEnginePage(QWebEnginePage):
|
159
|
+
def javaScriptConsoleMessage(self, level, message, lineNumber, sourceID):
|
160
|
+
logger.info(f"[JS Console] {level.name} at line {lineNumber} in {sourceID}: {message}")
|
161
|
+
|
162
|
+
|
163
|
+
class WebConsole(BECWidget, QWidget):
|
164
|
+
"""
|
165
|
+
A simple widget to display a website
|
166
|
+
"""
|
167
|
+
|
168
|
+
PLUGIN = True
|
169
|
+
ICON_NAME = "terminal"
|
170
|
+
|
171
|
+
def __init__(self, parent=None, config=None, client=None, gui_id=None, **kwargs):
|
172
|
+
super().__init__(parent=parent, client=client, gui_id=gui_id, config=config, **kwargs)
|
173
|
+
_web_console_registry.register(self)
|
174
|
+
self._token = _web_console_registry._token
|
175
|
+
layout = QVBoxLayout()
|
176
|
+
layout.setContentsMargins(0, 0, 0, 0)
|
177
|
+
self.browser = QWebEngineView(self)
|
178
|
+
self.page = BECWebEnginePage(self)
|
179
|
+
self.page.authenticationRequired.connect(self._authenticate)
|
180
|
+
self.browser.setPage(self.page)
|
181
|
+
layout.addWidget(self.browser)
|
182
|
+
self.setLayout(layout)
|
183
|
+
self.page.setUrl(QUrl(f"http://localhost:{_web_console_registry._server_port}"))
|
184
|
+
|
185
|
+
def write(self, data: str, send_return: bool = True):
|
186
|
+
"""
|
187
|
+
Send data to the web page
|
188
|
+
"""
|
189
|
+
self.page.runJavaScript(f"window.term.paste('{data}');")
|
190
|
+
if send_return:
|
191
|
+
self.send_return()
|
192
|
+
|
193
|
+
def _authenticate(self, _, auth):
|
194
|
+
"""
|
195
|
+
Authenticate the request with the provided username and password.
|
196
|
+
"""
|
197
|
+
auth.setUser("user")
|
198
|
+
auth.setPassword(self._token)
|
199
|
+
|
200
|
+
def send_return(self):
|
201
|
+
"""
|
202
|
+
Send return to the web page
|
203
|
+
"""
|
204
|
+
self.page.runJavaScript(
|
205
|
+
"document.querySelector('textarea.xterm-helper-textarea').dispatchEvent(new KeyboardEvent('keypress', {charCode: 13}))"
|
206
|
+
)
|
207
|
+
|
208
|
+
def send_ctrl_c(self):
|
209
|
+
"""
|
210
|
+
Send Ctrl+C to the web page
|
211
|
+
"""
|
212
|
+
self.page.runJavaScript(
|
213
|
+
"document.querySelector('textarea.xterm-helper-textarea').dispatchEvent(new KeyboardEvent('keypress', {charCode: 3}))"
|
214
|
+
)
|
215
|
+
|
216
|
+
def cleanup(self):
|
217
|
+
"""
|
218
|
+
Clean up the registry by removing any instances that are no longer valid.
|
219
|
+
"""
|
220
|
+
_web_console_registry.unregister(self)
|
221
|
+
super().cleanup()
|
222
|
+
|
223
|
+
|
224
|
+
if __name__ == "__main__": # pragma: no cover
|
225
|
+
import sys
|
226
|
+
|
227
|
+
app = QApplication(sys.argv)
|
228
|
+
widget = WebConsole()
|
229
|
+
widget.show()
|
230
|
+
sys.exit(app.exec_())
|
@@ -0,0 +1 @@
|
|
1
|
+
{'files': ['web_console.py']}
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# Copyright (C) 2022 The Qt Company Ltd.
|
2
|
+
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
3
|
+
|
4
|
+
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
5
|
+
|
6
|
+
from bec_widgets.utils.bec_designer import designer_material_icon
|
7
|
+
from bec_widgets.widgets.editors.web_console.web_console import WebConsole
|
8
|
+
|
9
|
+
DOM_XML = """
|
10
|
+
<ui language='c++'>
|
11
|
+
<widget class='WebConsole' name='web_console'>
|
12
|
+
</widget>
|
13
|
+
</ui>
|
14
|
+
"""
|
15
|
+
|
16
|
+
|
17
|
+
class WebConsolePlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
18
|
+
def __init__(self):
|
19
|
+
super().__init__()
|
20
|
+
self._form_editor = None
|
21
|
+
|
22
|
+
def createWidget(self, parent):
|
23
|
+
t = WebConsole(parent)
|
24
|
+
return t
|
25
|
+
|
26
|
+
def domXml(self):
|
27
|
+
return DOM_XML
|
28
|
+
|
29
|
+
def group(self):
|
30
|
+
return "BEC Console"
|
31
|
+
|
32
|
+
def icon(self):
|
33
|
+
return designer_material_icon(WebConsole.ICON_NAME)
|
34
|
+
|
35
|
+
def includeFile(self):
|
36
|
+
return "web_console"
|
37
|
+
|
38
|
+
def initialize(self, form_editor):
|
39
|
+
self._form_editor = form_editor
|
40
|
+
|
41
|
+
def isContainer(self):
|
42
|
+
return False
|
43
|
+
|
44
|
+
def isInitialized(self):
|
45
|
+
return self._form_editor is not None
|
46
|
+
|
47
|
+
def name(self):
|
48
|
+
return "WebConsole"
|
49
|
+
|
50
|
+
def toolTip(self):
|
51
|
+
return ""
|
52
|
+
|
53
|
+
def whatsThis(self):
|
54
|
+
return self.toolTip()
|
@@ -31,6 +31,9 @@ from bec_widgets.widgets.control.device_input.device_line_edit.device_line_edit
|
|
31
31
|
)
|
32
32
|
from bec_widgets.widgets.dap.dap_combo_box.dap_combo_box import DapComboBox
|
33
33
|
from bec_widgets.widgets.plots.waveform.curve import CurveConfig, DeviceSignal
|
34
|
+
from bec_widgets.widgets.utility.visual.color_button_native.color_button_native import (
|
35
|
+
ColorButtonNative,
|
36
|
+
)
|
34
37
|
from bec_widgets.widgets.utility.visual.colormap_widget.colormap_widget import BECColorMapWidget
|
35
38
|
|
36
39
|
if TYPE_CHECKING: # pragma: no cover
|
@@ -40,49 +43,6 @@ if TYPE_CHECKING: # pragma: no cover
|
|
40
43
|
logger = bec_logger.logger
|
41
44
|
|
42
45
|
|
43
|
-
class ColorButton(QPushButton):
|
44
|
-
"""A QPushButton subclass that displays a color.
|
45
|
-
|
46
|
-
The background is set to the given color and the button text is the hex code.
|
47
|
-
The text color is chosen automatically (black if the background is light, white if dark)
|
48
|
-
to guarantee good readability.
|
49
|
-
"""
|
50
|
-
|
51
|
-
def __init__(self, color="#000000", parent=None):
|
52
|
-
"""Initialize the color button.
|
53
|
-
|
54
|
-
Args:
|
55
|
-
color (str): The initial color in hex format (e.g., '#000000').
|
56
|
-
parent: Optional QWidget parent.
|
57
|
-
"""
|
58
|
-
super().__init__(parent)
|
59
|
-
self.set_color(color)
|
60
|
-
|
61
|
-
def set_color(self, color):
|
62
|
-
"""Set the button's color and update its appearance.
|
63
|
-
|
64
|
-
Args:
|
65
|
-
color (str or QColor): The new color to assign.
|
66
|
-
"""
|
67
|
-
if isinstance(color, QColor):
|
68
|
-
self._color = color.name()
|
69
|
-
else:
|
70
|
-
self._color = color
|
71
|
-
self._update_appearance()
|
72
|
-
|
73
|
-
def color(self):
|
74
|
-
"""Return the current color in hex."""
|
75
|
-
return self._color
|
76
|
-
|
77
|
-
def _update_appearance(self):
|
78
|
-
"""Update the button style based on the background color's brightness."""
|
79
|
-
c = QColor(self._color)
|
80
|
-
brightness = c.lightnessF()
|
81
|
-
text_color = "#000000" if brightness > 0.5 else "#FFFFFF"
|
82
|
-
self.setStyleSheet(f"background-color: {self._color}; color: {text_color};")
|
83
|
-
self.setText(self._color)
|
84
|
-
|
85
|
-
|
86
46
|
class CurveRow(QTreeWidgetItem):
|
87
47
|
DELETE_BUTTON_COLOR = "#CC181E"
|
88
48
|
"""A unified row that can represent either a device or a DAP curve.
|
@@ -193,7 +153,7 @@ class CurveRow(QTreeWidgetItem):
|
|
193
153
|
def _init_style_controls(self):
|
194
154
|
"""Create columns 3..6: color button, style combo, width spin, symbol spin."""
|
195
155
|
# Color in col 3
|
196
|
-
self.color_button =
|
156
|
+
self.color_button = ColorButtonNative(color=self.config.color)
|
197
157
|
self.color_button.clicked.connect(lambda: self._select_color(self.color_button))
|
198
158
|
self.tree.setItemWidget(self, 3, self.color_button)
|
199
159
|
|
@@ -284,6 +244,11 @@ class CurveRow(QTreeWidgetItem):
|
|
284
244
|
self.dap_combo.deleteLater()
|
285
245
|
self.dap_combo = None
|
286
246
|
|
247
|
+
if getattr(self, "color_button", None) is not None:
|
248
|
+
self.color_button.close()
|
249
|
+
self.color_button.deleteLater()
|
250
|
+
self.color_button = None
|
251
|
+
|
287
252
|
# Remove the item from the tree widget
|
288
253
|
index = self.tree.indexOfTopLevelItem(self)
|
289
254
|
if index != -1:
|
@@ -337,8 +302,8 @@ class CurveRow(QTreeWidgetItem):
|
|
337
302
|
self.config.label = f"{parent_conf.label}-{new_dap}"
|
338
303
|
|
339
304
|
# Common style fields
|
340
|
-
self.config.color = self.color_button.color
|
341
|
-
self.config.symbol_color = self.color_button.color
|
305
|
+
self.config.color = self.color_button.color
|
306
|
+
self.config.symbol_color = self.color_button.color
|
342
307
|
self.config.pen_style = self.style_combo.currentText()
|
343
308
|
self.config.pen_width = self.width_spin.value()
|
344
309
|
self.config.symbol_size = self.symbol_spin.value()
|
File without changes
|
@@ -0,0 +1,58 @@
|
|
1
|
+
from qtpy.QtGui import QColor
|
2
|
+
from qtpy.QtWidgets import QPushButton
|
3
|
+
|
4
|
+
from bec_widgets import BECWidget, SafeProperty, SafeSlot
|
5
|
+
|
6
|
+
|
7
|
+
class ColorButtonNative(BECWidget, QPushButton):
|
8
|
+
"""A QPushButton subclass that displays a color.
|
9
|
+
|
10
|
+
The background is set to the given color and the button text is the hex code.
|
11
|
+
The text color is chosen automatically (black if the background is light, white if dark)
|
12
|
+
to guarantee good readability.
|
13
|
+
"""
|
14
|
+
|
15
|
+
RPC = False
|
16
|
+
PLUGIN = True
|
17
|
+
ICON_NAME = "colors"
|
18
|
+
|
19
|
+
def __init__(self, parent=None, color="#000000", **kwargs):
|
20
|
+
"""Initialize the color button.
|
21
|
+
|
22
|
+
Args:
|
23
|
+
parent: Optional QWidget parent.
|
24
|
+
color (str): The initial color in hex format (e.g., '#000000').
|
25
|
+
"""
|
26
|
+
super().__init__(parent=parent, **kwargs)
|
27
|
+
self.set_color(color)
|
28
|
+
|
29
|
+
@SafeSlot()
|
30
|
+
def set_color(self, color):
|
31
|
+
"""Set the button's color and update its appearance.
|
32
|
+
|
33
|
+
Args:
|
34
|
+
color (str or QColor): The new color to assign.
|
35
|
+
"""
|
36
|
+
if isinstance(color, QColor):
|
37
|
+
self._color = color.name()
|
38
|
+
else:
|
39
|
+
self._color = color
|
40
|
+
self._update_appearance()
|
41
|
+
|
42
|
+
@SafeProperty("QColor")
|
43
|
+
def color(self):
|
44
|
+
"""Return the current color in hex."""
|
45
|
+
return self._color
|
46
|
+
|
47
|
+
@color.setter
|
48
|
+
def color(self, value):
|
49
|
+
"""Set the button's color and update its appearance."""
|
50
|
+
self.set_color(value)
|
51
|
+
|
52
|
+
def _update_appearance(self):
|
53
|
+
"""Update the button style based on the background color's brightness."""
|
54
|
+
c = QColor(self._color)
|
55
|
+
brightness = c.lightnessF()
|
56
|
+
text_color = "#000000" if brightness > 0.5 else "#FFFFFF"
|
57
|
+
self.setStyleSheet(f"background-color: {self._color}; color: {text_color};")
|
58
|
+
self.setText(self._color)
|
@@ -0,0 +1 @@
|
|
1
|
+
{'files': ['color_button_native.py']}
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# Copyright (C) 2022 The Qt Company Ltd.
|
2
|
+
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
3
|
+
|
4
|
+
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
5
|
+
|
6
|
+
from bec_widgets.utils.bec_designer import designer_material_icon
|
7
|
+
from bec_widgets.widgets.utility.visual.color_button_native.color_button_native import (
|
8
|
+
ColorButtonNative,
|
9
|
+
)
|
10
|
+
|
11
|
+
DOM_XML = """
|
12
|
+
<ui language='c++'>
|
13
|
+
<widget class='ColorButtonNative' name='color_button_native'>
|
14
|
+
</widget>
|
15
|
+
</ui>
|
16
|
+
"""
|
17
|
+
|
18
|
+
|
19
|
+
class ColorButtonNativePlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
20
|
+
def __init__(self):
|
21
|
+
super().__init__()
|
22
|
+
self._form_editor = None
|
23
|
+
|
24
|
+
def createWidget(self, parent):
|
25
|
+
t = ColorButtonNative(parent)
|
26
|
+
return t
|
27
|
+
|
28
|
+
def domXml(self):
|
29
|
+
return DOM_XML
|
30
|
+
|
31
|
+
def group(self):
|
32
|
+
return "BEC Buttons"
|
33
|
+
|
34
|
+
def icon(self):
|
35
|
+
return designer_material_icon(ColorButtonNative.ICON_NAME)
|
36
|
+
|
37
|
+
def includeFile(self):
|
38
|
+
return "color_button_native"
|
39
|
+
|
40
|
+
def initialize(self, form_editor):
|
41
|
+
self._form_editor = form_editor
|
42
|
+
|
43
|
+
def isContainer(self):
|
44
|
+
return False
|
45
|
+
|
46
|
+
def isInitialized(self):
|
47
|
+
return self._form_editor is not None
|
48
|
+
|
49
|
+
def name(self):
|
50
|
+
return "ColorButtonNative"
|
51
|
+
|
52
|
+
def toolTip(self):
|
53
|
+
return "A QPushButton subclass that displays a color."
|
54
|
+
|
55
|
+
def whatsThis(self):
|
56
|
+
return self.toolTip()
|
@@ -0,0 +1,17 @@
|
|
1
|
+
def main(): # pragma: no cover
|
2
|
+
from qtpy import PYSIDE6
|
3
|
+
|
4
|
+
if not PYSIDE6:
|
5
|
+
print("PYSIDE6 is not available in the environment. Cannot patch designer.")
|
6
|
+
return
|
7
|
+
from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
|
8
|
+
|
9
|
+
from bec_widgets.widgets.utility.visual.color_button_native.color_button_native_plugin import (
|
10
|
+
ColorButtonNativePlugin,
|
11
|
+
)
|
12
|
+
|
13
|
+
QPyDesignerCustomWidgetCollection.addCustomWidget(ColorButtonNativePlugin())
|
14
|
+
|
15
|
+
|
16
|
+
if __name__ == "__main__": # pragma: no cover
|
17
|
+
main()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: bec_widgets
|
3
|
-
Version: 2.
|
3
|
+
Version: 2.4.3
|
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
|
@@ -25,6 +25,7 @@ Requires-Dist: coverage~=7.0; extra == 'dev'
|
|
25
25
|
Requires-Dist: fakeredis>=2.23.2,~=2.23; extra == 'dev'
|
26
26
|
Requires-Dist: isort>=5.13.2,~=5.13; extra == 'dev'
|
27
27
|
Requires-Dist: pytest-bec-e2e<=4.0,>=2.21.4; extra == 'dev'
|
28
|
+
Requires-Dist: pytest-cov~=6.1.1; extra == 'dev'
|
28
29
|
Requires-Dist: pytest-qt~=4.4; extra == 'dev'
|
29
30
|
Requires-Dist: pytest-random-order~=1.1; extra == 'dev'
|
30
31
|
Requires-Dist: pytest-timeout~=2.2; extra == 'dev'
|