mirror of
https://github.com/bec-project/bec_widgets.git
synced 2026-04-15 13:10:54 +02:00
Compare commits
6 Commits
v1.24.4
...
feature/ac
| Author | SHA1 | Date | |
|---|---|---|---|
| 4947549a20 | |||
| 59e4c2f612 | |||
|
|
15e11b287d | ||
| 7cbebbb1f0 | |||
|
|
66f4f9bfa8 | ||
| 66c6c7fa50 |
16
CHANGELOG.md
16
CHANGELOG.md
@@ -1,6 +1,22 @@
|
||||
# CHANGELOG
|
||||
|
||||
|
||||
## v1.25.0 (2025-03-07)
|
||||
|
||||
### Features
|
||||
|
||||
- **waveform**: Add slice handling and reset functionality for async updates
|
||||
([`7cbebbb`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/7cbebbb1f00ea2e2b3678c96b183a877e59c5240))
|
||||
|
||||
|
||||
## v1.24.5 (2025-03-06)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Add support for additional keyword arguments in widget constructors
|
||||
([`66c6c7f`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/66c6c7fa5075dcd5b6729fa3c2166aa821a6c51d))
|
||||
|
||||
|
||||
## v1.24.4 (2025-03-05)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -11,9 +11,11 @@ from contextlib import contextmanager
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import msgpack
|
||||
from bec_lib.endpoints import MessageEndpoints
|
||||
from bec_lib.logger import bec_logger
|
||||
from bec_lib.utils.import_utils import isinstance_based_on_class_name, lazy_import, lazy_import_from
|
||||
from cryptography.fernet import Fernet
|
||||
|
||||
import bec_widgets.cli.client as client
|
||||
from bec_widgets.cli.auto_updates import AutoUpdates
|
||||
@@ -67,7 +69,14 @@ def _get_output(process, logger) -> None:
|
||||
logger.error(f"Error reading process output: {str(e)}")
|
||||
|
||||
|
||||
def _start_plot_process(gui_id: str, gui_class: type, config: dict | str, logger=None) -> None:
|
||||
def _start_plot_process(
|
||||
gui_id: str,
|
||||
gui_class: type,
|
||||
config: dict | str,
|
||||
acl_data: bytes | None = None,
|
||||
token: bytes | None = None,
|
||||
logger=None,
|
||||
) -> None:
|
||||
"""
|
||||
Start the plot in a new process.
|
||||
|
||||
@@ -84,6 +93,8 @@ def _start_plot_process(gui_id: str, gui_class: type, config: dict | str, logger
|
||||
|
||||
env_dict = os.environ.copy()
|
||||
env_dict["PYTHONUNBUFFERED"] = "1"
|
||||
env_dict["BEC_GUI_ACL"] = acl_data or b""
|
||||
env_dict["BEC_GUI_TOKEN"] = token or b""
|
||||
|
||||
if logger is None:
|
||||
stdout_redirect = subprocess.DEVNULL
|
||||
@@ -179,6 +190,7 @@ class BECGuiClient(RPCBase):
|
||||
self._gui_started_event = threading.Event()
|
||||
self._process = None
|
||||
self._process_output_processing_thread = None
|
||||
self._fernet = None
|
||||
|
||||
@property
|
||||
def windows(self):
|
||||
@@ -263,6 +275,18 @@ class BECGuiClient(RPCBase):
|
||||
self._do_show_all()
|
||||
self._gui_started_event.set()
|
||||
|
||||
# def _update_gui_acls(self, username: str, password: str | None):
|
||||
# self._client.connector.send(
|
||||
# MessageEndpoints.gui_acls(self._gui_id),
|
||||
# messages.CredentialsMessage(
|
||||
# credentials={
|
||||
# "token": self._fernet.encrypt(
|
||||
# msgpack.dumps({"username": username, "password": password})
|
||||
# )
|
||||
# }
|
||||
# ),
|
||||
# )
|
||||
|
||||
def start_server(self, wait=False) -> None:
|
||||
"""
|
||||
Start the GUI server, and execute callback when it is launched
|
||||
@@ -271,8 +295,18 @@ class BECGuiClient(RPCBase):
|
||||
logger.success("GUI starting...")
|
||||
self._startup_timeout = 5
|
||||
self._gui_started_event.clear()
|
||||
encr_token = Fernet.generate_key()
|
||||
self._fernet = Fernet(encr_token)
|
||||
conn = self._client.connector._redis_conn.connection_pool.connection_kwargs
|
||||
acl_data = {"username": conn.get("username"), "password": conn.get("password")}
|
||||
acl_data = self._fernet.encrypt(msgpack.dumps(acl_data))
|
||||
self._process, self._process_output_processing_thread = _start_plot_process(
|
||||
self._gui_id, self.__class__, self._client._service_config.config, logger=logger
|
||||
self._gui_id,
|
||||
self.__class__,
|
||||
self._client._service_config.config,
|
||||
acl_data=acl_data,
|
||||
token=encr_token,
|
||||
logger=logger,
|
||||
)
|
||||
|
||||
def gui_started_callback(callback):
|
||||
|
||||
@@ -2,16 +2,19 @@ from __future__ import annotations
|
||||
|
||||
import functools
|
||||
import json
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
import types
|
||||
from contextlib import contextmanager, redirect_stderr, redirect_stdout
|
||||
from typing import Union
|
||||
|
||||
import msgpack
|
||||
from bec_lib.endpoints import MessageEndpoints
|
||||
from bec_lib.logger import bec_logger
|
||||
from bec_lib.service_config import ServiceConfig
|
||||
from bec_lib.utils.import_utils import lazy_import
|
||||
from cryptography.fernet import Fernet
|
||||
from qtpy.QtCore import Qt, QTimer
|
||||
from redis.exceptions import RedisError
|
||||
|
||||
@@ -57,7 +60,10 @@ class BECWidgetsCLIServer:
|
||||
client=None,
|
||||
config=None,
|
||||
gui_class: Union[BECFigure, BECDockArea] = BECFigure,
|
||||
token: str = None,
|
||||
) -> None:
|
||||
self._fernet = Fernet(token) if token else None
|
||||
self._init_acls(config)
|
||||
self.status = messages.BECStatus.BUSY
|
||||
self.dispatcher = BECDispatcher(config=config) if dispatcher is None else dispatcher
|
||||
self.client = self.dispatcher.client if client is None else client
|
||||
@@ -67,6 +73,8 @@ class BECWidgetsCLIServer:
|
||||
self.rpc_register = RPCRegister()
|
||||
self.rpc_register.add_rpc(self.gui)
|
||||
|
||||
# self.dispatcher.connect_slot(self.on_acl_update, MessageEndpoints.gui_acl(self.gui_id))
|
||||
|
||||
self.dispatcher.connect_slot(
|
||||
self.on_rpc_update, MessageEndpoints.gui_instructions(self.gui_id)
|
||||
)
|
||||
@@ -79,6 +87,13 @@ class BECWidgetsCLIServer:
|
||||
self.status = messages.BECStatus.RUNNING
|
||||
logger.success(f"Server started with gui_id: {self.gui_id}")
|
||||
|
||||
def _init_acls(self, config: ServiceConfig):
|
||||
acl_data = os.getenv("BEC_GUI_ACL")
|
||||
if not acl_data:
|
||||
return
|
||||
acl_data = msgpack.loads(self._fernet.decrypt(acl_data))
|
||||
config.config["acl"] = acl_data
|
||||
|
||||
def on_rpc_update(self, msg: dict, metadata: dict):
|
||||
request_id = metadata.get("request_id")
|
||||
logger.debug(f"Received RPC instruction: {msg}, metadata: {metadata}")
|
||||
@@ -96,6 +111,9 @@ class BECWidgetsCLIServer:
|
||||
logger.debug(f"RPC instruction executed successfully: {res}")
|
||||
self.send_response(request_id, True, {"result": res})
|
||||
|
||||
def on_acl_update(self, msg: dict, metadata: dict):
|
||||
logger.debug(f"Received ACL update: {msg}, metadata: {metadata}")
|
||||
|
||||
def send_response(self, request_id: str, accepted: bool, msg: dict):
|
||||
self.client.connector.set_and_publish(
|
||||
MessageEndpoints.gui_instruction_response(request_id),
|
||||
@@ -190,13 +208,10 @@ def _start_server(gui_id: str, gui_class: Union[BECFigure, BECDockArea], config:
|
||||
# if no config is provided, use the default config
|
||||
service_config = ServiceConfig()
|
||||
|
||||
# bec_logger.configure(
|
||||
# service_config.redis,
|
||||
# QtRedisConnector,
|
||||
# service_name="BECWidgetsCLIServer",
|
||||
# service_config=service_config.service_config,
|
||||
# )
|
||||
server = BECWidgetsCLIServer(gui_id=gui_id, config=service_config, gui_class=gui_class)
|
||||
token = os.getenv("BEC_GUI_TOKEN")
|
||||
server = BECWidgetsCLIServer(
|
||||
gui_id=gui_id, config=service_config, gui_class=gui_class, token=token
|
||||
)
|
||||
return server
|
||||
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ class BECWidget(BECConnector):
|
||||
config: ConnectionConfig = None,
|
||||
gui_id: str = None,
|
||||
theme_update: bool = False,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
Base class for all BEC widgets. This class should be used as a mixin class for all BEC widgets, e.g.:
|
||||
@@ -44,7 +45,7 @@ class BECWidget(BECConnector):
|
||||
"""
|
||||
if not isinstance(self, QWidget):
|
||||
raise RuntimeError(f"{repr(self)} is not a subclass of QWidget")
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
super().__init__(client=client, config=config, gui_id=gui_id, **kwargs)
|
||||
|
||||
# Set the theme to auto if it is not set yet
|
||||
app = QApplication.instance()
|
||||
|
||||
@@ -162,13 +162,14 @@ class BECFigure(BECWidget, pg.GraphicsLayoutWidget):
|
||||
config: Optional[FigureConfig] = None,
|
||||
client=None,
|
||||
gui_id: Optional[str] = None,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
if config is None:
|
||||
config = FigureConfig(widget_class=self.__class__.__name__)
|
||||
else:
|
||||
if isinstance(config, dict):
|
||||
config = FigureConfig(**config)
|
||||
super().__init__(client=client, gui_id=gui_id)
|
||||
super().__init__(client=client, gui_id=gui_id, config=config, **kwargs)
|
||||
pg.GraphicsLayoutWidget.__init__(self, parent)
|
||||
|
||||
self.widget_handler = WidgetHandler()
|
||||
@@ -573,10 +574,7 @@ class BECFigure(BECWidget, pg.GraphicsLayoutWidget):
|
||||
config=config,
|
||||
**axis_kwargs,
|
||||
)
|
||||
# has to be changed manually to ensure unique id, if config is copied from existing widget, the id could be
|
||||
# used otherwise multiple times
|
||||
widget.set_gui_id(widget_id)
|
||||
|
||||
widget.config.row = row
|
||||
widget.config.col = col
|
||||
|
||||
@@ -588,6 +586,7 @@ class BECFigure(BECWidget, pg.GraphicsLayoutWidget):
|
||||
self.config.num_cols = max(self.config.num_cols, col + 1)
|
||||
|
||||
# Saving config for future referencing
|
||||
|
||||
self.config.widgets[widget_id] = widget.config
|
||||
self._widgets[widget_id] = widget
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ class BECImageItem(BECConnector, pg.ImageItem):
|
||||
self.config = config
|
||||
else:
|
||||
self.config = config
|
||||
super().__init__(config=config, gui_id=gui_id)
|
||||
super().__init__(config=config, gui_id=gui_id, **kwargs)
|
||||
pg.ImageItem.__init__(self)
|
||||
|
||||
self.parent_image = parent_image
|
||||
|
||||
@@ -98,10 +98,11 @@ class BECPlotBase(BECConnector, pg.GraphicsLayout):
|
||||
config: Optional[SubplotConfig] = None,
|
||||
client=None,
|
||||
gui_id: Optional[str] = None,
|
||||
**kwargs,
|
||||
):
|
||||
if config is None:
|
||||
config = SubplotConfig(widget_class=self.__class__.__name__)
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
super().__init__(client=client, config=config, gui_id=gui_id, **kwargs)
|
||||
pg.GraphicsLayout.__init__(self, parent)
|
||||
|
||||
self.figure = parent_figure
|
||||
|
||||
@@ -99,11 +99,17 @@ class BECWaveform(BECPlotBase):
|
||||
config: Optional[Waveform1DConfig] = None,
|
||||
client=None,
|
||||
gui_id: Optional[str] = None,
|
||||
**kwargs,
|
||||
):
|
||||
if config is None:
|
||||
config = Waveform1DConfig(widget_class=self.__class__.__name__)
|
||||
super().__init__(
|
||||
parent=parent, parent_figure=parent_figure, config=config, client=client, gui_id=gui_id
|
||||
parent=parent,
|
||||
parent_figure=parent_figure,
|
||||
config=config,
|
||||
client=client,
|
||||
gui_id=gui_id,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
self._curves_data = defaultdict(dict)
|
||||
@@ -120,6 +126,8 @@ class BECWaveform(BECPlotBase):
|
||||
"label_suffix": "",
|
||||
}
|
||||
|
||||
self._slice_index = None
|
||||
|
||||
# Scan segment update proxy
|
||||
self.proxy_update_plot = pg.SignalProxy(
|
||||
self.scan_signal_update, rateLimit=25, slot=self._update_scan_curves
|
||||
@@ -1246,7 +1254,9 @@ class BECWaveform(BECPlotBase):
|
||||
x_data = None
|
||||
instruction = metadata.get("async_update", {}).get("type")
|
||||
max_shape = metadata.get("async_update", {}).get("max_shape", [])
|
||||
for curve in self._curves_data["async"].values():
|
||||
all_async_curves = self._curves_data["async"].values()
|
||||
# for curve in self._curves_data["async"].values():
|
||||
for curve in all_async_curves:
|
||||
y_entry = curve.config.signals.y.entry
|
||||
x_name = self._x_axis_mode["name"]
|
||||
for device, async_data in msg["signals"].items():
|
||||
@@ -1270,6 +1280,18 @@ class BECWaveform(BECPlotBase):
|
||||
curve.setData(x_data, new_data)
|
||||
else:
|
||||
curve.setData(new_data)
|
||||
elif instruction == "add_slice":
|
||||
current_slice_id = metadata.get("async_update", {}).get("index")
|
||||
data_plot = async_data["value"]
|
||||
if current_slice_id != self._slice_index:
|
||||
self._slice_index = current_slice_id
|
||||
new_data = data_plot
|
||||
else:
|
||||
x_data, y_data = curve.get_data()
|
||||
new_data = np.hstack((y_data, data_plot))
|
||||
|
||||
curve.setData(new_data)
|
||||
|
||||
elif instruction == "replace":
|
||||
if x_name == "timestamp":
|
||||
x_data = async_data["timestamp"]
|
||||
@@ -1518,6 +1540,10 @@ class BECWaveform(BECPlotBase):
|
||||
for curve_id in curve_ids_to_remove:
|
||||
self.remove_curve(curve_id)
|
||||
|
||||
def reset(self):
|
||||
self._slice_index = None
|
||||
super().reset()
|
||||
|
||||
def clear_all(self):
|
||||
sources = list(self._curves_data.keys())
|
||||
for source in sources:
|
||||
|
||||
@@ -97,7 +97,7 @@ class BECCurve(BECConnector, pg.PlotDataItem):
|
||||
else:
|
||||
self.config = config
|
||||
# config.widget_class = self.__class__.__name__
|
||||
super().__init__(config=config, gui_id=gui_id)
|
||||
super().__init__(config=config, gui_id=gui_id, **kwargs)
|
||||
pg.PlotDataItem.__init__(self, name=name)
|
||||
|
||||
self.parent_item = parent_item
|
||||
|
||||
@@ -13,9 +13,16 @@ class AbortButton(BECWidget, QWidget):
|
||||
ICON_NAME = "cancel"
|
||||
|
||||
def __init__(
|
||||
self, parent=None, client=None, config=None, gui_id=None, toolbar=False, scan_id=None
|
||||
self,
|
||||
parent=None,
|
||||
client=None,
|
||||
config=None,
|
||||
gui_id=None,
|
||||
toolbar=False,
|
||||
scan_id=None,
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
super().__init__(client=client, config=config, gui_id=gui_id, **kwargs)
|
||||
QWidget.__init__(self, parent=parent)
|
||||
|
||||
self.get_bec_shortcuts()
|
||||
|
||||
@@ -12,8 +12,8 @@ class ResetButton(BECWidget, QWidget):
|
||||
PLUGIN = True
|
||||
ICON_NAME = "restart_alt"
|
||||
|
||||
def __init__(self, parent=None, client=None, config=None, gui_id=None, toolbar=False):
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
def __init__(self, parent=None, client=None, config=None, gui_id=None, toolbar=False, **kwargs):
|
||||
super().__init__(client=client, config=config, gui_id=gui_id, **kwargs)
|
||||
QWidget.__init__(self, parent=parent)
|
||||
|
||||
self.get_bec_shortcuts()
|
||||
|
||||
@@ -12,8 +12,8 @@ class ResumeButton(BECWidget, QWidget):
|
||||
PLUGIN = True
|
||||
ICON_NAME = "resume"
|
||||
|
||||
def __init__(self, parent=None, client=None, config=None, gui_id=None, toolbar=False):
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
def __init__(self, parent=None, client=None, config=None, gui_id=None, toolbar=False, **kwargs):
|
||||
super().__init__(client=client, config=config, gui_id=gui_id, **kwargs)
|
||||
QWidget.__init__(self, parent=parent)
|
||||
|
||||
self.get_bec_shortcuts()
|
||||
|
||||
@@ -12,8 +12,8 @@ class StopButton(BECWidget, QWidget):
|
||||
PLUGIN = True
|
||||
ICON_NAME = "dangerous"
|
||||
|
||||
def __init__(self, parent=None, client=None, config=None, gui_id=None, toolbar=False):
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
def __init__(self, parent=None, client=None, config=None, gui_id=None, toolbar=False, **kwargs):
|
||||
super().__init__(client=client, config=config, gui_id=gui_id, **kwargs)
|
||||
QWidget.__init__(self, parent=parent)
|
||||
|
||||
self.get_bec_shortcuts()
|
||||
|
||||
@@ -12,8 +12,8 @@ class PositionIndicator(BECWidget, QWidget):
|
||||
PLUGIN = True
|
||||
ICON_NAME = "horizontal_distribute"
|
||||
|
||||
def __init__(self, parent=None, client=None, config=None, gui_id=None):
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
def __init__(self, parent=None, client=None, config=None, gui_id=None, **kwargs):
|
||||
super().__init__(client=client, config=config, gui_id=gui_id, **kwargs)
|
||||
QWidget.__init__(self, parent=parent)
|
||||
self.position = 50
|
||||
self.min_value = 0
|
||||
|
||||
@@ -59,7 +59,7 @@ class DeviceInputBase(BECWidget):
|
||||
ReadoutPriority.ON_REQUEST: "readout_on_request",
|
||||
}
|
||||
|
||||
def __init__(self, client=None, config=None, gui_id: str = None):
|
||||
def __init__(self, client=None, config=None, gui_id: str | None = None, **kwargs):
|
||||
|
||||
if config is None:
|
||||
config = DeviceInputConfig(widget_class=self.__class__.__name__)
|
||||
@@ -67,7 +67,7 @@ class DeviceInputBase(BECWidget):
|
||||
if isinstance(config, dict):
|
||||
config = DeviceInputConfig(**config)
|
||||
self.config = config
|
||||
super().__init__(client=client, config=config, gui_id=gui_id, theme_update=True)
|
||||
super().__init__(client=client, config=config, gui_id=gui_id, theme_update=True, **kwargs)
|
||||
self.get_bec_shortcuts()
|
||||
self._device_filter = []
|
||||
self._readout_filter = []
|
||||
|
||||
@@ -35,14 +35,14 @@ class DeviceSignalInputBase(BECWidget):
|
||||
Kind.config: "include_config_signals",
|
||||
}
|
||||
|
||||
def __init__(self, client=None, config=None, gui_id: str = None):
|
||||
def __init__(self, client=None, config=None, gui_id: str = None, **kwargs):
|
||||
if config is None:
|
||||
config = DeviceSignalInputBaseConfig(widget_class=self.__class__.__name__)
|
||||
else:
|
||||
if isinstance(config, dict):
|
||||
config = DeviceSignalInputBaseConfig(**config)
|
||||
self.config = config
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
super().__init__(client=client, config=config, gui_id=gui_id, **kwargs)
|
||||
|
||||
self._device = None
|
||||
self.get_bec_shortcuts()
|
||||
|
||||
@@ -45,8 +45,9 @@ class DeviceComboBox(DeviceInputBase, QComboBox):
|
||||
available_devices: list[str] | None = None,
|
||||
default: str | None = None,
|
||||
arg_name: str | None = None,
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
super().__init__(client=client, config=config, gui_id=gui_id, **kwargs)
|
||||
QComboBox.__init__(self, parent=parent)
|
||||
if arg_name is not None:
|
||||
self.config.arg_name = arg_name
|
||||
|
||||
@@ -48,11 +48,12 @@ class DeviceLineEdit(DeviceInputBase, QLineEdit):
|
||||
available_devices: list[str] | None = None,
|
||||
default: str | None = None,
|
||||
arg_name: str | None = None,
|
||||
**kwargs,
|
||||
):
|
||||
self._callback_id = None
|
||||
self._is_valid_input = False
|
||||
self._accent_colors = get_accent_colors()
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
super().__init__(client=client, config=config, gui_id=gui_id, **kwargs)
|
||||
QLineEdit.__init__(self, parent=parent)
|
||||
self.completer = QCompleter(self)
|
||||
self.setCompleter(self.completer)
|
||||
|
||||
@@ -38,8 +38,9 @@ class SignalComboBox(DeviceSignalInputBase, QComboBox):
|
||||
signal_filter: str | list[str] | None = None,
|
||||
default: str | None = None,
|
||||
arg_name: str | None = None,
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
super().__init__(client=client, config=config, gui_id=gui_id, **kwargs)
|
||||
QComboBox.__init__(self, parent=parent)
|
||||
if arg_name is not None:
|
||||
self.config.arg_name = arg_name
|
||||
|
||||
@@ -39,9 +39,10 @@ class SignalLineEdit(DeviceSignalInputBase, QLineEdit):
|
||||
signal_filter: str | list[str] | None = None,
|
||||
default: str | None = None,
|
||||
arg_name: str | None = None,
|
||||
**kwargs,
|
||||
):
|
||||
self._is_valid_input = False
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
super().__init__(client=client, config=config, gui_id=gui_id, **kwargs)
|
||||
QLineEdit.__init__(self, parent=parent)
|
||||
self._accent_colors = get_accent_colors()
|
||||
self.completer = QCompleter(self)
|
||||
|
||||
@@ -58,13 +58,14 @@ class ScanControl(BECWidget, QWidget):
|
||||
gui_id: str | None = None,
|
||||
allowed_scans: list | None = None,
|
||||
default_scan: str | None = None,
|
||||
**kwargs,
|
||||
):
|
||||
|
||||
if config is None:
|
||||
config = ScanControlConfig(
|
||||
widget_class=self.__class__.__name__, allowed_scans=allowed_scans
|
||||
)
|
||||
super().__init__(client=client, gui_id=gui_id, config=config)
|
||||
super().__init__(client=client, gui_id=gui_id, config=config, **kwargs)
|
||||
QWidget.__init__(self, parent=parent)
|
||||
|
||||
self._hide_add_remove_buttons = False
|
||||
|
||||
@@ -37,9 +37,14 @@ class DapComboBox(BECWidget, QWidget):
|
||||
fit_model_updated = Signal(str)
|
||||
|
||||
def __init__(
|
||||
self, parent=None, client=None, gui_id: str | None = None, default_fit: str | None = None
|
||||
self,
|
||||
parent=None,
|
||||
client=None,
|
||||
gui_id: str | None = None,
|
||||
default_fit: str | None = None,
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(client=client, gui_id=gui_id)
|
||||
super().__init__(client=client, gui_id=gui_id, **kwargs)
|
||||
QWidget.__init__(self, parent=parent)
|
||||
self.layout = QVBoxLayout(self)
|
||||
self.fit_model_combobox = QComboBox(self)
|
||||
|
||||
@@ -30,6 +30,7 @@ class LMFitDialog(BECWidget, QWidget):
|
||||
target_widget=None,
|
||||
gui_id: str | None = None,
|
||||
ui_file="lmfit_dialog_vertical.ui",
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
Initialises the LMFitDialog widget.
|
||||
@@ -42,7 +43,7 @@ class LMFitDialog(BECWidget, QWidget):
|
||||
gui_id (str): GUI ID.
|
||||
ui_file (str): The UI file to be loaded.
|
||||
"""
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
super().__init__(client=client, config=config, gui_id=gui_id, **kwargs)
|
||||
QWidget.__init__(self, parent=parent)
|
||||
self.setProperty("skip_settings", True)
|
||||
self.setObjectName("LMFitDialog")
|
||||
|
||||
@@ -49,8 +49,9 @@ class ScanMetadata(BECWidget, QWidget):
|
||||
client=None,
|
||||
scan_name: str | None = None,
|
||||
initial_extras: list[list[str]] | None = None,
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(client=client)
|
||||
super().__init__(client=client, **kwargs)
|
||||
QWidget.__init__(self, parent=parent)
|
||||
|
||||
self.set_schema(scan_name)
|
||||
|
||||
@@ -42,14 +42,14 @@ class TextBox(BECWidget, QWidget):
|
||||
USER_ACCESS = ["set_plain_text", "set_html_text"]
|
||||
ICON_NAME = "chat"
|
||||
|
||||
def __init__(self, parent=None, client=None, config=None, gui_id=None):
|
||||
def __init__(self, parent=None, client=None, config=None, gui_id=None, **kwargs):
|
||||
if config is None:
|
||||
config = TextBoxConfig(widget_class=self.__class__.__name__)
|
||||
else:
|
||||
if isinstance(config, dict):
|
||||
config = TextBoxConfig(**config)
|
||||
self.config = config
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
super().__init__(client=client, config=config, gui_id=gui_id, **kwargs)
|
||||
QWidget.__init__(self, parent)
|
||||
self.layout = QVBoxLayout(self)
|
||||
self.text_box_text_edit = QTextEdit(parent=self)
|
||||
|
||||
@@ -45,12 +45,12 @@ class VSCodeEditor(WebsiteWidget):
|
||||
USER_ACCESS = []
|
||||
ICON_NAME = "developer_mode_tv"
|
||||
|
||||
def __init__(self, parent=None, config=None, client=None, gui_id=None):
|
||||
def __init__(self, parent=None, config=None, client=None, gui_id=None, **kwargs):
|
||||
|
||||
self.process = None
|
||||
self.port = get_free_port()
|
||||
self._url = f"http://{self.host}:{self.port}?tkn={self.token}"
|
||||
super().__init__(parent=parent, config=config, client=client, gui_id=gui_id)
|
||||
super().__init__(parent=parent, config=config, client=client, gui_id=gui_id, **kwargs)
|
||||
self.start_server()
|
||||
self.bec_dispatcher.connect_slot(self.on_vscode_event, f"vscode-events/{self.gui_id}")
|
||||
|
||||
|
||||
@@ -23,8 +23,10 @@ class WebsiteWidget(BECWidget, QWidget):
|
||||
ICON_NAME = "travel_explore"
|
||||
USER_ACCESS = ["set_url", "get_url", "reload", "back", "forward"]
|
||||
|
||||
def __init__(self, parent=None, url: str = None, config=None, client=None, gui_id=None):
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
def __init__(
|
||||
self, parent=None, url: str = None, config=None, client=None, gui_id=None, **kwargs
|
||||
):
|
||||
super().__init__(client=client, config=config, gui_id=gui_id, **kwargs)
|
||||
QWidget.__init__(self, parent=parent)
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
@@ -54,13 +54,14 @@ class BECImageWidget(BECWidget, QWidget):
|
||||
config: ImageConfig | dict = None,
|
||||
client=None,
|
||||
gui_id: str | None = None,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
if config is None:
|
||||
config = ImageConfig(widget_class=self.__class__.__name__)
|
||||
else:
|
||||
if isinstance(config, dict):
|
||||
config = ImageConfig(**config)
|
||||
super().__init__(client=client, gui_id=gui_id)
|
||||
super().__init__(client=client, gui_id=gui_id, **kwargs)
|
||||
QWidget.__init__(self, parent)
|
||||
self.layout = QVBoxLayout(self)
|
||||
self.layout.setSpacing(0)
|
||||
|
||||
@@ -35,13 +35,14 @@ class BECMotorMapWidget(BECWidget, QWidget):
|
||||
config: MotorMapConfig | None = None,
|
||||
client=None,
|
||||
gui_id: str | None = None,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
if config is None:
|
||||
config = MotorMapConfig(widget_class=self.__class__.__name__)
|
||||
else:
|
||||
if isinstance(config, dict):
|
||||
config = MotorMapConfig(**config)
|
||||
super().__init__(client=client, gui_id=gui_id)
|
||||
super().__init__(client=client, gui_id=gui_id, **kwargs)
|
||||
QWidget.__init__(self, parent)
|
||||
|
||||
self.layout = QVBoxLayout(self)
|
||||
|
||||
@@ -63,13 +63,14 @@ class BECMultiWaveformWidget(BECWidget, QWidget):
|
||||
config: BECMultiWaveformConfig | dict = None,
|
||||
client=None,
|
||||
gui_id: str | None = None,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
if config is None:
|
||||
config = BECMultiWaveformConfig(widget_class=self.__class__.__name__)
|
||||
else:
|
||||
if isinstance(config, dict):
|
||||
config = BECMultiWaveformConfig(**config)
|
||||
super().__init__(client=client, gui_id=gui_id)
|
||||
super().__init__(client=client, gui_id=gui_id, **kwargs)
|
||||
QWidget.__init__(self, parent)
|
||||
|
||||
self.layout = QVBoxLayout(self)
|
||||
|
||||
@@ -86,13 +86,14 @@ class BECWaveformWidget(BECWidget, QWidget):
|
||||
config: Waveform1DConfig | dict = None,
|
||||
client=None,
|
||||
gui_id: str | None = None,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
if config is None:
|
||||
config = Waveform1DConfig(widget_class=self.__class__.__name__)
|
||||
else:
|
||||
if isinstance(config, dict):
|
||||
config = Waveform1DConfig(**config)
|
||||
super().__init__(client=client, gui_id=gui_id)
|
||||
super().__init__(client=client, gui_id=gui_id, **kwargs)
|
||||
QWidget.__init__(self, parent)
|
||||
|
||||
self.layout = QVBoxLayout(self)
|
||||
|
||||
@@ -69,10 +69,11 @@ class PlotBase(BECWidget, QWidget):
|
||||
client=None,
|
||||
gui_id: str | None = None,
|
||||
popups: bool = False,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
if config is None:
|
||||
config = ConnectionConfig(widget_class=self.__class__.__name__)
|
||||
super().__init__(client=client, gui_id=gui_id, config=config)
|
||||
super().__init__(client=client, gui_id=gui_id, config=config, **kwargs)
|
||||
QWidget.__init__(self, parent=parent)
|
||||
|
||||
# For PropertyManager identification
|
||||
|
||||
@@ -24,8 +24,8 @@ class BECProgressBar(BECWidget, QWidget):
|
||||
]
|
||||
ICON_NAME = "page_control"
|
||||
|
||||
def __init__(self, parent=None, client=None, config=None, gui_id=None):
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
def __init__(self, parent=None, client=None, config=None, gui_id=None, **kwargs):
|
||||
super().__init__(client=client, config=config, gui_id=gui_id, **kwargs)
|
||||
QWidget.__init__(self, parent=parent)
|
||||
|
||||
accent_colors = get_accent_colors()
|
||||
|
||||
@@ -99,6 +99,7 @@ class Ring(BECConnector):
|
||||
config: RingConfig | dict | None = None,
|
||||
client=None,
|
||||
gui_id: Optional[str] = None,
|
||||
**kwargs,
|
||||
):
|
||||
if config is None:
|
||||
config = RingConfig(widget_class=self.__class__.__name__)
|
||||
@@ -107,7 +108,7 @@ class Ring(BECConnector):
|
||||
if isinstance(config, dict):
|
||||
config = RingConfig(**config)
|
||||
self.config = config
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
super().__init__(client=client, config=config, gui_id=gui_id, **kwargs)
|
||||
|
||||
self.parent_progress_widget = parent_progress_widget
|
||||
self.color = None
|
||||
|
||||
@@ -101,6 +101,7 @@ class RingProgressBar(BECWidget, QWidget):
|
||||
client=None,
|
||||
gui_id: str | None = None,
|
||||
num_bars: int | None = None,
|
||||
**kwargs,
|
||||
):
|
||||
if config is None:
|
||||
config = RingProgressBarConfig(widget_class=self.__class__.__name__)
|
||||
@@ -109,7 +110,7 @@ class RingProgressBar(BECWidget, QWidget):
|
||||
if isinstance(config, dict):
|
||||
config = RingProgressBarConfig(**config, widget_class=self.__class__.__name__)
|
||||
self.config = config
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
super().__init__(client=client, config=config, gui_id=gui_id, **kwargs)
|
||||
QWidget.__init__(self, parent=parent)
|
||||
|
||||
self.get_bec_shortcuts()
|
||||
|
||||
@@ -42,8 +42,9 @@ class BECQueue(BECWidget, CompactPopupWidget):
|
||||
config: ConnectionConfig = None,
|
||||
gui_id: str = None,
|
||||
refresh_upon_start: bool = True,
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(client, config, gui_id)
|
||||
super().__init__(client=client, config=config, gui_id=gui_id, **kwargs)
|
||||
CompactPopupWidget.__init__(self, parent=parent, layout=QVBoxLayout)
|
||||
self.layout.setSpacing(0)
|
||||
self.layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
@@ -87,8 +87,9 @@ class BECStatusBox(BECWidget, CompactPopupWidget):
|
||||
client: BECClient = None,
|
||||
bec_service_status_mixin: BECServiceStatusMixin = None,
|
||||
gui_id: str = None,
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(client=client, gui_id=gui_id)
|
||||
super().__init__(client=client, gui_id=gui_id, **kwargs)
|
||||
CompactPopupWidget.__init__(self, parent=parent, layout=QHBoxLayout)
|
||||
|
||||
self.box_name = box_name
|
||||
|
||||
@@ -23,8 +23,9 @@ class DeviceBrowser(BECWidget, QWidget):
|
||||
config=None,
|
||||
client=None,
|
||||
gui_id: Optional[str] = None,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
super().__init__(client=client, config=config, gui_id=gui_id, **kwargs)
|
||||
QWidget.__init__(self, parent)
|
||||
|
||||
self.get_bec_shortcuts()
|
||||
|
||||
@@ -26,10 +26,11 @@ class BECSpinBox(BECWidget, QDoubleSpinBox):
|
||||
config: ConnectionConfig | None = None,
|
||||
client=None,
|
||||
gui_id: str | None = None,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
if config is None:
|
||||
config = ConnectionConfig(widget_class=self.__class__.__name__)
|
||||
super().__init__(client=client, gui_id=gui_id, config=config)
|
||||
super().__init__(client=client, gui_id=gui_id, config=config, **kwargs)
|
||||
QDoubleSpinBox.__init__(self, parent=parent)
|
||||
|
||||
self.setObjectName("BECSpinBox")
|
||||
|
||||
@@ -12,8 +12,8 @@ class BECColorMapWidget(BECWidget, QWidget):
|
||||
USER_ACCESS = ["colormap"]
|
||||
PLUGIN = True
|
||||
|
||||
def __init__(self, parent=None, cmap: str = "magma"):
|
||||
super().__init__()
|
||||
def __init__(self, parent=None, cmap: str = "magma", **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
QWidget.__init__(self, parent=parent)
|
||||
|
||||
# Create the ColorMapButton
|
||||
|
||||
@@ -20,8 +20,9 @@ class DarkModeButton(BECWidget, QWidget):
|
||||
client=None,
|
||||
gui_id: str | None = None,
|
||||
toolbar: bool = False,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
super().__init__(client=client, gui_id=gui_id, theme_update=True)
|
||||
super().__init__(client=client, gui_id=gui_id, theme_update=True, **kwargs)
|
||||
QWidget.__init__(self, parent)
|
||||
|
||||
self._dark_mode_enabled = False
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "bec_widgets"
|
||||
version = "1.24.4"
|
||||
version = "1.25.0"
|
||||
description = "BEC Widgets"
|
||||
requires-python = ">=3.10"
|
||||
classifiers = [
|
||||
@@ -24,6 +24,7 @@ dependencies = [
|
||||
"pyte", # needed for vt100 console
|
||||
"qtconsole~=5.5, >=5.5.1", # needed for jupyter console
|
||||
"qtpy~=2.4",
|
||||
"cryptography~=44.0",
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -81,5 +81,10 @@ def test_client_utils_passes_client_config_to_server(bec_dispatcher):
|
||||
wait=False
|
||||
) # the started event will not be set, wait=True would block forever
|
||||
mock_start_plot.assert_called_once_with(
|
||||
"gui_id", BECGuiClient, mixin._client._service_config.config, logger=mock.ANY
|
||||
"gui_id",
|
||||
BECGuiClient,
|
||||
mixin._client._service_config.config,
|
||||
logger=mock.ANY,
|
||||
token=mock.ANY,
|
||||
acl_data=mock.ANY,
|
||||
)
|
||||
|
||||
@@ -21,7 +21,9 @@ def test_rpc_server_start_server_without_service_config(mocked_cli_server):
|
||||
mock_server, mock_config, _ = mocked_cli_server
|
||||
|
||||
_start_server("gui_id", BECFigure, None)
|
||||
mock_server.assert_called_once_with(gui_id="gui_id", config=mock_config(), gui_class=BECFigure)
|
||||
mock_server.assert_called_once_with(
|
||||
gui_id="gui_id", config=mock_config(), gui_class=BECFigure, token=None
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -38,4 +40,6 @@ def test_rpc_server_start_server_with_service_config(mocked_cli_server, config,
|
||||
mock_server, mock_config, _ = mocked_cli_server
|
||||
config = mock_config(**call_config)
|
||||
_start_server("gui_id", BECFigure, config)
|
||||
mock_server.assert_called_once_with(gui_id="gui_id", config=config, gui_class=BECFigure)
|
||||
mock_server.assert_called_once_with(
|
||||
gui_id="gui_id", config=config, gui_class=BECFigure, token=None
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user