bec-widgets 0.79.3__py3-none-any.whl → 0.80.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 CHANGED
@@ -154,7 +154,6 @@ test-matrix:
154
154
  - "3.12"
155
155
  QT_PCKG:
156
156
  - "pyside6"
157
- - "pyqt5"
158
157
  - "pyqt6"
159
158
 
160
159
  stage: AdditionalTests
.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=PyQt5, pyqtgraph
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,23 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## v0.80.1 (2024-07-06)
4
+
5
+ ### Fix
6
+
7
+ * fix(entry_validator): check for entry == "" ([`61de7e9`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/61de7e9e221c766b9fb3ec23246da6a11c96a986))
8
+
9
+ ## v0.80.0 (2024-07-06)
10
+
11
+ ### Feature
12
+
13
+ * feat(qt5): dropped support for qt5; pyside2 and pyqt5 ([`fadbf77`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/fadbf77866903beff6580802bc203d53367fc7e7))
14
+
15
+ * feat(plugins): moved plugin dict to dataclass and container ([`03819a3`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/03819a3d902b4a51f3e882d52aedd971b2a8e127))
16
+
17
+ * feat(plugins): added support for pyqt6 ui files ([`d6d0777`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/d6d07771135335cb78dc648508ce573b8970261a))
18
+
19
+ * feat(plugins): added bec widgets base class ([`1aa83e0`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/1aa83e0ef1ffe45b01677b0b4590535cb0ca1cff))
20
+
3
21
  ## v0.79.3 (2024-07-05)
4
22
 
5
23
  ### Fix
@@ -123,27 +141,3 @@
123
141
  * refactor(dispatcher): cleanup ([`ca02132`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/ca02132c8d18535b37e9192e00459d2aca6ba5cf))
124
142
 
125
143
  ## v0.74.1 (2024-06-26)
126
-
127
- ### Build
128
-
129
- * build: added missing pytest-bec-e2e dependency; closes #219 ([`56fdae4`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/56fdae42757bdb9fa301c1e425a77e98b6eaf92b))
130
-
131
- * build: fixed dependency ranges; closes #135 ([`e6a06c9`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/e6a06c9f43e0ad6bbfcfa550a2f580d2a27aff66))
132
-
133
- ### Chore
134
-
135
- * chore: sorted dependencies alphabetically ([`21c807f`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/21c807f35831fdd1ef2e488ab90edae4719f0cb7))
136
-
137
- ### Documentation
138
-
139
- * docs: fixed doc string ([`f979a63`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/f979a63d3d1a008f80e500510909750878ff4303))
140
-
141
- ### Fix
142
-
143
- * fix(rings): rings properties updated right after setting ([`c8b7367`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/c8b7367815b095f8e4aa8b819481efb701f2e542))
144
-
145
- * fix(motor_map): motor map can be removed from BECFigure with .remove() ([`6b25abf`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/6b25abff70280271e2eeb70450553c05d4b7c99c))
146
-
147
- ### Test
148
-
149
- * test(bec_figure): tests for removing widgets with rpc e2e ([`a268caa`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/a268caaa30711fcc7ece542d24578d74cbf65c77))
PKG-INFO CHANGED
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bec_widgets
3
- Version: 0.79.3
3
+ Version: 0.80.1
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 PyQt5 and PyQt6, however, no default distribution is specified. As a result, users must install one of the supported
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[pyqt5]
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"
@@ -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
- published_classes(dict): A dictionary with keys "connector_classes" and "top_level_classes" and values as lists of classes.
47
+ class_container: The class container with the classes to generate the client for.
51
48
  """
52
- self.write_client_enum(published_classes["top_level_classes"])
53
- for cls in published_classes["connector_classes"]:
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["top_level_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["top_level_classes"]}
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 PYQT5, PYQT6, PYSIDE2, PYSIDE6, QCoreApplication, QObject
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 PYQT5 or PYQT6:
130
+ if PYQT6:
131
131
  cls.qapp.exit()
132
- elif PYSIDE2 or PYSIDE6:
132
+ elif PYSIDE6:
133
133
  cls.qapp.shutdown()
134
134
  cls.qapp = None
135
135
 
@@ -0,0 +1,2 @@
1
+ class BECWidget:
2
+ """Base class for all BEC widgets."""
@@ -19,7 +19,7 @@ class EntryValidator:
19
19
  device = self.devices[name]
20
20
  description = device.describe()
21
21
 
22
- if entry is None:
22
+ if entry is None or entry == "":
23
23
  entry = next(iter(device._hints), name) if hasattr(device, "_hints") else name
24
24
  if entry not in description:
25
25
  raise ValueError(f"Entry '{entry}' not found in device '{name}' signals")
@@ -1,12 +1,13 @@
1
1
  import importlib
2
2
  import inspect
3
3
  import os
4
- from typing import Literal
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
- def get_rpc_classes(
48
- repo_name: str,
49
- ) -> dict[Literal["connector_classes", "top_level_classes"], list[type]]:
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
- connector_classes = []
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) and issubclass(obj, BECConnector):
82
- connector_classes.append(obj)
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
- top_level_classes.append(obj)
155
+ class_info.is_top_level = True
156
+ collection.add_class(class_info)
87
157
 
88
- return {"connector_classes": connector_classes, "top_level_classes": top_level_classes}
158
+ return collection
@@ -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
- widgets = get_rpc_classes("bec_widgets").get("top_level_classes", [])
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 PyQt5, PyQt6, PySide2, and PySide6."""
28
+ """Universal UI loader for PyQt6 and PySide6."""
31
29
 
32
30
  def __init__(self, parent=None):
33
31
  self.parent = parent
34
- if QT_VERSION.startswith("5"):
35
- # PyQt5 or PySide2
36
- from qtpy import uic
37
-
38
- self.loader = uic.loadUi
39
- elif QT_VERSION.startswith("6"):
40
- # PyQt6 or PySide6
41
- if PYSIDE6:
42
- self.loader = self.load_ui_pyside6
43
- elif PYQT6:
44
- from PyQt6.uic import loadUi
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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bec_widgets
3
- Version: 0.79.3
3
+ Version: 0.80.1
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'
@@ -1,12 +1,12 @@
1
1
  .gitignore,sha256=cMQ1MLmnoR88aMCCJwUyfoTnufzl4-ckmHtlFUqHcT4,3253
2
- .gitlab-ci.yml,sha256=RnYDz4zKXjlqltTryprlB1s5vLXxI2-seW-Vb70NNF0,8162
3
- .pylintrc,sha256=OstrgmEyP0smNFBKoIN5_26-UmNZgMHnbjvAWX0UrLs,18535
2
+ .gitlab-ci.yml,sha256=vuDJPAYOK0995_H6fu0N5dIgIrOJgTvKr0HZkNWlxMw,8142
3
+ .pylintrc,sha256=eeY8YwSI74oFfq6IYIbCqnx3Vk8ZncKaatv96n_Y8Rs,18544
4
4
  .readthedocs.yaml,sha256=aSOc277LqXcsTI6lgvm_JY80lMlr69GbPKgivua2cS0,603
5
- CHANGELOG.md,sha256=GC_r9Byw1xBscFwof_pEjhjMkmOLnQYybtibbMQDv84,7091
5
+ CHANGELOG.md,sha256=zyzr2iWHZZpH8Zcb3Q2EdAEd9KzpMkzIVhoUpNbiTQw,6794
6
6
  LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
7
- PKG-INFO,sha256=KNOSryTtLS3GCT2Zw_6V5l9g4xxCSX_MAvinpePvrBU,1427
8
- README.md,sha256=y4jB6wvArS7N8_iTbKWnSM_oRAqLA2GqgzUR-FMh5sU,2645
9
- pyproject.toml,sha256=UakMzJgq7Qf9K_YagOPPzsfhPEg0FO_KedOKoZEDT9c,2403
7
+ PKG-INFO,sha256=W5BiUCnKEu7UtciQ89fZp4AqzfUxwuHdNW84R2Yg8IQ,1309
8
+ README.md,sha256=Od69x-RS85Hph0-WwWACwal4yUd67XkEn4APEfHhHFw,2649
9
+ pyproject.toml,sha256=1-rpTxKUDLOPeIAe91QcKJEY1P0YgefF2Gz60Z8VJzI,2358
10
10
  .git_hooks/pre-commit,sha256=n3RofIZHJl8zfJJIUomcMyYGFi_rwq4CC19z0snz3FI,286
11
11
  .gitlab/issue_templates/bug_report_template.md,sha256=gAuyEwl7XlnebBrkiJ9AqffSNOywmr8vygUFWKTuQeI,386
12
12
  .gitlab/issue_templates/documentation_update_template.md,sha256=FHLdb3TS_D9aL4CYZCjyXSulbaW5mrN2CmwTaeLPbNw,860
@@ -17,11 +17,11 @@ bec_widgets/assets/bec_widgets_icon.png,sha256=K8dgGwIjalDh9PRHUsSQBqgdX7a00nM3i
17
17
  bec_widgets/assets/terminal_icon.png,sha256=bJl7Tft4Fi2uxvuXI8o14uMHnI9eAWKSU2uftXCH9ws,3889
18
18
  bec_widgets/cli/__init__.py,sha256=d0Q6Fn44e7wFfLabDOBxpcJ1DPKWlFunGYDUBmO-4hA,22
19
19
  bec_widgets/cli/auto_updates.py,sha256=DyBV3HnjMSH-cvVkYNcDiYKVf0Xut4Qy2qGQqkW47Bw,4833
20
- bec_widgets/cli/client.py,sha256=ahOa50EN5qknxv_cnWdoBCi-FPlj6UHOGGOnl48enSk,60054
20
+ bec_widgets/cli/client.py,sha256=J011qUzqWpdwclRzB2yA8zqytALAwCwgZLgruUKeyv0,60054
21
21
  bec_widgets/cli/client_utils.py,sha256=zq1gPW7t4n9Nsn4MLkdUeKwwl-9nUcf5UjuN8lZr9iY,12281
22
- bec_widgets/cli/generate_cli.py,sha256=FUMSm84ztE6UIIHs8U0Irof1i5LRu6CXW1sl-RF_UKA,5877
22
+ bec_widgets/cli/generate_cli.py,sha256=Ea5px9KblUlcGg-1JbJBTIU7laGg2n8PM7Efw9WVVzM,5889
23
23
  bec_widgets/cli/rpc_register.py,sha256=QxXUZu5XNg00Yf5O3UHWOXg3-f_pzKjjoZYMOa-MOJc,2216
24
- bec_widgets/cli/rpc_wigdet_handler.py,sha256=1qQOGrM8rozaWLkoxAW8DTVLv_L_DZdZgUMDPy5MOek,1486
24
+ bec_widgets/cli/rpc_wigdet_handler.py,sha256=6kQng2DyS6rhLJqSJ7xa0kdgSxp-35A2upcf833dJRE,1483
25
25
  bec_widgets/cli/server.py,sha256=2EJvkQDzrDTsZjRPs7g2v_iPTspGqxzY34tRAnvjxjY,7281
26
26
  bec_widgets/examples/__init__.py,sha256=WWQ0cu7m8sA4Ehy-DWdTIqSISjaHsbxhsNmNrMnhDZU,202
27
27
  bec_widgets/examples/jupyter_console/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -38,20 +38,21 @@ bec_widgets/examples/plugin_example_pyside/tictactoe.py,sha256=s3rCurXloVcmMdzZi
38
38
  bec_widgets/examples/plugin_example_pyside/tictactoeplugin.py,sha256=BBt3MD8oDLUMCCY3mioJa1QRR0WQdW6DuvVmK1Taovk,1734
39
39
  bec_widgets/examples/plugin_example_pyside/tictactoetaskmenu.py,sha256=LNwplI6deUdKY6FOhUuWBanotxk9asF2G-6k7lFfA8Y,2301
40
40
  bec_widgets/utils/__init__.py,sha256=1930ji1Jj6dVuY81Wd2kYBhHYNV-2R0bN_L4o9zBj1U,533
41
- bec_widgets/utils/bec_connector.py,sha256=ZWaN9C2CKwqj4NIL1irxNDRyMJ1cBSkCAzb6xmxTIKw,9472
41
+ bec_widgets/utils/bec_connector.py,sha256=JScGWHEt4kh6C-C0O_JV_bOuiQlBjYTAPJktqgeAj70,9534
42
42
  bec_widgets/utils/bec_designer.py,sha256=ak3G8FdojUPjVBBwdPXw7tN5P2Uxr-SSoQt394jXeAA,4308
43
- bec_widgets/utils/bec_dispatcher.py,sha256=QZjRKNrZ181yt_6nLJCfdNk5EyeaGImApNA1FWR4rqo,6186
43
+ bec_widgets/utils/bec_dispatcher.py,sha256=dghOw63Ql6rQ-zH1ZaYU6kT9Dg5kNDcZwEdu2YPBJeU,6150
44
44
  bec_widgets/utils/bec_table.py,sha256=nA2b8ukSeUfquFMAxGrUVOqdrzMoDYD6O_4EYbOG2zk,717
45
+ bec_widgets/utils/bec_widget.py,sha256=Y-t90XMe2PASvY96Xve5a_jevXqnxk1SJ-mfy6ULC9Y,59
45
46
  bec_widgets/utils/colors.py,sha256=CP_lwj757CpwdVhWSfdNEXKFCEVVVF48DizD2WJKSwI,9759
46
47
  bec_widgets/utils/container_utils.py,sha256=m3VUyAYmSWkEwApP9tBvKxPYVtc2kHw4toxIpMryJy4,1495
47
48
  bec_widgets/utils/crosshair.py,sha256=SubY4FQCI6vUKsmMYGKHR7uYdGQJ6vhoYLuC1XlKS9I,9626
48
- bec_widgets/utils/entry_validator.py,sha256=IqmtResXQtnmMvWVSl8IrnggqSzXLp4cSggn6WdSTpE,1298
49
+ bec_widgets/utils/entry_validator.py,sha256=3skJIsUwTYicT76AMHm_M78RiWtUgyD2zb-Rxo2HdHQ,1313
49
50
  bec_widgets/utils/generate_designer_plugin.py,sha256=T6DqL29sN4LcXLH8Hoa410UGnoRzjsZ-z0PRGTEzkRE,5809
50
51
  bec_widgets/utils/layout_manager.py,sha256=H0nKsIMaPxRkof1MEXlSmW6w1dFxA6astaGzf4stI84,4727
51
- bec_widgets/utils/plugin_utils.py,sha256=tmZkUNvVlldPjHDfL_TbaV2jjAECgPjGsvLMmmyZcfc,3342
52
+ bec_widgets/utils/plugin_utils.py,sha256=njvVdvF-AR47Yn80ntpvFldEvLuFx9GV-qEX4p_n4AI,5263
52
53
  bec_widgets/utils/rpc_decorator.py,sha256=pIvtqySQLnuS7l2Ti_UAe4WX7CRivZnsE5ZdKAihxh0,479
53
54
  bec_widgets/utils/thread_checker.py,sha256=rDNuA3X6KQyA7JPb67mccTg0z8YkInynLAENQDQpbuE,1607
54
- bec_widgets/utils/ui_loader.py,sha256=2N50GPH2nh7lT7NQVoHzcjgZzk2zFybFqrBmG91dq8M,2552
55
+ bec_widgets/utils/ui_loader.py,sha256=9C4DkMIM8PEGXs2LmbZMyLVt38EIO9-T9C2UnK2SAvE,4882
55
56
  bec_widgets/utils/validator_delegate.py,sha256=Emj1WF6W8Ke1ruBWUfmHdVJpmOSPezuOt4zvQTay_44,442
56
57
  bec_widgets/utils/widget_io.py,sha256=4wlmXwKs7ncpCEQYXBzLVo7Z0IjU-TaJ9RtuaM8-Q98,12038
57
58
  bec_widgets/utils/yaml_dialog.py,sha256=T6UyGNGdmpXW74fa_7Nk6b99T5pp2Wvyw3AOauRc8T8,2407
@@ -174,7 +175,7 @@ docs/developer/developer.md,sha256=VUdMnQBsSRCawYMFCe0vya5oj1MimLML7Pd58kY6fYY,7
174
175
  docs/developer/getting_started/development.md,sha256=aYLmuLMYpp5FcIXeDUqCfcStIV8veuiMBjOt5bTW_30,1406
175
176
  docs/developer/getting_started/getting_started.md,sha256=My_K_6O7LLaXVB_eINrRku5o-jVx95lsmGgHxgZhT7A,378
176
177
  docs/developer/widgets/widgets.md,sha256=aNsJgG7R-3EerumNB6GH84JLIXfZqGN5GjvpKWDi0Hk,504
177
- docs/introduction/introduction.md,sha256=wp7jmhkUtJnSnEnmIAZGUcau_3-5e5-FohvZb63khw4,1432
178
+ docs/introduction/introduction.md,sha256=YBEFDhxqHTcbfbNTo76xDflaFUHIqDs-sToA1HRmCnI,1436
178
179
  docs/user/customisation.md,sha256=wCW8fAbqtlgGE3mURvXOrK67Xo0_B-lxfg0sYuQWB40,3186
179
180
  docs/user/user.md,sha256=uCTcjclIi6rdjYRQebko6bWFEVsjyfshsVU3BDYrC-Y,1403
180
181
  docs/user/api_reference/api_reference.md,sha256=q2Imc48Rq6GcAP0R4bS3KuW5ptZZdsV4wxGJb3JJQHg,174
@@ -183,7 +184,7 @@ docs/user/getting_started/BECDockArea.png,sha256=t3vSm_rVRk371J5LOutbolETuEjStNc
183
184
  docs/user/getting_started/auto_updates.md,sha256=Gicx3lplI6JRBlnPj_VL6IhqOIcsWjYF4_EdZSCje2A,3754
184
185
  docs/user/getting_started/getting_started.md,sha256=lxZXCr6HAkM61oo5Bu-YjINSKo4wihWhAPJdotEAAVQ,358
185
186
  docs/user/getting_started/gui_complex_gui.gif,sha256=ovv9u371BGG5GqhzyBMl4mvqMHLfJS0ylr-dR0Ydwtw,6550393
186
- docs/user/getting_started/installation.md,sha256=5_fPbmUqLGtwOskFHTlytd4PJKrMcHqHShzM9ymM0oI,1149
187
+ docs/user/getting_started/installation.md,sha256=VqtHasD0BAPt-K6YJQicWpyqb91RJYkZ8qpvwSLsgaY,1153
187
188
  docs/user/getting_started/quick_start.md,sha256=ABDRRB8DM8dFYdsWUfzQV0eaffRFAlcn2HIfw7yiUGs,9401
188
189
  docs/user/widgets/BECFigure.png,sha256=8dQr4u0uk_y0VV-R1Jh9yTR3Vidd9HDEno_07R0swaE,1605920
189
190
  docs/user/widgets/bec_figure.md,sha256=tNT-5QP3DekY_iC9jOMULzdxmUU8wMkkX5Ru9yq0Txo,6671
@@ -225,11 +226,11 @@ tests/unit_tests/test_color_validation.py,sha256=xbFbtFDia36XLgaNrX2IwvAX3IDC_Od
225
226
  tests/unit_tests/test_crosshair.py,sha256=3OMAJ2ZaISYXMOtkXf1rPdy94vCr8njeLi6uHblBL9Q,5045
226
227
  tests/unit_tests/test_device_input_base.py,sha256=Bf2lk_utvLsDqDX5NWgyArSAo-dWMQ72yiCPGoWVwxM,2431
227
228
  tests/unit_tests/test_device_input_widgets.py,sha256=PmUL8aJXg_BYwWDE3Gh1kGm8q6fQZD5sWlVXz3KLwI0,5837
228
- tests/unit_tests/test_generate_cli_client.py,sha256=adcMoXjWpFLVjpauCu0r31CMMibUY1LF1MMf8rO-6rw,2815
229
+ tests/unit_tests/test_generate_cli_client.py,sha256=ng-eV5iF7Dhm-6YpxYo99CMY0KgqoaZBQNkMeKULDBU,3355
229
230
  tests/unit_tests/test_generate_plugin.py,sha256=9603ucZChM-pYpHadzsR94U1Zec1KZT34WedX9qzgMo,4464
230
231
  tests/unit_tests/test_motor_control.py,sha256=bdCjhthWEy9iNyNZiD1JAeN_PSMaOblS780XGDjVwsw,20851
231
232
  tests/unit_tests/test_plot_base.py,sha256=vWbt-QO5blSoE5C1392MIFha9A9JnUsx_D_9yTqhcRo,3845
232
- tests/unit_tests/test_plugin_utils.py,sha256=PonKNpu4fZaFmKbI2v0tZJjZrsTvBGSF96bPHvKJvrE,608
233
+ tests/unit_tests/test_plugin_utils.py,sha256=ayksWdrFY7kOiA0wVEjKFmFCF3UhH3lG8tzPVOpwysk,528
233
234
  tests/unit_tests/test_ring_progress_bar.py,sha256=hDlqkQho7FR7HAfM4Zrr4q1m773a3_rQ8CbM1GqDDSE,12252
234
235
  tests/unit_tests/test_rpc_register.py,sha256=hECjZEimd440mwRrO0rg7L3PKN7__3DgjmESN6wx3bo,1179
235
236
  tests/unit_tests/test_rpc_widget_handler.py,sha256=ceQ3BPnBIFY2Hy-sDPw0wxxREVTTphILts0gvX9qoUw,234
@@ -247,8 +248,8 @@ tests/unit_tests/test_configs/config_device_no_entry.yaml,sha256=hdvue9KLc_kfNzG
247
248
  tests/unit_tests/test_configs/config_scan.yaml,sha256=vo484BbWOjA_e-h6bTjSV9k7QaQHrlAvx-z8wtY-P4E,1915
248
249
  tests/unit_tests/test_msgs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
249
250
  tests/unit_tests/test_msgs/available_scans_message.py,sha256=m_z97hIrjHXXMa2Ex-UvsPmTxOYXfjxyJaGkIY6StTY,46532
250
- bec_widgets-0.79.3.dist-info/METADATA,sha256=KNOSryTtLS3GCT2Zw_6V5l9g4xxCSX_MAvinpePvrBU,1427
251
- bec_widgets-0.79.3.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
252
- bec_widgets-0.79.3.dist-info/entry_points.txt,sha256=3otEkCdDB9LZJuBLzG4pFLK5Di0CVybN_12IsZrQ-58,166
253
- bec_widgets-0.79.3.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
254
- bec_widgets-0.79.3.dist-info/RECORD,,
251
+ bec_widgets-0.80.1.dist-info/METADATA,sha256=W5BiUCnKEu7UtciQ89fZp4AqzfUxwuHdNW84R2Yg8IQ,1309
252
+ bec_widgets-0.80.1.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
253
+ bec_widgets-0.80.1.dist-info/entry_points.txt,sha256=3otEkCdDB9LZJuBLzG4pFLK5Di0CVybN_12IsZrQ-58,166
254
+ bec_widgets-0.80.1.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
255
+ bec_widgets-0.80.1.dist-info/RECORD,,
@@ -10,7 +10,7 @@ Targeting the unique needs of beamline scientists, BEC Widgets stands out with i
10
10
  **Key Features**:
11
11
 
12
12
  - **Integration:** Seamlessly integrates with [BEC (Beamline Experiment Control)](https://gitlab.psi.ch/bec/bec), ensuring a cohesive and efficient experiment control experience.
13
- - **Support for PyQt5 and PyQt6:** Provides compatibility with both PyQt5 and PyQt6, offering versatility in your development environment.
13
+ - **Support for Pyside6 and PyQt6:** Provides compatibility with both Pyside6 and PyQt6, offering versatility in your development environment.
14
14
  - **Widget Modularity:** Features modular widgets that can be easily combined to create customized applications, perfectly aligning with the diverse needs of beamline experiments.
15
15
 
16
16
  ## Getting Started
@@ -16,10 +16,10 @@ To install BEC Widgets using the pip package manager, execute the following comm
16
16
  pip install 'bec_widgets[pyqt6]'
17
17
  ```
18
18
 
19
- In case you want to use PyQt5, you can install it by using the following command:
19
+ In case you want to use Pyside6, you can install it by using the following command:
20
20
 
21
21
  ```bash
22
- pip install 'bec_widgets[pyqt5]'
22
+ pip install 'bec_widgets[pyside6]'
23
23
  ```
24
24
 
25
25
  **Troubleshooting**
pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "bec_widgets"
7
- version = "0.79.3"
7
+ version = "0.80.1"
8
8
  description = "BEC Widgets"
9
9
  requires-python = ">=3.10"
10
10
  classifiers = [
@@ -37,7 +37,6 @@ dev = [
37
37
  "pytest-xvfb~=3.0",
38
38
  "pytest~=8.0",
39
39
  ]
40
- pyqt5 = ["PyQt5>=5.9", "PyQtWebEngine>=5.9"]
41
40
  pyqt6 = ["PyQt6>=6.7", "PyQt6-WebEngine>=6.7"]
42
41
  pyside6 = ["PySide6>=6.7"]
43
42
 
@@ -4,6 +4,7 @@ import black
4
4
  import isort
5
5
 
6
6
  from bec_widgets.cli.generate_cli import ClientGenerator
7
+ from bec_widgets.utils.plugin_utils import BECClassContainer, BECClassInfo
7
8
 
8
9
  # pylint: disable=missing-function-docstring
9
10
 
@@ -33,11 +34,31 @@ class MockBECFigure:
33
34
 
34
35
  def test_client_generator_with_black_formatting():
35
36
  generator = ClientGenerator()
36
- rpc_classes = {
37
- "connector_classes": [MockBECWaveform1D, MockBECFigure],
38
- "top_level_classes": [MockBECFigure],
39
- }
40
- generator.generate_client(rpc_classes)
37
+ container = BECClassContainer()
38
+ container.add_class(
39
+ BECClassInfo(
40
+ name="MockBECWaveform1D",
41
+ module="test_module",
42
+ file="test_file",
43
+ obj=MockBECWaveform1D,
44
+ is_connector=True,
45
+ is_widget=True,
46
+ is_top_level=False,
47
+ )
48
+ )
49
+ container.add_class(
50
+ BECClassInfo(
51
+ name="MockBECFigure",
52
+ module="test_module",
53
+ file="test_file",
54
+ obj=MockBECFigure,
55
+ is_connector=True,
56
+ is_widget=True,
57
+ is_top_level=True,
58
+ )
59
+ )
60
+
61
+ generator.generate_client(container)
41
62
 
42
63
  # Format the expected output with black to ensure it matches the generator output
43
64
  expected_output = dedent(
@@ -51,6 +72,7 @@ def test_client_generator_with_black_formatting():
51
72
 
52
73
  # pylint: skip-file
53
74
 
75
+
54
76
  class Widgets(str, enum.Enum):
55
77
  """
56
78
  Enum for the available widgets.
@@ -59,18 +81,6 @@ def test_client_generator_with_black_formatting():
59
81
  MockBECFigure = "MockBECFigure"
60
82
 
61
83
 
62
- class MockBECWaveform1D(RPCBase):
63
- @rpc_call
64
- def set_frequency(self, frequency: float) -> list:
65
- """
66
- Set the frequency of the waveform.
67
- """
68
- @rpc_call
69
- def set_amplitude(self, amplitude: float) -> tuple[float, float]:
70
- """
71
- Set the amplitude of the waveform.
72
- """
73
-
74
84
  class MockBECFigure(RPCBase):
75
85
  @rpc_call
76
86
  def add_plot(self, plot_id: str):
@@ -83,6 +93,20 @@ def test_client_generator_with_black_formatting():
83
93
  """
84
94
  Remove a plot from the figure.
85
95
  """
96
+
97
+
98
+ class MockBECWaveform1D(RPCBase):
99
+ @rpc_call
100
+ def set_frequency(self, frequency: float) -> list:
101
+ """
102
+ Set the frequency of the waveform.
103
+ """
104
+
105
+ @rpc_call
106
+ def set_amplitude(self, amplitude: float) -> tuple[float, float]:
107
+ """
108
+ Set the amplitude of the waveform.
109
+ """
86
110
  '''
87
111
  )
88
112
 
@@ -3,9 +3,8 @@ from bec_widgets.utils.plugin_utils import get_rpc_classes
3
3
 
4
4
  def test_client_generator_classes():
5
5
  out = get_rpc_classes("bec_widgets")
6
- assert list(out.keys()) == ["connector_classes", "top_level_classes"]
7
- connector_cls_names = [cls.__name__ for cls in out["connector_classes"]]
8
- top_level_cls_names = [cls.__name__ for cls in out["top_level_classes"]]
6
+ connector_cls_names = [cls.__name__ for cls in out.connector_classes]
7
+ top_level_cls_names = [cls.__name__ for cls in out.top_level_classes]
9
8
 
10
9
  assert "BECFigure" in connector_cls_names
11
10
  assert "BECWaveform" in connector_cls_names