mirror of
https://github.com/bec-project/bec_widgets.git
synced 2026-04-16 05:30:54 +02:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb4c30ad80 | ||
| 3fd09fceef | |||
| 8eb8225a7f | |||
| 491d04467c | |||
|
|
3bcff75107 | ||
| 608590c542 |
24
CHANGELOG.md
24
CHANGELOG.md
@@ -1,6 +1,30 @@
|
||||
# CHANGELOG
|
||||
|
||||
|
||||
## v2.30.2 (2025-07-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Factor out device name function and add test
|
||||
([`8eb8225`](https://github.com/bec-project/bec_widgets/commit/8eb8225a7f56014d6093aa142b3a5d071837982e))
|
||||
|
||||
- **rpc_base**: Rpc_call wrapper passes full_name for Devices indeed of name
|
||||
([`491d044`](https://github.com/bec-project/bec_widgets/commit/491d04467c8ce4e116d61e614895d1dcc6b4b201))
|
||||
|
||||
### Testing
|
||||
|
||||
- **test_plotting_framework_e2e**: Added test for waveform with passing device from dev container
|
||||
([`3fd09fc`](https://github.com/bec-project/bec_widgets/commit/3fd09fceef2ffa7e7c3eee20176304bafb00d0db))
|
||||
|
||||
|
||||
## v2.30.1 (2025-07-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Ignore KeyError in SignalLabel
|
||||
([`608590c`](https://github.com/bec-project/bec_widgets/commit/608590c5421368d5bba0e4b0f5187d90cac323be))
|
||||
|
||||
|
||||
## v2.30.0 (2025-07-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -7,6 +7,7 @@ from functools import wraps
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
|
||||
from bec_lib.client import BECClient
|
||||
from bec_lib.device import DeviceBaseWithConfig
|
||||
from bec_lib.endpoints import MessageEndpoints
|
||||
from bec_lib.utils.import_utils import lazy_import, lazy_import_from
|
||||
|
||||
@@ -24,6 +25,20 @@ else:
|
||||
# pylint: disable=protected-access
|
||||
|
||||
|
||||
def _name_arg(arg):
|
||||
if isinstance(arg, DeviceBaseWithConfig):
|
||||
# if dev.<device> is passed to GUI, it passes full_name
|
||||
if hasattr(arg, "full_name"):
|
||||
return arg.full_name
|
||||
elif hasattr(arg, "name"):
|
||||
return arg.name
|
||||
return arg
|
||||
|
||||
|
||||
def _transform_args_kwargs(args, kwargs) -> tuple[tuple, dict]:
|
||||
return tuple(_name_arg(arg) for arg in args), {k: _name_arg(v) for k, v in kwargs.items()}
|
||||
|
||||
|
||||
def rpc_call(func):
|
||||
"""
|
||||
A decorator for calling a function on the server.
|
||||
@@ -47,15 +62,7 @@ def rpc_call(func):
|
||||
return None # func(*args, **kwargs)
|
||||
caller_frame = caller_frame.f_back
|
||||
|
||||
out = []
|
||||
for arg in args:
|
||||
if hasattr(arg, "name"):
|
||||
arg = arg.name
|
||||
out.append(arg)
|
||||
args = tuple(out)
|
||||
for key, val in kwargs.items():
|
||||
if hasattr(val, "name"):
|
||||
kwargs[key] = val.name
|
||||
args, kwargs = _transform_args_kwargs(args, kwargs)
|
||||
if not self._root._gui_is_alive():
|
||||
raise RuntimeError("GUI is not alive")
|
||||
return self._run_rpc(func.__name__, *args, **kwargs)
|
||||
|
||||
@@ -28,6 +28,10 @@ class EntryValidator:
|
||||
if not available_entries:
|
||||
available_entries = [name]
|
||||
|
||||
# edge case for if name is passed instead of full_name, should not happen
|
||||
if entry in signals_dict:
|
||||
entry = signals_dict[entry].get("obj_name", entry)
|
||||
|
||||
if entry is None or entry == "":
|
||||
entry = next(iter(device._hints), name) if hasattr(device, "_hints") else name
|
||||
if entry not in available_entries:
|
||||
|
||||
@@ -300,7 +300,10 @@ class SignalLabel(BECWidget, QWidget):
|
||||
|
||||
def _signal_key_and_info(self) -> tuple[str, dict]:
|
||||
if isinstance(self._device_obj, Device):
|
||||
signal_info = self._device_obj._info["signals"][self._signal]
|
||||
try:
|
||||
signal_info = self._device_obj._info["signals"][self._signal]
|
||||
except KeyError:
|
||||
return "", {}
|
||||
if signal_info["kind_str"] == Kind.hinted.name:
|
||||
return signal_info["obj_name"], signal_info
|
||||
else:
|
||||
@@ -309,15 +312,6 @@ class SignalLabel(BECWidget, QWidget):
|
||||
return self._device, self._device_obj._info["describe_configuration"]
|
||||
return "", {}
|
||||
|
||||
# if self.dev[self._device]._info["signals"] == {}:
|
||||
# return self._signal or self._device
|
||||
# signal_info = self.dev[self._device]._info["signals"][self._signal]
|
||||
# return (
|
||||
# signal_info["obj_name"]
|
||||
# if signal_info["kind_str"] == Kind.hinted.name
|
||||
# else (self._signal or self._device)
|
||||
# )
|
||||
|
||||
@SafeProperty(str)
|
||||
def device(self) -> str:
|
||||
"""The device from which to select a signal"""
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "bec_widgets"
|
||||
version = "2.30.0"
|
||||
version = "2.30.2"
|
||||
description = "BEC Widgets"
|
||||
requires-python = ">=3.10"
|
||||
classifiers = [
|
||||
|
||||
@@ -254,3 +254,35 @@ def test_dap_rpc(qtbot, bec_client_lib, connected_client_gui_obj):
|
||||
res.wait()
|
||||
|
||||
qtbot.waitUntil(wait_for_fit, timeout=10000)
|
||||
|
||||
|
||||
def test_waveform_passing_device(qtbot, bec_client_lib, connected_client_gui_obj):
|
||||
gui = connected_client_gui_obj
|
||||
client = bec_client_lib
|
||||
dev = client.device_manager.devices
|
||||
scans = client.scans
|
||||
|
||||
dock = gui.bec
|
||||
wf = dock.new("wf_dock").new("Waveform")
|
||||
c1 = wf.plot(
|
||||
y_name=dev.samx, y_entry=dev.samx.setpoint
|
||||
) # using setpoint to not use readback signal
|
||||
|
||||
assert c1.object_name == "samx_samx_setpoint"
|
||||
|
||||
status = scans.line_scan(dev.samx, -5, 5, steps=5, exp_time=0.05, relative=False)
|
||||
status.wait()
|
||||
|
||||
# Wait for the scan to finish and the data to be available in history
|
||||
# Wait until scan_id is in history
|
||||
def _wait_for_scan_in_history():
|
||||
if len(client.history) == 0:
|
||||
return False
|
||||
# Once items appear in storage, the last one hast to be the one we just scanned
|
||||
return client.history[-1].metadata.bec["scan_id"] == status.scan.scan_id
|
||||
|
||||
qtbot.waitUntil(_wait_for_scan_in_history, timeout=10000)
|
||||
last_scan_data = client.history[-1]
|
||||
# check plotted data
|
||||
x_data, y_data = c1.get_data()
|
||||
assert np.array_equal(y_data, last_scan_data.devices.samx.samx_setpoint.read().get("value"))
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import pytest
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from bec_widgets.cli.rpc.rpc_base import DeletedWidgetError, RPCBase, RPCReference
|
||||
import pytest
|
||||
from bec_lib.device import DeviceBaseWithConfig, Signal
|
||||
|
||||
from bec_widgets.cli.rpc.rpc_base import (
|
||||
DeletedWidgetError,
|
||||
RPCBase,
|
||||
RPCReference,
|
||||
_transform_args_kwargs,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -26,3 +34,20 @@ def test_rpc_base(rpc_base):
|
||||
|
||||
with pytest.raises(DeletedWidgetError):
|
||||
ref._root # Object no longer referenced in registry
|
||||
|
||||
|
||||
def test_transform_args_kwargs():
|
||||
device_mock = MagicMock(spec=DeviceBaseWithConfig)
|
||||
device_mock.full_name = "full name"
|
||||
fallthrough_device_mock = MagicMock()
|
||||
fallthrough_device_mock.name = "short name"
|
||||
string_arg = "string_arg"
|
||||
signal_mock = MagicMock(spec=Signal)
|
||||
signal_mock.full_name = "full name"
|
||||
|
||||
args, kwargs = _transform_args_kwargs(
|
||||
(device_mock, fallthrough_device_mock, string_arg, signal_mock),
|
||||
{"a": device_mock, "b": fallthrough_device_mock, "c": string_arg, "d": signal_mock},
|
||||
)
|
||||
assert args == ("full name", "short name", "string_arg", "full name")
|
||||
assert kwargs == {"a": "full name", "b": "short name", "c": "string_arg", "d": "full name"}
|
||||
|
||||
Reference in New Issue
Block a user