bec-widgets 0.79.2__py3-none-any.whl → 0.80.0__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 +0 -1
- .pylintrc +1 -1
- CHANGELOG.md +24 -30
- PKG-INFO +1 -4
- README.md +2 -2
- bec_widgets/cli/client.py +2 -2
- bec_widgets/cli/generate_cli.py +11 -10
- bec_widgets/cli/rpc_wigdet_handler.py +1 -1
- bec_widgets/utils/bec_connector.py +2 -1
- bec_widgets/utils/bec_dispatcher.py +3 -3
- bec_widgets/utils/bec_widget.py +2 -0
- bec_widgets/utils/plugin_utils.py +80 -10
- bec_widgets/utils/ui_loader.py +85 -26
- bec_widgets/widgets/bec_status_box/bec_status_box.py +111 -151
- bec_widgets/widgets/bec_status_box/bec_status_box.pyproject +1 -0
- bec_widgets/widgets/bec_status_box/bec_status_box_plugin.py +54 -0
- bec_widgets/widgets/bec_status_box/register_bec_status_box.py +15 -0
- bec_widgets/widgets/bec_status_box/status_item.py +21 -54
- {bec_widgets-0.79.2.dist-info → bec_widgets-0.80.0.dist-info}/METADATA +1 -4
- {bec_widgets-0.79.2.dist-info → bec_widgets-0.80.0.dist-info}/RECORD +29 -25
- docs/introduction/introduction.md +1 -1
- docs/user/getting_started/installation.md +2 -2
- pyproject.toml +1 -2
- tests/unit_tests/test_bec_status_box.py +26 -24
- tests/unit_tests/test_generate_cli_client.py +41 -17
- tests/unit_tests/test_plugin_utils.py +2 -3
- {bec_widgets-0.79.2.dist-info → bec_widgets-0.80.0.dist-info}/WHEEL +0 -0
- {bec_widgets-0.79.2.dist-info → bec_widgets-0.80.0.dist-info}/entry_points.txt +0 -0
- {bec_widgets-0.79.2.dist-info → bec_widgets-0.80.0.dist-info}/licenses/LICENSE +0 -0
.gitlab-ci.yml
CHANGED
.pylintrc
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# A comma-separated list of package or module names from where C extensions may
|
4
4
|
# be loaded. Extensions are loading into the active Python interpreter and may
|
5
5
|
# run arbitrary code.
|
6
|
-
extension-pkg-allow-list=
|
6
|
+
extension-pkg-allow-list=PyQt6, PySide6, pyqtgraph
|
7
7
|
|
8
8
|
# A comma-separated list of package or module names from where C extensions may
|
9
9
|
# be loaded. Extensions are loading into the active Python interpreter and may
|
CHANGELOG.md
CHANGED
@@ -1,5 +1,29 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## v0.80.0 (2024-07-06)
|
4
|
+
|
5
|
+
### Feature
|
6
|
+
|
7
|
+
* feat(qt5): dropped support for qt5; pyside2 and pyqt5 ([`fadbf77`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/fadbf77866903beff6580802bc203d53367fc7e7))
|
8
|
+
|
9
|
+
* feat(plugins): moved plugin dict to dataclass and container ([`03819a3`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/03819a3d902b4a51f3e882d52aedd971b2a8e127))
|
10
|
+
|
11
|
+
* feat(plugins): added support for pyqt6 ui files ([`d6d0777`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/d6d07771135335cb78dc648508ce573b8970261a))
|
12
|
+
|
13
|
+
* feat(plugins): added bec widgets base class ([`1aa83e0`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/1aa83e0ef1ffe45b01677b0b4590535cb0ca1cff))
|
14
|
+
|
15
|
+
## v0.79.3 (2024-07-05)
|
16
|
+
|
17
|
+
### Fix
|
18
|
+
|
19
|
+
* fix: changed inheritance to adress qt designer bug in rendering ([`e403870`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/e403870874bd5e45840a034d6f1b3dd576d9c846))
|
20
|
+
|
21
|
+
* fix: add designer plugin classes ([`1586ce2`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/1586ce2d6cba2bb086b2ef596e724bb9e40ab4f2))
|
22
|
+
|
23
|
+
### Refactor
|
24
|
+
|
25
|
+
* refactor: simplify logic in bec_status_box ([`576353c`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/576353cfe8c6fd64db561f0b6e2bc951300643d3))
|
26
|
+
|
3
27
|
## v0.79.2 (2024-07-04)
|
4
28
|
|
5
29
|
### Fix
|
@@ -112,40 +136,10 @@
|
|
112
136
|
|
113
137
|
## v0.74.1 (2024-06-26)
|
114
138
|
|
115
|
-
### Build
|
116
|
-
|
117
|
-
* build: added missing pytest-bec-e2e dependency; closes #219 ([`56fdae4`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/56fdae42757bdb9fa301c1e425a77e98b6eaf92b))
|
118
|
-
|
119
|
-
* build: fixed dependency ranges; closes #135 ([`e6a06c9`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/e6a06c9f43e0ad6bbfcfa550a2f580d2a27aff66))
|
120
|
-
|
121
|
-
### Chore
|
122
|
-
|
123
|
-
* chore: sorted dependencies alphabetically ([`21c807f`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/21c807f35831fdd1ef2e488ab90edae4719f0cb7))
|
124
|
-
|
125
|
-
### Documentation
|
126
|
-
|
127
|
-
* docs: fixed doc string ([`f979a63`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/f979a63d3d1a008f80e500510909750878ff4303))
|
128
|
-
|
129
139
|
### Fix
|
130
140
|
|
131
141
|
* fix(rings): rings properties updated right after setting ([`c8b7367`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/c8b7367815b095f8e4aa8b819481efb701f2e542))
|
132
142
|
|
133
|
-
* fix(motor_map): motor map can be removed from BECFigure with .remove() ([`6b25abf`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/6b25abff70280271e2eeb70450553c05d4b7c99c))
|
134
|
-
|
135
143
|
### Test
|
136
144
|
|
137
145
|
* test(bec_figure): tests for removing widgets with rpc e2e ([`a268caa`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/a268caaa30711fcc7ece542d24578d74cbf65c77))
|
138
|
-
|
139
|
-
## v0.74.0 (2024-06-25)
|
140
|
-
|
141
|
-
### Documentation
|
142
|
-
|
143
|
-
* docs(becfigure): docs added ([`a51b15d`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/a51b15da3f5e83e0c897a0342bdb05b9c677a179))
|
144
|
-
|
145
|
-
### Feature
|
146
|
-
|
147
|
-
* feat(waveform1d): dap LMFit model can be added to plot ([`1866ba6`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/1866ba66c8e3526661beb13fff3e13af6a0ae562))
|
148
|
-
|
149
|
-
### Test
|
150
|
-
|
151
|
-
* test(waveform1d): dap e2e test added ([`7271b42`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/7271b422f98ef9264970d708811c414b69a644db))
|
PKG-INFO
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: bec_widgets
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.80.0
|
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
|
@@ -28,9 +28,6 @@ Requires-Dist: pytest-random-order~=1.1; extra == 'dev'
|
|
28
28
|
Requires-Dist: pytest-timeout~=2.2; extra == 'dev'
|
29
29
|
Requires-Dist: pytest-xvfb~=3.0; extra == 'dev'
|
30
30
|
Requires-Dist: pytest~=8.0; extra == 'dev'
|
31
|
-
Provides-Extra: pyqt5
|
32
|
-
Requires-Dist: pyqt5>=5.9; extra == 'pyqt5'
|
33
|
-
Requires-Dist: pyqtwebengine>=5.9; extra == 'pyqt5'
|
34
31
|
Provides-Extra: pyqt6
|
35
32
|
Requires-Dist: pyqt6-webengine>=6.7; extra == 'pyqt6'
|
36
33
|
Requires-Dist: pyqt6>=6.7; extra == 'pyqt6'
|
README.md
CHANGED
@@ -17,7 +17,7 @@ cd bec_widgets
|
|
17
17
|
pip install -e .[dev,pyqt6]
|
18
18
|
```
|
19
19
|
|
20
|
-
BEC Widgets currently supports both
|
20
|
+
BEC Widgets currently supports both Pyside6 and PyQt6, however, no default distribution is specified. As a result, users must install one of the supported
|
21
21
|
Python Qt distributions manually.
|
22
22
|
|
23
23
|
To select a specific Python Qt distribution, install the package with an additional tag:
|
@@ -28,7 +28,7 @@ pip install bec_widgets[pyqt6]
|
|
28
28
|
or
|
29
29
|
|
30
30
|
```bash
|
31
|
-
pip install bec_widgets[
|
31
|
+
pip install bec_widgets[pyside6]
|
32
32
|
```
|
33
33
|
## Documentation
|
34
34
|
|
bec_widgets/cli/client.py
CHANGED
@@ -13,12 +13,12 @@ class Widgets(str, enum.Enum):
|
|
13
13
|
Enum for the available widgets.
|
14
14
|
"""
|
15
15
|
|
16
|
-
BECQueue = "BECQueue"
|
17
|
-
BECStatusBox = "BECStatusBox"
|
18
16
|
BECDock = "BECDock"
|
19
17
|
BECDockArea = "BECDockArea"
|
20
18
|
BECFigure = "BECFigure"
|
21
19
|
BECMotorMapWidget = "BECMotorMapWidget"
|
20
|
+
BECQueue = "BECQueue"
|
21
|
+
BECStatusBox = "BECStatusBox"
|
22
22
|
RingProgressBar = "RingProgressBar"
|
23
23
|
ScanControl = "ScanControl"
|
24
24
|
TextBox = "TextBox"
|
bec_widgets/cli/generate_cli.py
CHANGED
@@ -5,13 +5,12 @@ import argparse
|
|
5
5
|
import inspect
|
6
6
|
import os
|
7
7
|
import sys
|
8
|
-
from typing import Literal
|
9
8
|
|
10
9
|
import black
|
11
10
|
import isort
|
12
11
|
|
13
12
|
from bec_widgets.utils.generate_designer_plugin import DesignerPluginGenerator
|
14
|
-
from bec_widgets.utils.plugin_utils import get_rpc_classes
|
13
|
+
from bec_widgets.utils.plugin_utils import BECClassContainer, get_rpc_classes
|
15
14
|
|
16
15
|
if sys.version_info >= (3, 11):
|
17
16
|
from typing import get_overloads
|
@@ -40,17 +39,20 @@ from bec_widgets.cli.client_utils import RPCBase, rpc_call, BECGuiClientMixin
|
|
40
39
|
|
41
40
|
self.content = ""
|
42
41
|
|
43
|
-
def generate_client(
|
44
|
-
self, published_classes: dict[Literal["connector_classes", "top_level_classes"], list[type]]
|
45
|
-
):
|
42
|
+
def generate_client(self, class_container: BECClassContainer):
|
46
43
|
"""
|
47
44
|
Generate the client for the published classes.
|
48
45
|
|
49
46
|
Args:
|
50
|
-
|
47
|
+
class_container: The class container with the classes to generate the client for.
|
51
48
|
"""
|
52
|
-
|
53
|
-
|
49
|
+
rpc_top_level_classes = class_container.rpc_top_level_classes
|
50
|
+
rpc_top_level_classes.sort(key=lambda x: x.__name__)
|
51
|
+
connector_classes = class_container.connector_classes
|
52
|
+
connector_classes.sort(key=lambda x: x.__name__)
|
53
|
+
|
54
|
+
self.write_client_enum(rpc_top_level_classes)
|
55
|
+
for cls in connector_classes:
|
54
56
|
self.content += "\n\n"
|
55
57
|
self.generate_content_for_class(cls)
|
56
58
|
|
@@ -156,13 +158,12 @@ def main():
|
|
156
158
|
client_path = os.path.join(current_path, "client.py")
|
157
159
|
|
158
160
|
rpc_classes = get_rpc_classes("bec_widgets")
|
159
|
-
rpc_classes["connector_classes"].sort(key=lambda x: x.__name__)
|
160
161
|
|
161
162
|
generator = ClientGenerator()
|
162
163
|
generator.generate_client(rpc_classes)
|
163
164
|
generator.write(client_path)
|
164
165
|
|
165
|
-
for cls in rpc_classes
|
166
|
+
for cls in rpc_classes.plugins:
|
166
167
|
plugin = DesignerPluginGenerator(cls)
|
167
168
|
if not hasattr(plugin, "info"):
|
168
169
|
continue
|
@@ -29,7 +29,7 @@ class RPCWidgetHandler:
|
|
29
29
|
from bec_widgets.utils.plugin_utils import get_rpc_classes
|
30
30
|
|
31
31
|
clss = get_rpc_classes("bec_widgets")
|
32
|
-
self._widget_classes = {cls.__name__: cls for cls in clss
|
32
|
+
self._widget_classes = {cls.__name__: cls for cls in clss.top_level_classes}
|
33
33
|
|
34
34
|
def create_widget(self, widget_type, **kwargs) -> BECConnector:
|
35
35
|
"""
|
@@ -13,6 +13,7 @@ from qtpy.QtCore import QObject, QRunnable, QThreadPool, Signal
|
|
13
13
|
from qtpy.QtCore import Slot as pyqtSlot
|
14
14
|
|
15
15
|
from bec_widgets.cli.rpc_register import RPCRegister
|
16
|
+
from bec_widgets.utils.bec_widget import BECWidget
|
16
17
|
from bec_widgets.utils.yaml_dialog import load_yaml, load_yaml_gui, save_yaml, save_yaml_gui
|
17
18
|
|
18
19
|
BECDispatcher = lazy_import_from("bec_widgets.utils.bec_dispatcher", ("BECDispatcher",))
|
@@ -63,7 +64,7 @@ class Worker(QRunnable):
|
|
63
64
|
self.signals.completed.emit()
|
64
65
|
|
65
66
|
|
66
|
-
class BECConnector:
|
67
|
+
class BECConnector(BECWidget):
|
67
68
|
"""Connection mixin class for all BEC widgets, to handle BEC client and device manager"""
|
68
69
|
|
69
70
|
USER_ACCESS = ["_config_dict", "_get_all_rpc"]
|
@@ -8,7 +8,7 @@ import redis
|
|
8
8
|
from bec_lib.client import BECClient
|
9
9
|
from bec_lib.redis_connector import MessageObject, RedisConnector
|
10
10
|
from bec_lib.service_config import ServiceConfig
|
11
|
-
from qtpy.QtCore import
|
11
|
+
from qtpy.QtCore import PYQT6, PYSIDE6, QCoreApplication, QObject
|
12
12
|
from qtpy.QtCore import Signal as pyqtSignal
|
13
13
|
|
14
14
|
if TYPE_CHECKING:
|
@@ -127,9 +127,9 @@ class BECDispatcher:
|
|
127
127
|
return
|
128
128
|
|
129
129
|
# shutdown QCoreApp if it exists
|
130
|
-
if
|
130
|
+
if PYQT6:
|
131
131
|
cls.qapp.exit()
|
132
|
-
elif
|
132
|
+
elif PYSIDE6:
|
133
133
|
cls.qapp.shutdown()
|
134
134
|
cls.qapp = None
|
135
135
|
|
@@ -1,12 +1,13 @@
|
|
1
1
|
import importlib
|
2
2
|
import inspect
|
3
3
|
import os
|
4
|
-
from
|
4
|
+
from dataclasses import dataclass
|
5
5
|
|
6
6
|
from bec_lib.plugin_helper import _get_available_plugins
|
7
7
|
from qtpy.QtWidgets import QGraphicsWidget, QWidget
|
8
8
|
|
9
9
|
from bec_widgets.utils import BECConnector
|
10
|
+
from bec_widgets.utils.bec_widget import BECWidget
|
10
11
|
|
11
12
|
|
12
13
|
def get_plugin_widgets() -> dict[str, BECConnector]:
|
@@ -44,9 +45,74 @@ def _filter_plugins(obj):
|
|
44
45
|
return inspect.isclass(obj) and issubclass(obj, BECConnector)
|
45
46
|
|
46
47
|
|
47
|
-
|
48
|
-
|
49
|
-
|
48
|
+
@dataclass
|
49
|
+
class BECClassInfo:
|
50
|
+
name: str
|
51
|
+
module: str
|
52
|
+
file: str
|
53
|
+
obj: type
|
54
|
+
is_connector: bool = False
|
55
|
+
is_widget: bool = False
|
56
|
+
is_top_level: bool = False
|
57
|
+
|
58
|
+
|
59
|
+
class BECClassContainer:
|
60
|
+
def __init__(self):
|
61
|
+
self._collection = []
|
62
|
+
|
63
|
+
def add_class(self, class_info: BECClassInfo):
|
64
|
+
"""
|
65
|
+
Add a class to the collection.
|
66
|
+
|
67
|
+
Args:
|
68
|
+
class_info(BECClassInfo): The class information
|
69
|
+
"""
|
70
|
+
self.collection.append(class_info)
|
71
|
+
|
72
|
+
@property
|
73
|
+
def collection(self):
|
74
|
+
"""
|
75
|
+
Get the collection of classes.
|
76
|
+
"""
|
77
|
+
return self._collection
|
78
|
+
|
79
|
+
@property
|
80
|
+
def connector_classes(self):
|
81
|
+
"""
|
82
|
+
Get all connector classes.
|
83
|
+
"""
|
84
|
+
return [info.obj for info in self.collection if info.is_connector]
|
85
|
+
|
86
|
+
@property
|
87
|
+
def top_level_classes(self):
|
88
|
+
"""
|
89
|
+
Get all top-level classes.
|
90
|
+
"""
|
91
|
+
return [info.obj for info in self.collection if info.is_top_level]
|
92
|
+
|
93
|
+
@property
|
94
|
+
def plugins(self):
|
95
|
+
"""
|
96
|
+
Get all plugins. These are all classes that are on the top level and are widgets.
|
97
|
+
"""
|
98
|
+
return [info.obj for info in self.collection if info.is_widget and info.is_top_level]
|
99
|
+
|
100
|
+
@property
|
101
|
+
def widgets(self):
|
102
|
+
"""
|
103
|
+
Get all widgets. These are all classes inheriting from BECWidget.
|
104
|
+
"""
|
105
|
+
return [info.obj for info in self.collection if info.is_widget]
|
106
|
+
|
107
|
+
@property
|
108
|
+
def rpc_top_level_classes(self):
|
109
|
+
"""
|
110
|
+
Get all top-level classes that are RPC-enabled. These are all classes that users can choose from.
|
111
|
+
"""
|
112
|
+
return [info.obj for info in self.collection if info.is_top_level and info.is_connector]
|
113
|
+
|
114
|
+
|
115
|
+
def get_rpc_classes(repo_name: str) -> BECClassContainer:
|
50
116
|
"""
|
51
117
|
Get all RPC-enabled classes in the specified repository.
|
52
118
|
|
@@ -56,8 +122,7 @@ def get_rpc_classes(
|
|
56
122
|
Returns:
|
57
123
|
dict: A dictionary with keys "connector_classes" and "top_level_classes" and values as lists of classes.
|
58
124
|
"""
|
59
|
-
|
60
|
-
top_level_classes = []
|
125
|
+
collection = BECClassContainer()
|
61
126
|
anchor_module = importlib.import_module(f"{repo_name}.widgets")
|
62
127
|
directory = os.path.dirname(anchor_module.__file__)
|
63
128
|
for root, _, files in sorted(os.walk(directory)):
|
@@ -78,11 +143,16 @@ def get_rpc_classes(
|
|
78
143
|
obj = getattr(module, name)
|
79
144
|
if not hasattr(obj, "__module__") or obj.__module__ != module.__name__:
|
80
145
|
continue
|
81
|
-
if isinstance(obj, type)
|
82
|
-
|
146
|
+
if isinstance(obj, type):
|
147
|
+
class_info = BECClassInfo(name=name, module=module_name, file=path, obj=obj)
|
148
|
+
if issubclass(obj, BECConnector):
|
149
|
+
class_info.is_connector = True
|
150
|
+
if issubclass(obj, BECWidget):
|
151
|
+
class_info.is_widget = True
|
83
152
|
if len(subs) == 1 and (
|
84
153
|
issubclass(obj, QWidget) or issubclass(obj, QGraphicsWidget)
|
85
154
|
):
|
86
|
-
|
155
|
+
class_info.is_top_level = True
|
156
|
+
collection.add_class(class_info)
|
87
157
|
|
88
|
-
return
|
158
|
+
return collection
|
bec_widgets/utils/ui_loader.py
CHANGED
@@ -1,20 +1,18 @@
|
|
1
|
+
import os
|
2
|
+
|
1
3
|
from qtpy import PYQT6, PYSIDE6, QT_VERSION
|
2
4
|
from qtpy.QtCore import QFile, QIODevice
|
3
5
|
|
6
|
+
from bec_widgets.utils.generate_designer_plugin import DesignerPluginInfo
|
7
|
+
from bec_widgets.utils.plugin_utils import get_rpc_classes
|
8
|
+
|
4
9
|
if PYSIDE6:
|
5
10
|
from PySide6.QtUiTools import QUiLoader
|
6
11
|
|
7
|
-
from bec_widgets.utils.plugin_utils import get_rpc_classes
|
8
|
-
from bec_widgets.widgets.buttons.color_button.color_button import ColorButton
|
9
|
-
|
10
12
|
class CustomUiLoader(QUiLoader):
|
11
|
-
def __init__(self, baseinstance):
|
13
|
+
def __init__(self, baseinstance, custom_widgets: dict = None):
|
12
14
|
super().__init__(baseinstance)
|
13
|
-
|
14
|
-
|
15
|
-
widgets.append(ColorButton)
|
16
|
-
|
17
|
-
self.custom_widgets = {widget.__name__: widget for widget in widgets}
|
15
|
+
self.custom_widgets = custom_widgets or {}
|
18
16
|
|
19
17
|
self.baseinstance = baseinstance
|
20
18
|
|
@@ -27,25 +25,21 @@ if PYSIDE6:
|
|
27
25
|
|
28
26
|
|
29
27
|
class UILoader:
|
30
|
-
"""Universal UI loader for
|
28
|
+
"""Universal UI loader for PyQt6 and PySide6."""
|
31
29
|
|
32
30
|
def __init__(self, parent=None):
|
33
31
|
self.parent = parent
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
self.loader = loadUi
|
47
|
-
else:
|
48
|
-
raise ImportError("No compatible Qt bindings found.")
|
32
|
+
|
33
|
+
widgets = get_rpc_classes("bec_widgets").top_level_classes
|
34
|
+
|
35
|
+
self.custom_widgets = {widget.__name__: widget for widget in widgets}
|
36
|
+
|
37
|
+
if PYSIDE6:
|
38
|
+
self.loader = self.load_ui_pyside6
|
39
|
+
elif PYQT6:
|
40
|
+
self.loader = self.load_ui_pyqt6
|
41
|
+
else:
|
42
|
+
raise ImportError("No compatible Qt bindings found.")
|
49
43
|
|
50
44
|
def load_ui_pyside6(self, ui_file, parent=None):
|
51
45
|
"""
|
@@ -58,7 +52,7 @@ class UILoader:
|
|
58
52
|
QWidget: The loaded widget.
|
59
53
|
"""
|
60
54
|
|
61
|
-
loader = CustomUiLoader(parent)
|
55
|
+
loader = CustomUiLoader(parent, self.custom_widgets)
|
62
56
|
file = QFile(ui_file)
|
63
57
|
if not file.open(QIODevice.ReadOnly):
|
64
58
|
raise IOError(f"Cannot open file: {ui_file}")
|
@@ -66,6 +60,71 @@ class UILoader:
|
|
66
60
|
file.close()
|
67
61
|
return widget
|
68
62
|
|
63
|
+
def load_ui_pyqt6(self, ui_file, parent=None):
|
64
|
+
"""
|
65
|
+
Specific loader for PyQt6 using loadUi.
|
66
|
+
Args:
|
67
|
+
ui_file(str): Path to the .ui file.
|
68
|
+
parent(QWidget): Parent widget.
|
69
|
+
|
70
|
+
Returns:
|
71
|
+
QWidget: The loaded widget.
|
72
|
+
"""
|
73
|
+
from PyQt6.uic.Loader.loader import DynamicUILoader
|
74
|
+
|
75
|
+
class CustomDynamicUILoader(DynamicUILoader):
|
76
|
+
def __init__(self, package, custom_widgets: dict = None):
|
77
|
+
super().__init__(package)
|
78
|
+
self.custom_widgets = custom_widgets or {}
|
79
|
+
|
80
|
+
def _handle_custom_widgets(self, el):
|
81
|
+
"""Handle the <customwidgets> element."""
|
82
|
+
|
83
|
+
def header2module(header):
|
84
|
+
"""header2module(header) -> string
|
85
|
+
|
86
|
+
Convert paths to C++ header files to according Python modules
|
87
|
+
>>> header2module("foo/bar/baz.h")
|
88
|
+
'foo.bar.baz'
|
89
|
+
"""
|
90
|
+
|
91
|
+
if header.endswith(".h"):
|
92
|
+
header = header[:-2]
|
93
|
+
|
94
|
+
mpath = []
|
95
|
+
for part in header.split("/"):
|
96
|
+
# Ignore any empty parts or those that refer to the current
|
97
|
+
# directory.
|
98
|
+
if part not in ("", "."):
|
99
|
+
if part == "..":
|
100
|
+
# We should allow this for Python3.
|
101
|
+
raise SyntaxError(
|
102
|
+
"custom widget header file name may not contain '..'."
|
103
|
+
)
|
104
|
+
|
105
|
+
mpath.append(part)
|
106
|
+
|
107
|
+
return ".".join(mpath)
|
108
|
+
|
109
|
+
for custom_widget in el:
|
110
|
+
classname = custom_widget.findtext("class")
|
111
|
+
header = custom_widget.findtext("header")
|
112
|
+
if header:
|
113
|
+
header = self._translate_bec_widgets_header(header)
|
114
|
+
self.factory.addCustomWidget(
|
115
|
+
classname,
|
116
|
+
custom_widget.findtext("extends") or "QWidget",
|
117
|
+
header2module(header),
|
118
|
+
)
|
119
|
+
|
120
|
+
def _translate_bec_widgets_header(self, header):
|
121
|
+
for name, value in self.custom_widgets.items():
|
122
|
+
if header == DesignerPluginInfo.pascal_to_snake(name):
|
123
|
+
return value.__module__
|
124
|
+
return header
|
125
|
+
|
126
|
+
return CustomDynamicUILoader("", self.custom_widgets).loadUi(ui_file, parent)
|
127
|
+
|
69
128
|
def load_ui(self, ui_file, parent=None):
|
70
129
|
"""
|
71
130
|
Universal UI loader method.
|