bec-widgets 0.104.0__py3-none-any.whl → 0.106.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.
Files changed (34) hide show
  1. CHANGELOG.md +26 -24
  2. PKG-INFO +1 -1
  3. bec_widgets/cli/client.py +39 -0
  4. bec_widgets/cli/client_utils.py +5 -4
  5. bec_widgets/cli/server.py +21 -9
  6. bec_widgets/examples/plugin_example_pyside/tictactoe.py +0 -1
  7. bec_widgets/utils/bec_connector.py +5 -4
  8. bec_widgets/utils/bec_dispatcher.py +9 -13
  9. bec_widgets/utils/bec_widget.py +4 -0
  10. bec_widgets/widgets/dap_combo_box/__init__.py +0 -0
  11. bec_widgets/widgets/dap_combo_box/dap_combo_box.py +185 -0
  12. bec_widgets/widgets/dap_combo_box/dap_combo_box.pyproject +1 -0
  13. bec_widgets/widgets/dap_combo_box/dap_combo_box_plugin.py +54 -0
  14. bec_widgets/widgets/dap_combo_box/register_dap_combo_box.py +15 -0
  15. bec_widgets/widgets/device_browser/device_item/device_item.py +4 -1
  16. bec_widgets/widgets/figure/figure.py +4 -1
  17. bec_widgets/widgets/figure/plots/axis_settings.py +3 -0
  18. bec_widgets/widgets/figure/plots/axis_settings.ui +38 -17
  19. bec_widgets/widgets/figure/plots/image/image.py +4 -1
  20. bec_widgets/widgets/figure/plots/image/image_item.py +4 -1
  21. bec_widgets/widgets/figure/plots/motor_map/motor_map.py +5 -2
  22. bec_widgets/widgets/figure/plots/plot_base.py +18 -1
  23. bec_widgets/widgets/figure/plots/waveform/waveform.py +7 -4
  24. bec_widgets/widgets/figure/plots/waveform/waveform_curve.py +4 -2
  25. bec_widgets/widgets/ring_progress_bar/ring_progress_bar.py +8 -5
  26. bec_widgets/widgets/scan_control/scan_group_box.py +6 -1
  27. bec_widgets/widgets/waveform/waveform_popups/curve_dialog/curve_dialog.py +6 -14
  28. bec_widgets/widgets/waveform/waveform_widget.py +13 -5
  29. {bec_widgets-0.104.0.dist-info → bec_widgets-0.106.0.dist-info}/METADATA +1 -1
  30. {bec_widgets-0.104.0.dist-info → bec_widgets-0.106.0.dist-info}/RECORD +34 -29
  31. pyproject.toml +1 -1
  32. {bec_widgets-0.104.0.dist-info → bec_widgets-0.106.0.dist-info}/WHEEL +0 -0
  33. {bec_widgets-0.104.0.dist-info → bec_widgets-0.106.0.dist-info}/entry_points.txt +0 -0
  34. {bec_widgets-0.104.0.dist-info → bec_widgets-0.106.0.dist-info}/licenses/LICENSE +0 -0
CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## v0.106.0 (2024-09-05)
4
+
5
+ ### Feature
6
+
7
+ * feat(plot_base): toggle to switch outer axes for plotting widgets ([`06d7741`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/06d7741622aea8556208cd17cae521c37333f8b6))
8
+
9
+ ### Refactor
10
+
11
+ * refactor: use DAPComboBox in curve_dialog selection ([`998a745`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/998a7451335b1b35c3e18691d3bab8d882e2d30b))
12
+
13
+ ### Test
14
+
15
+ * test: fix tests ([`6b15abc`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/6b15abcc73170cb49292741a619a08ee615e6250))
16
+
17
+ ## v0.105.0 (2024-09-04)
18
+
19
+ ### Feature
20
+
21
+ * feat: add dap_combobox ([`cc691d4`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/cc691d4039bde710e78f362d2f0e712f9e8f196f))
22
+
23
+ ### Refactor
24
+
25
+ * refactor: cleanup and renaming of slot/signals ([`0fd5cee`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/0fd5cee77611b6645326eaefa68455ea8de26597))
26
+
27
+ * refactor(logger): changed prints to logger calls ([`3a5d7d0`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/3a5d7d07966ab9b38ba33bda0bed38c30f500c66))
28
+
3
29
  ## v0.104.0 (2024-09-04)
4
30
 
5
31
  ### Documentation
@@ -131,27 +157,3 @@
131
157
  * fix(toolbar): widget action added ([`2efd487`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/2efd48736cbe04e84533f7933c552ea8274e2162))
132
158
 
133
159
  * fix(reset_button): reset button added ([`6ed1efc`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/6ed1efc6af193908f70aa37fb73157d2ca6a62f4))
134
-
135
- * fix(abort_button): abort button added; some minor fixes ([`a568633`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/a568633c3206a8c26069d140f2d9a548bf4124b0))
136
-
137
- ## v0.99.11 (2024-08-29)
138
-
139
- ### Fix
140
-
141
- * fix(resume_button): resume button added ([`8be8295`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/8be8295b2b38f36da210ab36c5da6d0a00e330cc))
142
-
143
- ### Refactor
144
-
145
- * refactor(icons): general app icon changed; jupyter app icon changed to material icon ([`5d73fe4`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/5d73fe455a568ad40a9fadc5ce6e249d782ad20d))
146
-
147
- * refactor: add option to select scan and hide arg bundle buttons ([`7dadab1`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/7dadab1f14aa41876ad39e8cdc7f7732248cc643))
148
-
149
- ## v0.99.10 (2024-08-29)
150
-
151
- ### Fix
152
-
153
- * fix(stop_button): queue logic scan changed to halt instead of abort and reset ([`4a89028`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/4a890281f7eaef02d0ec9f4c5bf080be11fe0fe3))
154
-
155
- ### Refactor
156
-
157
- * refactor(stop_button): stop button changed to QWidget and adapted for toolbar ([`097946f`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/097946fd688b8faf770e7cc0e689ea668206bc7a))
PKG-INFO CHANGED
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bec_widgets
3
- Version: 0.104.0
3
+ Version: 0.106.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
bec_widgets/cli/client.py CHANGED
@@ -22,6 +22,7 @@ class Widgets(str, enum.Enum):
22
22
  BECQueue = "BECQueue"
23
23
  BECStatusBox = "BECStatusBox"
24
24
  BECWaveformWidget = "BECWaveformWidget"
25
+ DapComboBox = "DapComboBox"
25
26
  DarkModeButton = "DarkModeButton"
26
27
  DeviceBrowser = "DeviceBrowser"
27
28
  DeviceComboBox = "DeviceComboBox"
@@ -1652,6 +1653,15 @@ class BECPlotBase(RPCBase):
1652
1653
  y(bool): Show grid on the y-axis.
1653
1654
  """
1654
1655
 
1656
+ @rpc_call
1657
+ def set_outer_axes(self, show: "bool" = True):
1658
+ """
1659
+ Set the outer axes of the plot widget.
1660
+
1661
+ Args:
1662
+ show(bool): Show the outer axes.
1663
+ """
1664
+
1655
1665
  @rpc_call
1656
1666
  def lock_aspect_ratio(self, lock):
1657
1667
  """
@@ -2312,6 +2322,35 @@ class BECWaveformWidget(RPCBase):
2312
2322
  """
2313
2323
 
2314
2324
 
2325
+ class DapComboBox(RPCBase):
2326
+ @rpc_call
2327
+ def select_y_axis(self, y_axis: str):
2328
+ """
2329
+ Slot to update the y axis.
2330
+
2331
+ Args:
2332
+ y_axis(str): Y axis.
2333
+ """
2334
+
2335
+ @rpc_call
2336
+ def select_x_axis(self, x_axis: str):
2337
+ """
2338
+ Slot to update the x axis.
2339
+
2340
+ Args:
2341
+ x_axis(str): X axis.
2342
+ """
2343
+
2344
+ @rpc_call
2345
+ def select_fit_model(self, fit_name: str | None):
2346
+ """
2347
+ Slot to update the fit model.
2348
+
2349
+ Args:
2350
+ default_device(str): Default device name.
2351
+ """
2352
+
2353
+
2315
2354
  class DarkModeButton(RPCBase):
2316
2355
  @rpc_call
2317
2356
  def toggle_dark_mode(self) -> "None":
@@ -80,7 +80,7 @@ def _get_output(process, logger) -> None:
80
80
  buf.clear()
81
81
  buf.append(remaining)
82
82
  except Exception as e:
83
- print(f"Error reading process output: {str(e)}")
83
+ logger.error(f"Error reading process output: {str(e)}")
84
84
 
85
85
 
86
86
  def _start_plot_process(gui_id: str, gui_class: type, config: dict | str, logger=None) -> None:
@@ -146,7 +146,7 @@ class BECGuiClientMixin:
146
146
  continue
147
147
  return ep.load()(gui=self)
148
148
  except Exception as e:
149
- print(f"Error loading auto update script from plugin: {str(e)}")
149
+ logger.error(f"Error loading auto update script from plugin: {str(e)}")
150
150
  return None
151
151
 
152
152
  @property
@@ -189,11 +189,12 @@ class BECGuiClientMixin:
189
189
  if self._process is None or self._process.poll() is not None:
190
190
  self._start_update_script()
191
191
  self._process, self._process_output_processing_thread = _start_plot_process(
192
- self._gui_id, self.__class__, self._client._service_config.config
192
+ self._gui_id, self.__class__, self._client._service_config.config, logger=logger
193
193
  )
194
194
  while not self.gui_is_alive():
195
195
  print("Waiting for GUI to start...")
196
196
  time.sleep(1)
197
+ logger.success(f"GUI started with id: {self._gui_id}")
197
198
 
198
199
  def close(self) -> None:
199
200
  """
@@ -226,7 +227,7 @@ class RPCBase:
226
227
  def __init__(self, gui_id: str = None, config: dict = None, parent=None) -> None:
227
228
  self._client = BECClient() # BECClient is a singleton; here, we simply get the instance
228
229
  self._config = config if config is not None else {}
229
- self._gui_id = gui_id if gui_id is not None else str(uuid.uuid4())
230
+ self._gui_id = gui_id if gui_id is not None else str(uuid.uuid4())[:5]
230
231
  self._parent = parent
231
232
  self._msg_wait_event = threading.Event()
232
233
  self._rpc_response = None
bec_widgets/cli/server.py CHANGED
@@ -53,9 +53,11 @@ class BECWidgetsCLIServer:
53
53
  self._heartbeat_timer.start(200)
54
54
 
55
55
  self.status = messages.BECStatus.RUNNING
56
+ logger.success(f"Server started with gui_id: {self.gui_id}")
56
57
 
57
58
  def on_rpc_update(self, msg: dict, metadata: dict):
58
59
  request_id = metadata.get("request_id")
60
+ logger.debug(f"Received RPC instruction: {msg}, metadata: {metadata}")
59
61
  try:
60
62
  obj = self.get_object_from_config(msg["parameter"])
61
63
  method = msg["action"]
@@ -63,9 +65,10 @@ class BECWidgetsCLIServer:
63
65
  kwargs = msg["parameter"].get("kwargs", {})
64
66
  res = self.run_rpc(obj, method, args, kwargs)
65
67
  except Exception as e:
66
- print(e)
68
+ logger.error(f"Error while executing RPC instruction: {e}")
67
69
  self.send_response(request_id, False, {"error": str(e)})
68
70
  else:
71
+ logger.debug(f"RPC instruction executed successfully: {res}")
69
72
  self.send_response(request_id, True, {"result": res})
70
73
 
71
74
  def send_response(self, request_id: str, accepted: bool, msg: dict):
@@ -113,6 +116,7 @@ class BECWidgetsCLIServer:
113
116
  return obj
114
117
 
115
118
  def emit_heartbeat(self):
119
+ logger.trace(f"Emitting heartbeat for {self.gui_id}")
116
120
  self.client.connector.set(
117
121
  MessageEndpoints.gui_heartbeat(self.gui_id),
118
122
  messages.StatusMessage(name=self.gui_id, status=self.status, info={}),
@@ -120,6 +124,7 @@ class BECWidgetsCLIServer:
120
124
  )
121
125
 
122
126
  def shutdown(self): # TODO not sure if needed when cleanup is done at level of BECConnector
127
+ logger.info(f"Shutting down server with gui_id: {self.gui_id}")
123
128
  self.status = messages.BECStatus.IDLE
124
129
  self._heartbeat_timer.stop()
125
130
  self.emit_heartbeat()
@@ -137,7 +142,8 @@ class SimpleFileLikeFromLogOutputFunc:
137
142
 
138
143
  def flush(self):
139
144
  lines, _, remaining = "".join(self._buffer).rpartition("\n")
140
- self._log_func(lines)
145
+ if lines:
146
+ self._log_func(lines)
141
147
  self._buffer = [remaining]
142
148
 
143
149
  def close(self):
@@ -155,12 +161,12 @@ def _start_server(gui_id: str, gui_class: Union[BECFigure, BECDockArea], config:
155
161
  # if no config is provided, use the default config
156
162
  service_config = ServiceConfig()
157
163
 
158
- bec_logger.configure(
159
- service_config.redis,
160
- QtRedisConnector,
161
- service_name="BECWidgetsCLIServer",
162
- service_config=service_config.service_config,
163
- )
164
+ # bec_logger.configure(
165
+ # service_config.redis,
166
+ # QtRedisConnector,
167
+ # service_name="BECWidgetsCLIServer",
168
+ # service_config=service_config.service_config,
169
+ # )
164
170
  server = BECWidgetsCLIServer(gui_id=gui_id, config=service_config, gui_class=gui_class)
165
171
  return server
166
172
 
@@ -175,6 +181,12 @@ def main():
175
181
 
176
182
  import bec_widgets
177
183
 
184
+ bec_logger.level = bec_logger.LOGLEVEL.DEBUG
185
+ if __name__ != "__main__":
186
+ # if not running as main, set the log level to critical
187
+ # pylint: disable=protected-access
188
+ bec_logger._stderr_log_level = bec_logger.LOGLEVEL.CRITICAL
189
+
178
190
  parser = argparse.ArgumentParser(description="BEC Widgets CLI Server")
179
191
  parser.add_argument("--id", type=str, help="The id of the server")
180
192
  parser.add_argument(
@@ -197,7 +209,7 @@ def main():
197
209
  )
198
210
  gui_class = BECFigure
199
211
 
200
- with redirect_stdout(SimpleFileLikeFromLogOutputFunc(logger.debug)):
212
+ with redirect_stdout(SimpleFileLikeFromLogOutputFunc(logger.info)):
201
213
  with redirect_stderr(SimpleFileLikeFromLogOutputFunc(logger.error)):
202
214
  app = QApplication(sys.argv)
203
215
  app.setApplicationName("BEC Figure")
@@ -16,7 +16,6 @@ class TicTacToe(QWidget): # pragma: no cover
16
16
  super().__init__(parent)
17
17
  self._state = DEFAULT_STATE
18
18
  self._turn_number = 0
19
- print("TicTac HERE !!!!!!")
20
19
 
21
20
  def minimumSizeHint(self):
22
21
  return QSize(200, 200)
@@ -6,7 +6,7 @@ import time
6
6
  import uuid
7
7
  from typing import Optional
8
8
 
9
- import yaml
9
+ from bec_lib.logger import bec_logger
10
10
  from bec_lib.utils.import_utils import lazy_import_from
11
11
  from pydantic import BaseModel, Field, field_validator
12
12
  from qtpy.QtCore import QObject, QRunnable, QThreadPool, Signal
@@ -17,6 +17,7 @@ from bec_widgets.qt_utils.error_popups import ErrorPopupUtility
17
17
  from bec_widgets.qt_utils.error_popups import SafeSlot as pyqtSlot
18
18
  from bec_widgets.utils.yaml_dialog import load_yaml, load_yaml_gui, save_yaml, save_yaml_gui
19
19
 
20
+ logger = bec_logger.logger
20
21
  BECDispatcher = lazy_import_from("bec_widgets.utils.bec_dispatcher", ("BECDispatcher",))
21
22
 
22
23
 
@@ -81,9 +82,9 @@ class BECConnector:
81
82
  # the function depends on BECClient, and BECDispatcher
82
83
  @pyqtSlot()
83
84
  def terminate(client=self.client, dispatcher=self.bec_dispatcher):
84
- print("Disconnecting", repr(dispatcher))
85
+ logger.info("Disconnecting", repr(dispatcher))
85
86
  dispatcher.disconnect_all()
86
- print("Shutting down BEC Client", repr(client))
87
+ logger.info("Shutting down BEC Client", repr(client))
87
88
  client.shutdown()
88
89
 
89
90
  BECConnector.EXIT_HANDLERS[self.client] = terminate
@@ -93,7 +94,7 @@ class BECConnector:
93
94
  self.config = config
94
95
  self.config.widget_class = self.__class__.__name__
95
96
  else:
96
- print(
97
+ logger.debug(
97
98
  f"No initial config found for {self.__class__.__name__}.\n"
98
99
  f"Initializing with default config."
99
100
  )
@@ -6,11 +6,14 @@ from typing import TYPE_CHECKING, Union
6
6
 
7
7
  import redis
8
8
  from bec_lib.client import BECClient
9
+ from bec_lib.logger import bec_logger
9
10
  from bec_lib.redis_connector import MessageObject, RedisConnector
10
11
  from bec_lib.service_config import ServiceConfig
11
12
  from qtpy.QtCore import QObject
12
13
  from qtpy.QtCore import Signal as pyqtSignal
13
14
 
15
+ logger = bec_logger.logger
16
+
14
17
  if TYPE_CHECKING:
15
18
  from bec_lib.endpoints import EndpointInfo
16
19
 
@@ -65,11 +68,6 @@ class QtRedisConnector(RedisConnector):
65
68
  cb(msg.content, msg.metadata)
66
69
 
67
70
 
68
- class BECClientWithoutLoggerInit(BECClient):
69
- def _initialize_logger(self):
70
- return
71
-
72
-
73
71
  class BECDispatcher:
74
72
  """Utility class to keep track of slots connected to a particular redis connector"""
75
73
 
@@ -94,24 +92,22 @@ class BECDispatcher:
94
92
  if not isinstance(config, ServiceConfig):
95
93
  # config is supposed to be a path
96
94
  config = ServiceConfig(config)
97
- self.client = BECClientWithoutLoggerInit(
98
- config=config, connector_cls=QtRedisConnector
99
- ) # , forced=True)
100
- else:
101
- self.client = BECClientWithoutLoggerInit(
102
- connector_cls=QtRedisConnector
103
- ) # , forced=True)
95
+ self.client = BECClient(
96
+ config=config, connector_cls=QtRedisConnector, name="BECWidgets"
97
+ )
104
98
  else:
105
99
  if self.client.started:
106
100
  # have to reinitialize client to use proper connector
101
+ logger.info("Shutting down BECClient to switch to QtRedisConnector")
107
102
  self.client.shutdown()
108
103
  self.client._BECClient__init_params["connector_cls"] = QtRedisConnector
109
104
 
110
105
  try:
111
106
  self.client.start()
112
107
  except redis.exceptions.ConnectionError:
113
- print("Could not connect to Redis, skipping start of BECClient.")
108
+ logger.warning("Could not connect to Redis, skipping start of BECClient.")
114
109
 
110
+ logger.success("Initialized BECDispatcher")
115
111
  self._initialized = True
116
112
 
117
113
  @classmethod
@@ -1,12 +1,15 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import darkdetect
4
+ from bec_lib.logger import bec_logger
4
5
  from qtpy.QtCore import Slot
5
6
  from qtpy.QtWidgets import QApplication, QWidget
6
7
 
7
8
  from bec_widgets.utils.bec_connector import BECConnector, ConnectionConfig
8
9
  from bec_widgets.utils.colors import set_theme
9
10
 
11
+ logger = bec_logger.logger
12
+
10
13
 
11
14
  class BECWidget(BECConnector):
12
15
  """Mixin class for all BEC widgets, to handle cleanup"""
@@ -54,6 +57,7 @@ class BECWidget(BECConnector):
54
57
  set_theme("light")
55
58
 
56
59
  if theme_update:
60
+ logger.debug(f"Subscribing to theme updates for {self.__class__.__name__}")
57
61
  self._connect_to_theme_change()
58
62
 
59
63
  def _connect_to_theme_change(self):
File without changes
@@ -0,0 +1,185 @@
1
+ """ Module for DapComboBox widget class to select a DAP model from a combobox. """
2
+
3
+ from bec_lib.logger import bec_logger
4
+ from qtpy.QtCore import Property, Signal, Slot
5
+ from qtpy.QtWidgets import QComboBox, QVBoxLayout, QWidget
6
+
7
+ from bec_widgets.utils.bec_widget import BECWidget
8
+
9
+ logger = bec_logger.logger
10
+
11
+
12
+ class DapComboBox(BECWidget, QWidget):
13
+ """
14
+ The DAPComboBox widget is an extension to the QComboBox with all avaialble DAP model from BEC.
15
+
16
+ Args:
17
+ parent: Parent widget.
18
+ client: BEC client object.
19
+ gui_id: GUI ID.
20
+ default: Default device name.
21
+ """
22
+
23
+ ICON_NAME = "data_exploration"
24
+
25
+ USER_ACCESS = ["select_y_axis", "select_x_axis", "select_fit_model"]
26
+
27
+ ### Signals ###
28
+ # Signal to emit a new dap_config: (x_axis, y_axis, fit_model). Can be used to add a new DAP process
29
+ # in the BECWaveformWidget using its add_dap method. The signal is emitted when the user selects a new
30
+ # fit model, but only if x_axis and y_axis are set.
31
+ new_dap_config = Signal(str, str, str)
32
+ # Signal to emit the name of the updated x_axis
33
+ x_axis_updated = Signal(str)
34
+ # Signal to emit the name of the updated y_axis
35
+ y_axis_updated = Signal(str)
36
+ # Signal to emit the name of the updated fit model
37
+ fit_model_updated = Signal(str)
38
+
39
+ def __init__(
40
+ self, parent=None, client=None, gui_id: str | None = None, default_fit: str | None = None
41
+ ):
42
+ super().__init__(client=client, gui_id=gui_id)
43
+ QWidget.__init__(self, parent=parent)
44
+ self.layout = QVBoxLayout(self)
45
+ self.fit_model_combobox = QComboBox(self)
46
+ self.layout.addWidget(self.fit_model_combobox)
47
+ self.layout.setContentsMargins(0, 0, 0, 0)
48
+ self._available_models = None
49
+ self._x_axis = None
50
+ self._y_axis = None
51
+ self.populate_fit_model_combobox()
52
+ self.fit_model_combobox.currentTextChanged.connect(self._update_current_fit)
53
+ # Set default fit model
54
+ self.select_default_fit(default_fit)
55
+
56
+ def select_default_fit(self, default_fit: str | None):
57
+ """Set the default fit model.
58
+
59
+ Args:
60
+ default_fit(str): Default fit model.
61
+ """
62
+ if self._validate_dap_model(default_fit):
63
+ self.select_fit_model(default_fit)
64
+ else:
65
+ self.select_fit_model("GaussianModel")
66
+
67
+ @property
68
+ def available_models(self):
69
+ """Available models property."""
70
+ return self._available_models
71
+
72
+ @available_models.setter
73
+ def available_models(self, available_models: list[str]):
74
+ """Set the available models.
75
+
76
+ Args:
77
+ available_models(list[str]): Available models.
78
+ """
79
+ self._available_models = available_models
80
+
81
+ @Property(str)
82
+ def x_axis(self):
83
+ """X axis property."""
84
+ return self._x_axis
85
+
86
+ @x_axis.setter
87
+ def x_axis(self, x_axis: str):
88
+ """Set the x axis.
89
+
90
+ Args:
91
+ x_axis(str): X axis.
92
+ """
93
+ # TODO add validator for x axis -> Positioner? or also device (must be monitored)!!
94
+ self._x_axis = x_axis
95
+ self.x_axis_updated.emit(x_axis)
96
+
97
+ @Property(str)
98
+ def y_axis(self):
99
+ """Y axis property."""
100
+ # TODO add validator for y axis -> Positioner & Device? Must be a monitored device!!
101
+ return self._y_axis
102
+
103
+ @y_axis.setter
104
+ def y_axis(self, y_axis: str):
105
+ """Set the y axis.
106
+
107
+ Args:
108
+ y_axis(str): Y axis.
109
+ """
110
+ self._y_axis = y_axis
111
+ self.y_axis_updated.emit(y_axis)
112
+
113
+ def _update_current_fit(self, fit_name: str):
114
+ """Update the current fit."""
115
+ self.fit_model_updated.emit(fit_name)
116
+ if self.x_axis is not None and self.y_axis is not None:
117
+ self.new_dap_config.emit(self._x_axis, self._y_axis, fit_name)
118
+
119
+ @Slot(str)
120
+ def select_x_axis(self, x_axis: str):
121
+ """Slot to update the x axis.
122
+
123
+ Args:
124
+ x_axis(str): X axis.
125
+ """
126
+ self.x_axis = x_axis
127
+
128
+ @Slot(str)
129
+ def select_y_axis(self, y_axis: str):
130
+ """Slot to update the y axis.
131
+
132
+ Args:
133
+ y_axis(str): Y axis.
134
+ """
135
+ self.y_axis = y_axis
136
+
137
+ @Slot(str)
138
+ def select_fit_model(self, fit_name: str | None):
139
+ """Slot to update the fit model.
140
+
141
+ Args:
142
+ default_device(str): Default device name.
143
+ """
144
+ if not self._validate_dap_model(fit_name):
145
+ raise ValueError(f"Fit {fit_name} is not valid.")
146
+ self.fit_model_combobox.setCurrentText(fit_name)
147
+
148
+ def populate_fit_model_combobox(self):
149
+ """Populate the fit_model_combobox with the devices."""
150
+ # pylint: disable=protected-access
151
+ self.available_models = [model for model in self.client.dap._available_dap_plugins.keys()]
152
+ self.fit_model_combobox.clear()
153
+ self.fit_model_combobox.addItems(self.available_models)
154
+
155
+ def _validate_dap_model(self, model: str | None) -> bool:
156
+ """Validate the DAP model.
157
+
158
+ Args:
159
+ model(str): Model name.
160
+ """
161
+ if model is None:
162
+ return False
163
+ if model not in self.available_models:
164
+ return False
165
+ return True
166
+
167
+
168
+ # pragma: no cover
169
+ def main():
170
+ """Main function to run the DapComboBox widget."""
171
+ import sys
172
+
173
+ from qtpy.QtWidgets import QApplication
174
+
175
+ from bec_widgets.utils.colors import set_theme
176
+
177
+ app = QApplication(sys.argv)
178
+ set_theme("auto")
179
+ widget = DapComboBox()
180
+ widget.show()
181
+ sys.exit(app.exec_())
182
+
183
+
184
+ if __name__ == "__main__":
185
+ main()
@@ -0,0 +1 @@
1
+ {'files': ['dap_combo_box.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.dap_combo_box.dap_combo_box import DapComboBox
8
+
9
+ DOM_XML = """
10
+ <ui language='c++'>
11
+ <widget class='DapComboBox' name='dap_combo_box'>
12
+ </widget>
13
+ </ui>
14
+ """
15
+
16
+
17
+ class DapComboBoxPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
18
+ def __init__(self):
19
+ super().__init__()
20
+ self._form_editor = None
21
+
22
+ def createWidget(self, parent):
23
+ t = DapComboBox(parent)
24
+ return t
25
+
26
+ def domXml(self):
27
+ return DOM_XML
28
+
29
+ def group(self):
30
+ return "BEC Selection Widgets"
31
+
32
+ def icon(self):
33
+ return designer_material_icon(DapComboBox.ICON_NAME)
34
+
35
+ def includeFile(self):
36
+ return "dap_combo_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 "DapComboBox"
49
+
50
+ def toolTip(self):
51
+ return ""
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.dap_combo_box.dap_combo_box_plugin import DapComboBoxPlugin
10
+
11
+ QPyDesignerCustomWidgetCollection.addCustomWidget(DapComboBoxPlugin())
12
+
13
+
14
+ if __name__ == "__main__": # pragma: no cover
15
+ main()
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING
4
4
 
5
+ from bec_lib.logger import bec_logger
5
6
  from qtpy.QtCore import QMimeData, Qt
6
7
  from qtpy.QtGui import QDrag
7
8
  from qtpy.QtWidgets import QHBoxLayout, QLabel, QWidget
@@ -9,6 +10,8 @@ from qtpy.QtWidgets import QHBoxLayout, QLabel, QWidget
9
10
  if TYPE_CHECKING:
10
11
  from qtpy.QtGui import QMouseEvent
11
12
 
13
+ logger = bec_logger.logger
14
+
12
15
 
13
16
  class DeviceItem(QWidget):
14
17
  def __init__(self, device: str) -> None:
@@ -37,7 +40,7 @@ class DeviceItem(QWidget):
37
40
  drag.exec_(Qt.MoveAction)
38
41
 
39
42
  def mouseDoubleClickEvent(self, event: QMouseEvent) -> None:
40
- print("Double Clicked")
43
+ logger.debug("Double Clicked")
41
44
  # TODO: Implement double click action for opening the device properties dialog
42
45
  return super().mouseDoubleClickEvent(event)
43
46