0
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2025-07-14 11:41:49 +02:00

feat(widgets): added device box with spinner

This commit is contained in:
2024-07-04 18:37:50 +02:00
committed by wyzula_j
parent 903ce7d46b
commit 1b017edfad
23 changed files with 946 additions and 1 deletions

View File

@ -49,11 +49,19 @@ class FakePositioner(FakeDevice):
self.read_value = read_value
self.name = name
@property
def precision(self):
return 3
def set_read_value(self, value):
self.read_value = value
def read(self):
return {self.name: {"value": self.read_value}}
return {
self.name: {"value": self.read_value},
f"{self.name}_setpoint": {"value": self.read_value},
f"{self.name}_motor_is_moving": {"value": 0},
}
def set_limits(self, limits):
self.limits = limits

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,98 @@
from unittest import mock
import pytest
from bec_lib.endpoints import MessageEndpoints
from bec_lib.messages import ScanQueueMessage
from qtpy.QtGui import QValidator
from bec_widgets.widgets.device_box.device_box import DeviceBox
from .client_mocks import mocked_client
@pytest.fixture
def device_box(qtbot, mocked_client):
with mock.patch("bec_widgets.widgets.device_box.device_box.uuid.uuid4") as mock_uuid:
mock_uuid.return_value = "fake_uuid"
db = DeviceBox(device="samx", client=mocked_client)
qtbot.addWidget(db)
yield db
def test_device_box(device_box):
assert device_box.device == "samx"
data = device_box.dev["samx"].read()
setpoint_text = device_box.ui.setpoint.text()
# check that the setpoint is taken correctly after init
assert float(setpoint_text) == data["samx_setpoint"]["value"]
# check that the precision is taken correctly after init
precision = device_box.dev["samx"].precision
assert setpoint_text == f"{data['samx_setpoint']['value']:.{precision}f}"
# check that the step size is set according to the device precision
assert device_box.ui.step_size.value() == 10**-precision * 10
def test_device_box_update_limits(device_box):
device_box._limits = None
device_box.update_limits([0, 10])
assert device_box._limits == [0, 10]
assert device_box.setpoint_validator.bottom() == 0
assert device_box.setpoint_validator.top() == 10
assert device_box.setpoint_validator.validate("100", 0) == (
QValidator.State.Intermediate,
"100",
0,
)
device_box.update_limits(None)
assert device_box._limits is None
assert device_box.setpoint_validator.validate("100", 0) == (
QValidator.State.Acceptable,
"100",
0,
)
def test_device_box_on_stop(device_box):
with mock.patch.object(device_box.client.connector, "send") as mock_send:
device_box.on_stop()
params = {"device": "samx", "rpc_id": "fake_uuid", "func": "stop", "args": [], "kwargs": {}}
msg = ScanQueueMessage(
scan_type="device_rpc",
parameter=params,
queue="emergency",
metadata={"RID": "fake_uuid", "response": False},
)
mock_send.assert_called_once_with(MessageEndpoints.scan_queue_request(), msg)
def test_device_box_setpoint_change(device_box):
with mock.patch.object(device_box.dev["samx"], "move") as mock_move:
device_box.ui.setpoint.setText("100")
device_box.on_setpoint_change()
mock_move.assert_called_once_with(100, relative=False)
def test_device_box_on_tweak_right(device_box):
with mock.patch.object(device_box.dev["samx"], "move") as mock_move:
device_box.ui.step_size.setValue(0.1)
device_box.on_tweak_right()
mock_move.assert_called_once_with(0.1, relative=True)
def test_device_box_on_tweak_left(device_box):
with mock.patch.object(device_box.dev["samx"], "move") as mock_move:
device_box.ui.step_size.setValue(0.1)
device_box.on_tweak_left()
mock_move.assert_called_once_with(-0.1, relative=True)
def test_device_box_setpoint_out_of_range(device_box):
device_box.update_limits([0, 10])
device_box.ui.setpoint.setText("100")
device_box.on_setpoint_change()
assert device_box.ui.setpoint.text() == "100"
assert device_box.ui.setpoint.hasAcceptableInput() == False

View File

@ -0,0 +1,92 @@
import os
import sys
import pytest
import qdarktheme
from PIL import Image, ImageChops
from qtpy.QtGui import QPixmap
from bec_widgets.widgets.spinner.spinner import SpinnerWidget
@pytest.fixture
def spinner_widget(qtbot):
qdarktheme.setup_theme("light")
spinner = SpinnerWidget()
qtbot.addWidget(spinner)
qtbot.waitExposed(spinner)
yield spinner
def save_pixmap(widget, filename):
pixmap = QPixmap(widget.size())
widget.render(pixmap)
pixmap.save(str(filename))
return pixmap
def compare_images(image1_path: str, reference_image_path: str):
image1 = Image.open(image1_path)
image2 = Image.open(reference_image_path)
if image1.size != image2.size:
raise ValueError("Image size has changed")
diff = ImageChops.difference(image1, image2)
if diff.getbbox():
# copy image1 to the reference directory to upload as artifact
output_dir = os.path.join(os.path.dirname(__file__), "reference_failures")
os.makedirs(output_dir, exist_ok=True)
image_name = os.path.join(output_dir, os.path.basename(image1_path))
image1.save(image_name)
print(f"Image saved to {image_name}")
raise ValueError("Images are different")
def test_spinner_widget_paint_event(spinner_widget, qtbot):
spinner_widget.paintEvent(None)
def snap_and_compare(widget, tmpdir, suffix=""):
os_suffix = sys.platform
name = (
f"{widget.__class__.__name__}_{suffix}_{os_suffix}.png"
if suffix
else f"{widget.__class__.__name__}_{os_suffix}.png"
)
# Save the widget to a pixmap
test_image_path = str(tmpdir / name)
pixmap = QPixmap(widget.size())
widget.render(pixmap)
pixmap.save(test_image_path)
try:
references_path = os.path.join(os.path.dirname(__file__), "references")
reference_image_path = os.path.join(references_path, name)
if not os.path.exists(reference_image_path):
raise ValueError(f"Reference image not found: {reference_image_path}")
compare_images(test_image_path, reference_image_path)
except ValueError:
image = Image.open(test_image_path)
output_dir = os.path.join(os.path.dirname(__file__), "reference_failures")
os.makedirs(output_dir, exist_ok=True)
image_name = os.path.join(output_dir, name)
image.save(image_name)
print(f"Image saved to {image_name}")
raise
def test_spinner_widget_rendered(spinner_widget, qtbot, tmpdir):
spinner_widget.update()
qtbot.wait(200)
snap_and_compare(spinner_widget, tmpdir, suffix="")
spinner_widget._started = True
spinner_widget.update()
qtbot.wait(200)
snap_and_compare(spinner_widget, tmpdir, suffix="started")