bec-widgets 0.82.2__py3-none-any.whl → 0.83.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.
- .gitlab-ci.yml +3 -0
- CHANGELOG.md +42 -46
- PKG-INFO +2 -2
- bec_widgets/examples/jupyter_console/jupyter_console_window.py +2 -2
- bec_widgets/qt_utils/toolbar.py +3 -1
- bec_widgets/utils/bec_connector.py +2 -0
- bec_widgets/utils/bec_widget.py +6 -0
- bec_widgets/utils/colors.py +27 -0
- bec_widgets/utils/generate_designer_plugin.py +6 -5
- bec_widgets/utils/reference_utils.py +92 -0
- bec_widgets/widgets/bec_status_box/bec_status_box.py +2 -2
- bec_widgets/widgets/console/console.py +1 -1
- bec_widgets/widgets/device_box/__init__.py +0 -0
- bec_widgets/widgets/device_box/device_box.py +197 -0
- bec_widgets/widgets/device_box/device_box.pyproject +1 -0
- bec_widgets/widgets/device_box/device_box.ui +179 -0
- bec_widgets/widgets/device_box/device_box_plugin.py +54 -0
- bec_widgets/widgets/device_box/register_device_box.py +15 -0
- bec_widgets/widgets/figure/figure.py +5 -6
- bec_widgets/widgets/figure/plots/axis_settings.py +2 -2
- bec_widgets/widgets/position_indicator/position_indicator.py +71 -0
- bec_widgets/widgets/position_indicator/position_indicator.pyproject +1 -0
- bec_widgets/widgets/position_indicator/position_indicator_plugin.py +54 -0
- bec_widgets/widgets/position_indicator/register_position_indicator.py +17 -0
- bec_widgets/widgets/scan_control/scan_control.py +2 -2
- bec_widgets/widgets/spinner/__init__.py +0 -0
- bec_widgets/widgets/spinner/register_spinner_widget.py +15 -0
- bec_widgets/widgets/spinner/spinner.py +86 -0
- bec_widgets/widgets/spinner/spinner_widget.pyproject +1 -0
- bec_widgets/widgets/spinner/spinner_widget_plugin.py +54 -0
- bec_widgets/widgets/vscode/vscode.py +0 -14
- bec_widgets/widgets/website/website.py +1 -1
- {bec_widgets-0.82.2.dist-info → bec_widgets-0.83.1.dist-info}/METADATA +2 -2
- {bec_widgets-0.82.2.dist-info → bec_widgets-0.83.1.dist-info}/RECORD +46 -24
- pyproject.toml +2 -2
- tests/references/SpinnerWidget/SpinnerWidget_darwin.png +0 -0
- tests/references/SpinnerWidget/SpinnerWidget_linux.png +0 -0
- tests/references/SpinnerWidget/SpinnerWidget_started_darwin.png +0 -0
- tests/references/SpinnerWidget/SpinnerWidget_started_linux.png +0 -0
- tests/unit_tests/client_mocks.py +9 -1
- tests/unit_tests/test_device_box.py +98 -0
- tests/unit_tests/test_spinner.py +30 -0
- tests/unit_tests/test_vscode_widget.py +27 -32
- {bec_widgets-0.82.2.dist-info → bec_widgets-0.83.1.dist-info}/WHEEL +0 -0
- {bec_widgets-0.82.2.dist-info → bec_widgets-0.83.1.dist-info}/entry_points.txt +0 -0
- {bec_widgets-0.82.2.dist-info → bec_widgets-0.83.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,179 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<ui version="4.0">
|
3
|
+
<class>Form</class>
|
4
|
+
<widget class="QWidget" name="Form">
|
5
|
+
<property name="geometry">
|
6
|
+
<rect>
|
7
|
+
<x>0</x>
|
8
|
+
<y>0</y>
|
9
|
+
<width>251</width>
|
10
|
+
<height>289</height>
|
11
|
+
</rect>
|
12
|
+
</property>
|
13
|
+
<property name="minimumSize">
|
14
|
+
<size>
|
15
|
+
<width>0</width>
|
16
|
+
<height>192</height>
|
17
|
+
</size>
|
18
|
+
</property>
|
19
|
+
<property name="maximumSize">
|
20
|
+
<size>
|
21
|
+
<width>16777215</width>
|
22
|
+
<height>16777215</height>
|
23
|
+
</size>
|
24
|
+
</property>
|
25
|
+
<property name="windowTitle">
|
26
|
+
<string>Form</string>
|
27
|
+
</property>
|
28
|
+
<layout class="QVBoxLayout" name="verticalLayout">
|
29
|
+
<item>
|
30
|
+
<widget class="QGroupBox" name="device_box">
|
31
|
+
<property name="title">
|
32
|
+
<string>Device Name</string>
|
33
|
+
</property>
|
34
|
+
<layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0,0">
|
35
|
+
<property name="topMargin">
|
36
|
+
<number>0</number>
|
37
|
+
</property>
|
38
|
+
<item row="3" column="1">
|
39
|
+
<widget class="QDoubleSpinBox" name="step_size"/>
|
40
|
+
</item>
|
41
|
+
<item row="3" column="2">
|
42
|
+
<widget class="QToolButton" name="tweak_right">
|
43
|
+
<property name="minimumSize">
|
44
|
+
<size>
|
45
|
+
<width>50</width>
|
46
|
+
<height>50</height>
|
47
|
+
</size>
|
48
|
+
</property>
|
49
|
+
<property name="maximumSize">
|
50
|
+
<size>
|
51
|
+
<width>50</width>
|
52
|
+
<height>50</height>
|
53
|
+
</size>
|
54
|
+
</property>
|
55
|
+
<property name="text">
|
56
|
+
<string>...</string>
|
57
|
+
</property>
|
58
|
+
<property name="iconSize">
|
59
|
+
<size>
|
60
|
+
<width>30</width>
|
61
|
+
<height>30</height>
|
62
|
+
</size>
|
63
|
+
</property>
|
64
|
+
<property name="arrowType">
|
65
|
+
<enum>Qt::ArrowType::RightArrow</enum>
|
66
|
+
</property>
|
67
|
+
</widget>
|
68
|
+
</item>
|
69
|
+
<item row="2" column="0" colspan="3">
|
70
|
+
<widget class="QLineEdit" name="setpoint"/>
|
71
|
+
</item>
|
72
|
+
<item row="3" column="0">
|
73
|
+
<widget class="QToolButton" name="tweak_left">
|
74
|
+
<property name="minimumSize">
|
75
|
+
<size>
|
76
|
+
<width>50</width>
|
77
|
+
<height>50</height>
|
78
|
+
</size>
|
79
|
+
</property>
|
80
|
+
<property name="maximumSize">
|
81
|
+
<size>
|
82
|
+
<width>50</width>
|
83
|
+
<height>50</height>
|
84
|
+
</size>
|
85
|
+
</property>
|
86
|
+
<property name="text">
|
87
|
+
<string>...</string>
|
88
|
+
</property>
|
89
|
+
<property name="iconSize">
|
90
|
+
<size>
|
91
|
+
<width>30</width>
|
92
|
+
<height>30</height>
|
93
|
+
</size>
|
94
|
+
</property>
|
95
|
+
<property name="arrowType">
|
96
|
+
<enum>Qt::ArrowType::LeftArrow</enum>
|
97
|
+
</property>
|
98
|
+
</widget>
|
99
|
+
</item>
|
100
|
+
<item row="4" column="0" colspan="3">
|
101
|
+
<widget class="QPushButton" name="stop">
|
102
|
+
<property name="text">
|
103
|
+
<string>Stop</string>
|
104
|
+
</property>
|
105
|
+
</widget>
|
106
|
+
</item>
|
107
|
+
<item row="0" column="0" colspan="3">
|
108
|
+
<layout class="QVBoxLayout" name="verticalLayout_2">
|
109
|
+
<item>
|
110
|
+
<layout class="QHBoxLayout" name="horizontalLayout">
|
111
|
+
<item>
|
112
|
+
<spacer name="horizontalSpacer">
|
113
|
+
<property name="orientation">
|
114
|
+
<enum>Qt::Orientation::Horizontal</enum>
|
115
|
+
</property>
|
116
|
+
<property name="sizeType">
|
117
|
+
<enum>QSizePolicy::Policy::Expanding</enum>
|
118
|
+
</property>
|
119
|
+
<property name="sizeHint" stdset="0">
|
120
|
+
<size>
|
121
|
+
<width>40</width>
|
122
|
+
<height>20</height>
|
123
|
+
</size>
|
124
|
+
</property>
|
125
|
+
</spacer>
|
126
|
+
</item>
|
127
|
+
<item>
|
128
|
+
<widget class="SpinnerWidget" name="spinner_widget">
|
129
|
+
<property name="minimumSize">
|
130
|
+
<size>
|
131
|
+
<width>25</width>
|
132
|
+
<height>25</height>
|
133
|
+
</size>
|
134
|
+
</property>
|
135
|
+
<property name="maximumSize">
|
136
|
+
<size>
|
137
|
+
<width>25</width>
|
138
|
+
<height>25</height>
|
139
|
+
</size>
|
140
|
+
</property>
|
141
|
+
</widget>
|
142
|
+
</item>
|
143
|
+
</layout>
|
144
|
+
</item>
|
145
|
+
<item>
|
146
|
+
<widget class="PositionIndicator" name="position_indicator"/>
|
147
|
+
</item>
|
148
|
+
<item>
|
149
|
+
<widget class="QLabel" name="readback">
|
150
|
+
<property name="text">
|
151
|
+
<string>Position</string>
|
152
|
+
</property>
|
153
|
+
<property name="alignment">
|
154
|
+
<set>Qt::AlignmentFlag::AlignCenter</set>
|
155
|
+
</property>
|
156
|
+
</widget>
|
157
|
+
</item>
|
158
|
+
</layout>
|
159
|
+
</item>
|
160
|
+
</layout>
|
161
|
+
</widget>
|
162
|
+
</item>
|
163
|
+
</layout>
|
164
|
+
</widget>
|
165
|
+
<customwidgets>
|
166
|
+
<customwidget>
|
167
|
+
<class>SpinnerWidget</class>
|
168
|
+
<extends>QWidget</extends>
|
169
|
+
<header>spinner_widget</header>
|
170
|
+
</customwidget>
|
171
|
+
<customwidget>
|
172
|
+
<class>PositionIndicator</class>
|
173
|
+
<extends>QWidget</extends>
|
174
|
+
<header>position_indicator</header>
|
175
|
+
</customwidget>
|
176
|
+
</customwidgets>
|
177
|
+
<resources/>
|
178
|
+
<connections/>
|
179
|
+
</ui>
|
@@ -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
|
+
from qtpy.QtGui import QIcon
|
6
|
+
|
7
|
+
from bec_widgets.widgets.device_box.device_box import DeviceBox
|
8
|
+
|
9
|
+
DOM_XML = """
|
10
|
+
<ui language='c++'>
|
11
|
+
<widget class='DeviceBox' name='device_box'>
|
12
|
+
</widget>
|
13
|
+
</ui>
|
14
|
+
"""
|
15
|
+
|
16
|
+
|
17
|
+
class DeviceBoxPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
18
|
+
def __init__(self):
|
19
|
+
super().__init__()
|
20
|
+
self._form_editor = None
|
21
|
+
|
22
|
+
def createWidget(self, parent):
|
23
|
+
t = DeviceBox(parent)
|
24
|
+
return t
|
25
|
+
|
26
|
+
def domXml(self):
|
27
|
+
return DOM_XML
|
28
|
+
|
29
|
+
def group(self):
|
30
|
+
return "Device Control"
|
31
|
+
|
32
|
+
def icon(self):
|
33
|
+
return QIcon()
|
34
|
+
|
35
|
+
def includeFile(self):
|
36
|
+
return "device_box"
|
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 "DeviceBox"
|
49
|
+
|
50
|
+
def toolTip(self):
|
51
|
+
return "A widget for controlling a single positioner. "
|
52
|
+
|
53
|
+
def whatsThis(self):
|
54
|
+
return self.toolTip()
|
@@ -0,0 +1,15 @@
|
|
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.device_box.device_box_plugin import DeviceBoxPlugin
|
10
|
+
|
11
|
+
QPyDesignerCustomWidgetCollection.addCustomWidget(DeviceBoxPlugin())
|
12
|
+
|
13
|
+
|
14
|
+
if __name__ == "__main__": # pragma: no cover
|
15
|
+
main()
|
@@ -7,13 +7,13 @@ from typing import Literal, Optional
|
|
7
7
|
|
8
8
|
import numpy as np
|
9
9
|
import pyqtgraph as pg
|
10
|
-
import qdarktheme
|
11
10
|
from pydantic import Field, ValidationError, field_validator
|
12
11
|
from qtpy.QtCore import Signal as pyqtSignal
|
13
12
|
from qtpy.QtWidgets import QWidget
|
14
13
|
from typeguard import typechecked
|
15
14
|
|
16
15
|
from bec_widgets.utils import BECConnector, ConnectionConfig, WidgetContainerUtils
|
16
|
+
from bec_widgets.utils.colors import apply_theme
|
17
17
|
from bec_widgets.widgets.figure.plots.image.image import BECImageShow, ImageConfig
|
18
18
|
from bec_widgets.widgets.figure.plots.motor_map.motor_map import BECMotorMap, MotorMapConfig
|
19
19
|
from bec_widgets.widgets.figure.plots.plot_base import BECPlotBase, SubplotConfig
|
@@ -670,9 +670,8 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
|
670
670
|
Args:
|
671
671
|
theme(Literal["dark","light"]): The theme to set for the figure widget.
|
672
672
|
"""
|
673
|
-
qdarktheme.setup_theme(theme)
|
674
|
-
self.setBackground("k" if theme == "dark" else "w")
|
675
673
|
self.config.theme = theme
|
674
|
+
apply_theme(theme)
|
676
675
|
for plot in self.widget_list:
|
677
676
|
plot.set_x_label(plot.plot_item.getAxis("bottom").label.toPlainText())
|
678
677
|
plot.set_y_label(plot.plot_item.getAxis("left").label.toPlainText())
|
@@ -830,6 +829,6 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
|
830
829
|
widget_class=self.__class__.__name__, gui_id=self.gui_id, theme=theme
|
831
830
|
)
|
832
831
|
|
833
|
-
def cleanup(self):
|
834
|
-
|
835
|
-
|
832
|
+
# def cleanup(self):
|
833
|
+
# self.clear_all()
|
834
|
+
# super().cleanup()
|
@@ -1,10 +1,10 @@
|
|
1
1
|
import os
|
2
2
|
|
3
|
-
import qdarktheme
|
4
3
|
from qtpy.QtCore import Slot
|
5
4
|
from qtpy.QtWidgets import QVBoxLayout, QWidget
|
6
5
|
|
7
6
|
from bec_widgets.utils import UILoader
|
7
|
+
from bec_widgets.utils.colors import apply_theme
|
8
8
|
from bec_widgets.utils.widget_io import WidgetIO
|
9
9
|
|
10
10
|
|
@@ -55,7 +55,7 @@ if __name__ == "__main__":
|
|
55
55
|
from qtpy.QtWidgets import QApplication
|
56
56
|
|
57
57
|
app = QApplication(sys.argv)
|
58
|
-
|
58
|
+
apply_theme("dark")
|
59
59
|
window = AxisSettings()
|
60
60
|
window.show()
|
61
61
|
sys.exit(app.exec_())
|
@@ -0,0 +1,71 @@
|
|
1
|
+
from qtpy.QtCore import Qt, Slot
|
2
|
+
from qtpy.QtGui import QPainter, QPen
|
3
|
+
from qtpy.QtWidgets import QWidget
|
4
|
+
|
5
|
+
|
6
|
+
class PositionIndicator(QWidget):
|
7
|
+
|
8
|
+
def __init__(self, parent=None):
|
9
|
+
super().__init__(parent)
|
10
|
+
self.position = 0.5
|
11
|
+
self.min_value = 0
|
12
|
+
self.max_value = 100
|
13
|
+
self.scaling_factor = 0.5
|
14
|
+
self.setMinimumHeight(10)
|
15
|
+
|
16
|
+
def set_range(self, min_value, max_value):
|
17
|
+
self.min_value = min_value
|
18
|
+
self.max_value = max_value
|
19
|
+
|
20
|
+
@Slot(float)
|
21
|
+
def on_position_update(self, position: float):
|
22
|
+
self.position = position
|
23
|
+
self.update()
|
24
|
+
|
25
|
+
def paintEvent(self, event):
|
26
|
+
painter = QPainter(self)
|
27
|
+
painter.setRenderHint(QPainter.Antialiasing)
|
28
|
+
|
29
|
+
width = self.width()
|
30
|
+
height = self.height()
|
31
|
+
|
32
|
+
# Draw horizontal line
|
33
|
+
painter.setPen(Qt.black)
|
34
|
+
painter.drawLine(0, height // 2, width, height // 2)
|
35
|
+
|
36
|
+
# Draw shorter vertical line at the current position
|
37
|
+
x_pos = int(self.position * width)
|
38
|
+
painter.setPen(QPen(Qt.red, 2))
|
39
|
+
short_line_height = int(height * self.scaling_factor)
|
40
|
+
painter.drawLine(
|
41
|
+
x_pos,
|
42
|
+
(height // 2) - (short_line_height // 2),
|
43
|
+
x_pos,
|
44
|
+
(height // 2) + (short_line_height // 2),
|
45
|
+
)
|
46
|
+
|
47
|
+
# Draw thicker vertical lines at the ends
|
48
|
+
end_line_pen = QPen(Qt.blue, 5)
|
49
|
+
painter.setPen(end_line_pen)
|
50
|
+
painter.drawLine(0, 0, 0, height)
|
51
|
+
painter.drawLine(width - 1, 0, width - 1, height)
|
52
|
+
|
53
|
+
|
54
|
+
if __name__ == "__main__":
|
55
|
+
from qtpy.QtWidgets import QApplication, QSlider, QVBoxLayout
|
56
|
+
|
57
|
+
app = QApplication([])
|
58
|
+
|
59
|
+
position_indicator = PositionIndicator()
|
60
|
+
slider = QSlider(Qt.Horizontal)
|
61
|
+
slider.valueChanged.connect(lambda value: position_indicator.on_position_update(value / 100))
|
62
|
+
|
63
|
+
layout = QVBoxLayout()
|
64
|
+
layout.addWidget(position_indicator)
|
65
|
+
layout.addWidget(slider)
|
66
|
+
|
67
|
+
widget = QWidget()
|
68
|
+
widget.setLayout(layout)
|
69
|
+
widget.show()
|
70
|
+
|
71
|
+
app.exec_()
|
@@ -0,0 +1 @@
|
|
1
|
+
{'files': ['position_indicator.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
|
+
from qtpy.QtGui import QIcon
|
6
|
+
|
7
|
+
from bec_widgets.widgets.position_indicator.position_indicator import PositionIndicator
|
8
|
+
|
9
|
+
DOM_XML = """
|
10
|
+
<ui language='c++'>
|
11
|
+
<widget class='PositionIndicator' name='position_indicator'>
|
12
|
+
</widget>
|
13
|
+
</ui>
|
14
|
+
"""
|
15
|
+
|
16
|
+
|
17
|
+
class PositionIndicatorPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
18
|
+
def __init__(self):
|
19
|
+
super().__init__()
|
20
|
+
self._form_editor = None
|
21
|
+
|
22
|
+
def createWidget(self, parent):
|
23
|
+
t = PositionIndicator(parent)
|
24
|
+
return t
|
25
|
+
|
26
|
+
def domXml(self):
|
27
|
+
return DOM_XML
|
28
|
+
|
29
|
+
def group(self):
|
30
|
+
return ""
|
31
|
+
|
32
|
+
def icon(self):
|
33
|
+
return QIcon()
|
34
|
+
|
35
|
+
def includeFile(self):
|
36
|
+
return "position_indicator"
|
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 "PositionIndicator"
|
49
|
+
|
50
|
+
def toolTip(self):
|
51
|
+
return "PositionIndicator"
|
52
|
+
|
53
|
+
def whatsThis(self):
|
54
|
+
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.position_indicator.position_indicator_plugin import (
|
10
|
+
PositionIndicatorPlugin,
|
11
|
+
)
|
12
|
+
|
13
|
+
QPyDesignerCustomWidgetCollection.addCustomWidget(PositionIndicatorPlugin())
|
14
|
+
|
15
|
+
|
16
|
+
if __name__ == "__main__": # pragma: no cover
|
17
|
+
main()
|
@@ -1,4 +1,3 @@
|
|
1
|
-
import qdarktheme
|
2
1
|
from bec_lib.endpoints import MessageEndpoints
|
3
2
|
from qtpy.QtWidgets import (
|
4
3
|
QApplication,
|
@@ -12,6 +11,7 @@ from qtpy.QtWidgets import (
|
|
12
11
|
)
|
13
12
|
|
14
13
|
from bec_widgets.utils import BECConnector
|
14
|
+
from bec_widgets.utils.colors import apply_theme
|
15
15
|
from bec_widgets.widgets.scan_control.scan_group_box import ScanGroupBox
|
16
16
|
from bec_widgets.widgets.stop_button.stop_button import StopButton
|
17
17
|
|
@@ -206,7 +206,7 @@ if __name__ == "__main__": # pragma: no cover
|
|
206
206
|
app = QApplication([])
|
207
207
|
scan_control = ScanControl()
|
208
208
|
|
209
|
-
|
209
|
+
apply_theme("dark")
|
210
210
|
window = scan_control
|
211
211
|
window.show()
|
212
212
|
app.exec()
|
File without changes
|
@@ -0,0 +1,15 @@
|
|
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.spinner.spinner_widget_plugin import SpinnerWidgetPlugin
|
10
|
+
|
11
|
+
QPyDesignerCustomWidgetCollection.addCustomWidget(SpinnerWidgetPlugin())
|
12
|
+
|
13
|
+
|
14
|
+
if __name__ == "__main__": # pragma: no cover
|
15
|
+
main()
|
@@ -0,0 +1,86 @@
|
|
1
|
+
import sys
|
2
|
+
|
3
|
+
import numpy as np
|
4
|
+
from qtpy.QtCore import QRect, Qt, QTimer
|
5
|
+
from qtpy.QtGui import QColor, QPainter, QPen
|
6
|
+
from qtpy.QtWidgets import QApplication, QMainWindow, QWidget
|
7
|
+
|
8
|
+
from bec_widgets.utils.colors import get_theme_palette
|
9
|
+
|
10
|
+
|
11
|
+
def ease_in_out_sine(t):
|
12
|
+
return 1 - np.sin(np.pi * t)
|
13
|
+
|
14
|
+
|
15
|
+
class SpinnerWidget(QWidget):
|
16
|
+
def __init__(self, parent=None):
|
17
|
+
super().__init__(parent)
|
18
|
+
|
19
|
+
self.angle = 0
|
20
|
+
self.timer = QTimer(self)
|
21
|
+
self.timer.timeout.connect(self.rotate)
|
22
|
+
self.time = 0
|
23
|
+
self.duration = 50
|
24
|
+
self.speed = 50
|
25
|
+
self._started = False
|
26
|
+
|
27
|
+
def start(self):
|
28
|
+
if self._started:
|
29
|
+
return
|
30
|
+
self.timer.start(self.speed)
|
31
|
+
self._started = True
|
32
|
+
|
33
|
+
def stop(self):
|
34
|
+
if not self._started:
|
35
|
+
return
|
36
|
+
self.timer.stop()
|
37
|
+
self._started = False
|
38
|
+
self.update()
|
39
|
+
|
40
|
+
def rotate(self):
|
41
|
+
self.time = (self.time + 1) % self.duration
|
42
|
+
t = self.time / self.duration
|
43
|
+
easing_value = ease_in_out_sine(t)
|
44
|
+
self.angle -= (20 * easing_value) % 360 + 10
|
45
|
+
self.update()
|
46
|
+
|
47
|
+
def paintEvent(self, event):
|
48
|
+
painter = QPainter(self)
|
49
|
+
painter.setRenderHint(QPainter.Antialiasing)
|
50
|
+
size = min(self.width(), self.height())
|
51
|
+
rect = QRect(0, 0, size, size)
|
52
|
+
|
53
|
+
background_color = QColor(200, 200, 200, 50)
|
54
|
+
line_width = 5
|
55
|
+
|
56
|
+
color_palette = get_theme_palette()
|
57
|
+
|
58
|
+
color = QColor(color_palette.COLOR_ACCENT_4)
|
59
|
+
|
60
|
+
rect.adjust(line_width, line_width, -line_width, -line_width)
|
61
|
+
|
62
|
+
# Background arc
|
63
|
+
painter.setPen(QPen(background_color, line_width, Qt.SolidLine))
|
64
|
+
adjusted_rect = QRect(rect.left(), rect.top(), rect.width(), rect.height())
|
65
|
+
painter.drawArc(adjusted_rect, 0, 360 * 16)
|
66
|
+
|
67
|
+
if self._started:
|
68
|
+
# Foreground arc
|
69
|
+
pen = QPen(color, line_width, Qt.SolidLine)
|
70
|
+
pen.setCapStyle(Qt.RoundCap)
|
71
|
+
painter.setPen(pen)
|
72
|
+
proportion = 1 / 4
|
73
|
+
angle_span = int(proportion * 360 * 16)
|
74
|
+
angle_span += angle_span * ease_in_out_sine(self.time / self.duration)
|
75
|
+
painter.drawArc(adjusted_rect, self.angle * 16, int(angle_span))
|
76
|
+
painter.end()
|
77
|
+
|
78
|
+
|
79
|
+
if __name__ == "__main__": # pragma: no cover
|
80
|
+
app = QApplication(sys.argv)
|
81
|
+
window = QMainWindow()
|
82
|
+
widget = SpinnerWidget()
|
83
|
+
widget.start()
|
84
|
+
window.setCentralWidget(widget)
|
85
|
+
window.show()
|
86
|
+
sys.exit(app.exec())
|
@@ -0,0 +1 @@
|
|
1
|
+
{'files': ['spinner.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
|
+
from qtpy.QtGui import QIcon
|
6
|
+
|
7
|
+
from bec_widgets.widgets.spinner.spinner import SpinnerWidget
|
8
|
+
|
9
|
+
DOM_XML = """
|
10
|
+
<ui language='c++'>
|
11
|
+
<widget class='SpinnerWidget' name='spinner_widget'>
|
12
|
+
</widget>
|
13
|
+
</ui>
|
14
|
+
"""
|
15
|
+
|
16
|
+
|
17
|
+
class SpinnerWidgetPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
18
|
+
def __init__(self):
|
19
|
+
super().__init__()
|
20
|
+
self._form_editor = None
|
21
|
+
|
22
|
+
def createWidget(self, parent):
|
23
|
+
t = SpinnerWidget(parent)
|
24
|
+
return t
|
25
|
+
|
26
|
+
def domXml(self):
|
27
|
+
return DOM_XML
|
28
|
+
|
29
|
+
def group(self):
|
30
|
+
return ""
|
31
|
+
|
32
|
+
def icon(self):
|
33
|
+
return QIcon()
|
34
|
+
|
35
|
+
def includeFile(self):
|
36
|
+
return "spinner_widget"
|
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 "SpinnerWidget"
|
49
|
+
|
50
|
+
def toolTip(self):
|
51
|
+
return "SpinnerWidget"
|
52
|
+
|
53
|
+
def whatsThis(self):
|
54
|
+
return self.toolTip()
|
@@ -49,13 +49,6 @@ class VSCodeEditor(WebsiteWidget):
|
|
49
49
|
break
|
50
50
|
self.set_url(self._url)
|
51
51
|
|
52
|
-
def closeEvent(self, event):
|
53
|
-
"""
|
54
|
-
Hook for the close event to terminate the server.
|
55
|
-
"""
|
56
|
-
self.cleanup_vscode()
|
57
|
-
super().closeEvent(event)
|
58
|
-
|
59
52
|
def cleanup_vscode(self):
|
60
53
|
"""
|
61
54
|
Cleanup the VSCode editor.
|
@@ -72,13 +65,6 @@ class VSCodeEditor(WebsiteWidget):
|
|
72
65
|
self.cleanup_vscode()
|
73
66
|
return super().cleanup()
|
74
67
|
|
75
|
-
def close(self):
|
76
|
-
"""
|
77
|
-
Close the widget.
|
78
|
-
"""
|
79
|
-
self.cleanup_vscode()
|
80
|
-
return super().close()
|
81
|
-
|
82
68
|
|
83
69
|
if __name__ == "__main__": # pragma: no cover
|
84
70
|
import sys
|