0
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2025-07-13 19:21:50 +02:00

Merge branch 'master' into frontend

This commit is contained in:
wyzula-jan
2023-08-11 10:31:24 +02:00
8 changed files with 420 additions and 192 deletions

View File

@ -2,6 +2,46 @@
<!--next-version-placeholder-->
## v0.5.0 (2023-08-11)
### Feature
* Add generic connect function for slots ([`6a3df34`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/6a3df34cdfbec2434153362ded630305e5dc5e28))
* Add possibility to provide service config ([`8c9a9c9`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/8c9a9c93535ee77c0622b483a3157af367ebce1f))
### Fix
* Dispatcher argparse and scan_plot tests ([`67f619e`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/67f619ee897e0040c6310e67d69fbb2e0685293d))
* Gui event removing bugs ([`a9dd191`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/a9dd191629295ca476e2f9a1b9944ff355216583))
## v0.4.0 (2023-08-11)
### Feature
* Cursor universal for 1D and 2D ([`f75554b`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/f75554bd7b072207847956a8720b9a62c20ba2c8))
* Added qt_utils package with general Crosshair function ([`5353fed`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/5353fed7bfe1819819fa3348ec93d2d0ba540628))
* 2D plot updating ([`d32088b`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/d32088b643a4d0613c32fb464a0a55a3b6b684d6))
* Metadata available on_dap_update ([`18b5d46`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/18b5d46678619a972815532629ce96c121f5fcc9))
* Plotting from streamer ([`bb806c1`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/bb806c149dee88023ecb647b523cbd5189ea9001))
* Added Legend to plot ([`0feca4b`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/0feca4b1578820ec1f5f3ead3073e4d45c23798b))
* Cursor coordinate as a QTable ([`a999f76`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/a999f7669a12910ad66e10a6d2e75197b2dce1c2))
* Changed from PlotItem to GraphicsLayoutWidget, added LabelItem ([`075cc79`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/075cc79d6fa011803cf4a06fbff8faa951c1b59f))
* Add display_ui_file.py ([`91d8ffa`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/91d8ffacffcbeebdf7623caf62e07244c4dcee16))
* Add disconnect_dap_slot ([`1325704`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/1325704750ebab897e3dcae80c9d455bfbbf886f))
* Inherit from GraphicsView for consistency with 2D plot ([`d8c101c`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/d8c101cdd7f960a152a1f318911cac6eecf6bad4))
* Add BECScanPlot2D ([`67905e8`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/67905e896c81383f57c268db544b3314104bda38))
* Emit the full bec message to slots ([`1bb3020`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/1bb30207038f3a54c0e96dbbbcd1ea7f6c70eca2))
### Fix
* Q selection for gui_event signal ([`0bf452a`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/0bf452ad1b7d9ad941e2ef4b8d61ec4ed5266415))
* Fixed logic in data subscription ([`c2d469b`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/c2d469b4543fcf237b274399b83969cc2213b61b))
* Scan_plot to accept metadata from dap signal ([`7bec0b5`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/7bec0b5e6c1663670f8fcc2fc6aa6c8b6df28b61))
* Plotting latest 1d curves ([`378be81`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/378be81bf6dd5e9239f8f1fb908cafc97161c79d))
* Testing the data structure of plotting ([`4fb0a3b`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/4fb0a3b058957f5b37227ff7c8e9bdf5259a1cde))
* Fix examples when run directly as a script ([`cd11ee5`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/cd11ee51c1c725255e748a32b89a74487e84a631))
* Module paths ([`e7f644c`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/e7f644c5079a8665d7d872eb0b27ed7da6cbd078))
## v0.3.0 (2023-07-19)
### Feature

View File

@ -1,20 +1,23 @@
import os
import warnings
import time
from typing import Any
import threading
import time
import warnings
from typing import Any
import numpy as np
import pyqtgraph
import pyqtgraph as pg
from bec_lib.core import BECMessage
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import QTableWidgetItem, QCheckBox
from bec_lib import BECClient
from PyQt5.QtWidgets import QCheckBox, QTableWidgetItem
from pyqtgraph import mkBrush, mkColor, mkPen
from pyqtgraph.Qt import QtCore, QtWidgets, uic
from pyqtgraph.Qt.QtCore import pyqtSignal
from bec_lib.core import BECMessage
from bec_widgets.bec_dispatcher import bec_dispatcher
from bec_lib.core.redis_connector import MessageObject, RedisConnector
client = bec_dispatcher.client
class BasicPlot(QtWidgets.QWidget):
@ -35,7 +38,7 @@ class BasicPlot(QtWidgets.QWidget):
pg.setConfigOption("background", "w")
pg.setConfigOption("foreground", "k")
current_path = os.path.dirname(__file__)
uic.loadUi(os.path.join(current_path, "line_plot.ui"), self)
uic.loadUi(os.path.join(current_path, "basic_plot.ui"), self)
# Set splitter distribution of widgets
self.splitter.setSizes([3, 1])
@ -44,6 +47,7 @@ class BasicPlot(QtWidgets.QWidget):
self.title = ""
self.label_bottom = ""
self.label_left = ""
self.producer = RedisConnector(["localhost:6379"]).producer()
self.scan_motors = []
self.y_value_list = y_value_list
@ -158,6 +162,14 @@ class BasicPlot(QtWidgets.QWidget):
"""For testing purpose now, get roi region and print it to self.label as tuple"""
region = self.roi_selector.getRegion()
self.label.setText(f"x = {(10**region[0]):.4f}, y ={(10**region[1]):.4f}")
return_dict = {
"horiz_roi": [
np.where(self.plotter_data_x[0] > 10 ** region[0])[0][0],
np.where(self.plotter_data_x[0] < 10 ** region[1])[0][-1],
]
}
msg = BECMessage.DeviceMessage(signals=return_dict).dumps()
self.producer.set_and_publish("px_stream/gui_event", msg=msg)
self.roi_signal.emit(region)
def add_text_items(self): # TODO probably can be removed
@ -386,14 +398,14 @@ class BasicPlot(QtWidgets.QWidget):
data = [BECMessage.DeviceMessage.loads(msg) for msg in msgs]
if not data:
continue
self.plotter_data_y = [
np.sum(
np.sum(data[-1].content["signals"]["data"] * self._current_norm, axis=1)
/ np.sum(self._current_norm, axis=0),
axis=0,
).squeeze()
]
with np.errstate(divide="ignore", invalid="ignore"):
self.plotter_data_y = [
np.sum(
np.sum(data[-1].content["signals"]["data"] * self._current_norm, axis=1)
/ np.sum(self._current_norm, axis=0),
axis=0,
).squeeze()
]
self.update_signal.emit()
@ -418,9 +430,9 @@ class BasicPlot(QtWidgets.QWidget):
if __name__ == "__main__":
import argparse
from bec_widgets.bec_dispatcher import bec_dispatcher
from bec_widgets import ctrl_c
from bec_widgets.bec_dispatcher import bec_dispatcher
parser = argparse.ArgumentParser()
parser.add_argument(

77
bec_widgets/basic_plot.ui Normal file
View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>845</width>
<height>635</height>
</rect>
</property>
<property name="windowTitle">
<string>Line Plot</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="opaqueResize">
<bool>false</bool>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="pushButton_debug">
<property name="text">
<string>Debug</string>
</property>
</widget>
</item>
<item>
<widget class="GraphicsLayoutWidget" name="glw"/>
</item>
</layout>
</widget>
<widget class="QTableWidget" name="mouse_table">
<property name="textElideMode">
<enum>Qt::ElideMiddle</enum>
</property>
<column>
<property name="text">
<string>Display</string>
</property>
</column>
<column>
<property name="text">
<string>Device</string>
</property>
</column>
<column>
<property name="text">
<string>X</string>
</property>
</column>
<column>
<property name="text">
<string>Y</string>
</property>
</column>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>GraphicsLayoutWidget</class>
<extends>QGraphicsView</extends>
<header>pyqtgraph.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -1,8 +1,11 @@
import argparse
import itertools
import os
from dataclasses import dataclass
from threading import RLock
from bec_lib import BECClient
from bec_lib.core import BECMessage, MessageEndpoints
from bec_lib.core import BECMessage, MessageEndpoints, ServiceConfig
from bec_lib.core.redis_connector import RedisConsumerThreaded
from PyQt5.QtCore import QObject, pyqtSignal
@ -15,6 +18,27 @@ class _BECDap:
slots = set()
# Adding a new pyqt signal requres a class factory, as they must be part of the class definition
# and cannot be dynamically added as class attributes after the class has been defined.
_signal_class_factory = (
type(f"Signal{i}", (QObject,), dict(signal=pyqtSignal("PyQt_PyObject")))
for i in itertools.count()
)
@dataclass
class _Connection:
"""Utility class to keep track of slots connected to a particular redis consumer"""
consumer: RedisConsumerThreaded
slots = set()
# keep a reference to a new signal class, so it is not gc'ed
_signal_container = next(_signal_class_factory)()
def __post_init__(self):
self.signal = self._signal_container.signal
class _BECDispatcher(QObject):
new_scan = pyqtSignal(dict, dict)
scan_segment = pyqtSignal(dict, dict)
@ -23,32 +47,45 @@ class _BECDispatcher(QObject):
new_projection_id = pyqtSignal(dict)
new_projection_data = pyqtSignal(dict)
def __init__(self):
def __init__(self, bec_config=None):
super().__init__()
self.client = BECClient()
self.client.start()
# TODO: this is a workaround for now to provide service config within qtdesigner, but is
# it possible to provide config via a cli arg?
if bec_config is None and os.path.isfile("bec_config.yaml"):
bec_config = "bec_config.yaml"
self.client.initialize(config=ServiceConfig(config_path=bec_config))
self._slot_signal_map = {
"on_scan_segment": self.scan_segment,
"on_new_scan": self.new_scan,
}
self._daps = {}
self._connections = {}
self._scan_id = None
scan_lock = RLock()
# self.new_projection_id.connect(self.new_projection_data)
def _scan_segment_cb(scan_segment, metadata):
def _scan_segment_cb(msg):
msg = BECMessage.ScanMessage.loads(msg.value)[0]
with scan_lock:
# TODO: use ScanStatusMessage instead?
scan_id = metadata["scanID"]
scan_id = msg.content["scanID"]
if self._scan_id != scan_id:
self._scan_id = scan_id
self.new_scan.emit(scan_segment, metadata)
self.scan_segment.emit(scan_segment, metadata)
self.new_scan.emit(msg.content, msg.metadata)
self.scan_segment.emit(msg.content, msg.metadata)
self.client.callbacks.register("scan_segment", _scan_segment_cb, sync=False)
scan_segment_topic = MessageEndpoints.scan_segment()
self._scan_segment_thread = self.client.connector.consumer(
topics=scan_segment_topic,
cb=_scan_segment_cb,
)
self._scan_segment_thread.start()
def connect(self, widget):
for slot_name, signal in self._slot_signal_map.items():
@ -56,6 +93,39 @@ class _BECDispatcher(QObject):
if callable(slot):
signal.connect(slot)
def connect_slot(self, slot, topic):
# create new connection for topic if it doesn't exist
if topic not in self._connections:
def cb(msg):
msg = BECMessage.MessageReader.loads(msg.value)
self._connections[topic].signal.emit(msg)
consumer = self.client.connector.consumer(topics=topic, cb=cb)
consumer.start()
self._connections[topic] = _Connection(consumer)
# connect slot if it's not connected
if slot not in self._connections[topic].slots:
self._connections[topic].signal.connect(slot)
self._connections[topic].slots.add(slot)
def disconnect_slot(self, slot, topic):
if topic not in self._connections:
return
if slot not in self._connections[topic].slots:
return
self._connections[topic].signal.disconnect(slot)
self._connections[topic].slots.remove(slot)
if not self._connections[topic].slots:
# shutdown consumer if there are no more connected slots
self._connections[topic].consumer.shutdown()
del self._connections[topic]
def connect_dap_slot(self, slot, dap_name):
if dap_name not in self._daps:
# create a new consumer and connect slot
@ -150,4 +220,8 @@ class _BECDispatcher(QObject):
del self._daps[data_ep]
bec_dispatcher = _BECDispatcher()
parser = argparse.ArgumentParser()
parser.add_argument("--bec-config", default=None)
args, _ = parser.parse_known_args()
bec_dispatcher = _BECDispatcher(args.bec_config)

View File

@ -1,6 +1,6 @@
from setuptools import setup
__version__ = "0.3.0"
__version__ = "0.5.0"
if __name__ == "__main__":
setup(

152
tests/test_basic_plot.py Normal file
View File

@ -0,0 +1,152 @@
from unittest import mock
import numpy as np
from pytestqt import qtbot
from bec_widgets import basic_plot
def test_basic_plot_emits_no_signal(qtbot):
"""Test LinePlot emits no signal when only one data entry is present."""
y_value_list = ["y1", "y2"]
plot = basic_plot.BasicPlot(y_value_list=y_value_list)
data = {
"data": {
"x": {"x": {"value": 1}},
"y1": {"y1": {"value": 1}},
"y2": {"y2": {"value": 3}},
}
}
metadata = {"scanID": "test", "scan_number": 1, "scan_report_devices": ["x"]}
with mock.patch("bec_widgets.basic_plot.client") as mock_client:
with mock.patch.object(plot, "update_signal") as mock_update_signal:
plot.on_scan_segment(data=data, metadata=metadata)
mock_update_signal.emit.assert_not_called()
def test_basic_plot_emits_signal(qtbot):
"""Test LinePlot emits signal."""
y_value_list = ["y1", "y2"]
plot = basic_plot.BasicPlot(y_value_list=y_value_list)
data = {
"data": {
"x": {"x": {"value": 1}},
"y1": {"y1": {"value": 1}},
"y2": {"y2": {"value": 3}},
}
}
plotter_data_y = [[1, 1], [3, 3]]
metadata = {"scanID": "test", "scan_number": 1, "scan_report_devices": ["x"]}
with mock.patch("bec_widgets.basic_plot.client") as mock_client:
# mock_client.device_manager.devices.keys.return_value = ["y1"]
with mock.patch.object(plot, "update_signal") as mock_update_signal:
mock_update_signal.emit()
plot.on_scan_segment(data=data, metadata=metadata)
plot.on_scan_segment(data=data, metadata=metadata)
mock_update_signal.emit.assert_called()
# TODO allow mock_client to create return values for device_manager_devices
# assert plot.plotter_data_y == plotter_data_y
def test_basic_plot_raise_warning_wrong_signal_request(qtbot):
"""Test LinePlot raises warning and skips signal when entry not present in data."""
y_value_list = ["y1", "y22"]
plot = basic_plot.BasicPlot(y_value_list=y_value_list)
data = {
"data": {
"x": {"x": {"value": [1, 2, 3, 4, 5]}},
"y1": {"y1": {"value": [1, 2, 3, 4, 5]}},
"y2": {"y2": {"value": [1, 2, 3, 4, 5]}},
}
}
metadata = {"scanID": "test", "scan_number": 1, "scan_report_devices": ["x"]}
with mock.patch("bec_widgets.basic_plot.client") as mock_client:
# TODO fix mock_client
mock_dict = {"y1": [1, 2]}
mock_client.device_manager.devices.__contains__.side_effect = mock_dict.__contains__
# = {"y1": [1, 2]}
with mock.patch.object(plot, "update_signal") as mock_update_signal:
mock_update_signal.emit()
plot.on_scan_segment(data=data, metadata=metadata)
assert plot.y_value_list == ["y1"]
# def test_basic_plot_update(qtbot):
# """Test LinePlot update."""
# y_value_list = ["y1", "y2"]
# plot = basic_plot.BasicPlot(y_value_list=y_value_list)
# plot.label_bottom = "x"
# plot.label_left = f"{', '.join(y_value_list)}"
# plot.plotter_data_x = [1, 2, 3, 4, 5]
# plot.plotter_data_y = [[1, 2, 3, 4, 5], [3, 4, 5, 6, 7]]
# plot.update()
# assert all(plot.curves[0].getData()[0] == np.array([1, 2, 3, 4, 5]))
# assert all(plot.curves[0].getData()[1] == np.array([1, 2, 3, 4, 5]))
# assert all(plot.curves[1].getData()[1] == np.array([3, 4, 5, 6, 7]))
# # TODO Outputting the wrong data, e.g. motor is not in list of devices
# def test_basic_plot_update(qtbot):
# """Test LinePlot update."""
# y_value_list = ["y1", "y2"]
# plot = basic_plot.BasicPlot(y_value_list=y_value_list)
# plot.label_bottom = "x"
# plot.label_left = f"{', '.join(y_value_list)}"
# plot.plotter_data_x = [1, 2, 3, 4, 5]
# plot.plotter_data_y = [[1, 2, 3, 4, 5], [3, 4, 5, 6, 7]]
# plot.update()
# assert all(plot.curves[0].getData()[0] == np.array([1, 2, 3, 4, 5]))
# assert all(plot.curves[0].getData()[1] == np.array([1, 2, 3, 4, 5]))
# assert all(plot.curves[1].getData()[1] == np.array([3, 4, 5, 6, 7]))
# def test_basic_plot_mouse_moved(qtbot):
# """Test LinePlot mouse_moved."""
# y_value_list = ["y1", "y2"]
# plot = basic_plot.BasicPlot(y_value_list=y_value_list)
# plot.plotter_data_x = [1, 2, 3, 4, 5]
# plot.plotter_data_y = [[1, 2, 3, 4, 5], [3, 4, 5, 6, 7]]
# plot.precision = 3
# string_cap = 10
# x_data = f"{3:.{plot.precision}f}"
# y_data = f"{3:.{plot.precision}f}"
# output_string = "".join(
# [
# "Mouse cursor",
# "\n",
# f"{y_value_list[0]}",
# "\n",
# f"X_data: {x_data:>{string_cap}}",
# "\n",
# f"Y_data: {y_data:>{string_cap}}",
# ]
# )
# x_data = f"{3:.{plot.precision}f}"
# y_data = f"{5:.{plot.precision}f}"
# output_string = "".join(
# [
# output_string,
# "\n",
# f"{y_value_list[1]}",
# "\n",
# f"X_data: {x_data:>{string_cap}}",
# "\n",
# f"Y_data: {y_data:>{string_cap}}",
# ]
# )
# with mock.patch.object(
# plot, "plot"
# ) as mock_plot: # TODO change test to simulate QTable instead of QLabel
# mock_plot.sceneBoundingRect.contains.return_value = True
# mock_plot.vb.mapSceneToView((20, 10)).x.return_value = 2.8
# plot.mouse_moved((20, 10))
# assert plot.mouse_box_data.text() == output_string

View File

@ -1,152 +0,0 @@
from unittest import mock
import numpy as np
from pytestqt import qtbot
from bec_widgets import line_plot
def test_line_plot_emits_no_signal(qtbot):
"""Test LinePlot emits no signal when only one data entry is present."""
y_value_list = ["y1", "y2"]
plot = line_plot.BasicPlot(y_value_list=y_value_list)
data = {
"data": {
"x": {"x": {"value": 1}},
"y1": {"y1": {"value": 1}},
"y2": {"y2": {"value": 3}},
}
}
metadata = {"scanID": "test", "scan_number": 1, "scan_report_devices": ["x"]}
with mock.patch("bec_widgets.line_plot.BECClient") as mock_client:
with mock.patch.object(plot, "update_signal") as mock_update_signal:
plot(data=data, metadata=metadata)
mock_update_signal.emit.assert_not_called()
def test_line_plot_emits_signal(qtbot):
"""Test LinePlot emits signal."""
y_value_list = ["y1", "y2"]
plot = line_plot.BasicPlot(y_value_list=y_value_list)
data = {
"data": {
"x": {"x": {"value": 1}},
"y1": {"y1": {"value": 1}},
"y2": {"y2": {"value": 3}},
}
}
plotter_data_y = [[1, 1], [3, 3]]
metadata = {"scanID": "test", "scan_number": 1, "scan_report_devices": ["x"]}
with mock.patch("bec_widgets.line_plot.BECClient") as mock_client:
# mock_client.device_manager.devices.keys.return_value = ["y1"]
with mock.patch.object(plot, "update_signal") as mock_update_signal:
mock_update_signal.emit()
plot(data=data, metadata=metadata)
plot(data=data, metadata=metadata)
mock_update_signal.emit.assert_called()
# TODO allow mock_client to create return values for device_manager_devices
# assert plot.plotter_data_y == plotter_data_y
def test_line_plot_raise_warning_wrong_signal_request(qtbot):
"""Test LinePlot raises warning and skips signal when entry not present in data."""
y_value_list = ["y1", "y22"]
plot = line_plot.BasicPlot(y_value_list=y_value_list)
data = {
"data": {
"x": {"x": {"value": [1, 2, 3, 4, 5]}},
"y1": {"y1": {"value": [1, 2, 3, 4, 5]}},
"y2": {"y2": {"value": [1, 2, 3, 4, 5]}},
}
}
metadata = {"scanID": "test", "scan_number": 1, "scan_report_devices": ["x"]}
with mock.patch("bec_widgets.line_plot.BECClient") as mock_client:
# TODO fix mock_client
mock_dict = {"y1": [1, 2]}
mock_client().device_manager.devices.__contains__.side_effect = mock_dict.__contains__
# = {"y1": [1, 2]}
with mock.patch.object(plot, "update_signal") as mock_update_signal:
mock_update_signal.emit()
plot(data=data, metadata=metadata)
assert plot.y_value_list == ["y1"]
def test_line_plot_update(qtbot):
"""Test LinePlot update."""
y_value_list = ["y1", "y2"]
plot = line_plot.BasicPlot(y_value_list=y_value_list)
plot.label_bottom = "x"
plot.label_left = f"{', '.join(y_value_list)}"
plot.plotter_data_x = [1, 2, 3, 4, 5]
plot.plotter_data_y = [[1, 2, 3, 4, 5], [3, 4, 5, 6, 7]]
plot.update()
assert all(plot.curves[0].getData()[0] == np.array([1, 2, 3, 4, 5]))
assert all(plot.curves[0].getData()[1] == np.array([1, 2, 3, 4, 5]))
assert all(plot.curves[1].getData()[1] == np.array([3, 4, 5, 6, 7]))
# TODO Outputting the wrong data, e.g. motor is not in list of devices
def test_line_plot_update(qtbot):
"""Test LinePlot update."""
y_value_list = ["y1", "y2"]
plot = line_plot.BasicPlot(y_value_list=y_value_list)
plot.label_bottom = "x"
plot.label_left = f"{', '.join(y_value_list)}"
plot.plotter_data_x = [1, 2, 3, 4, 5]
plot.plotter_data_y = [[1, 2, 3, 4, 5], [3, 4, 5, 6, 7]]
plot.update()
assert all(plot.curves[0].getData()[0] == np.array([1, 2, 3, 4, 5]))
assert all(plot.curves[0].getData()[1] == np.array([1, 2, 3, 4, 5]))
assert all(plot.curves[1].getData()[1] == np.array([3, 4, 5, 6, 7]))
def test_line_plot_mouse_moved(qtbot):
"""Test LinePlot mouse_moved."""
y_value_list = ["y1", "y2"]
plot = line_plot.BasicPlot(y_value_list=y_value_list)
plot.plotter_data_x = [1, 2, 3, 4, 5]
plot.plotter_data_y = [[1, 2, 3, 4, 5], [3, 4, 5, 6, 7]]
plot.precision = 3
string_cap = 10
x_data = f"{3:.{plot.precision}f}"
y_data = f"{3:.{plot.precision}f}"
output_string = "".join(
[
"Mouse cursor",
"\n",
f"{y_value_list[0]}",
"\n",
f"X_data: {x_data:>{string_cap}}",
"\n",
f"Y_data: {y_data:>{string_cap}}",
]
)
x_data = f"{3:.{plot.precision}f}"
y_data = f"{5:.{plot.precision}f}"
output_string = "".join(
[
output_string,
"\n",
f"{y_value_list[1]}",
"\n",
f"X_data: {x_data:>{string_cap}}",
"\n",
f"Y_data: {y_data:>{string_cap}}",
]
)
with mock.patch.object(
plot, "plot"
) as mock_plot: # TODO change test to simulate QTable instead of QLabel
mock_plot.sceneBoundingRect.contains.return_value = True
mock_plot.vb.mapSceneToView((20, 10)).x.return_value = 2.8
plot.mouse_moved((20, 10))
assert plot.mouse_box_data.text() == output_string

View File

@ -13,12 +13,25 @@ def test_scan_plot(qtbot):
plot.x_channel = "x"
plot.y_channel_list = ["y1", "y2"]
plot.initialize()
plot.redraw_scan(
{"x": {"x": {"value": 1}}, "y1": {"y1": {"value": 1}}, "y2": {"y2": {"value": 3}}}
plot.on_scan_segment(
{
"data": {
"x": {"x": {"value": 1}},
"y1": {"y1": {"value": 1}},
"y2": {"y2": {"value": 3}},
}
},
{"scanID": "test", "scan_number": 1, "scan_report_devices": ["x"]},
)
plot.redraw_scan(
{"x": {"x": {"value": 2}}, "y1": {"y1": {"value": 2}}, "y2": {"y2": {"value": 4}}}
plot.on_scan_segment(
{
"data": {
"x": {"x": {"value": 2}},
"y1": {"y1": {"value": 2}},
"y2": {"y2": {"value": 4}},
}
},
{"scanID": "test", "scan_number": 1, "scan_report_devices": ["x"]},
)
assert all(plot.scan_curves["y1"].getData()[0] == [1, 2])
@ -35,13 +48,26 @@ def test_scan_plot_clears_data(qtbot):
plot.x_channel = "x"
plot.y_channel_list = ["y1", "y2"]
plot.initialize()
plot.redraw_scan(
{"x": {"x": {"value": 1}}, "y1": {"y1": {"value": 1}}, "y2": {"y2": {"value": 3}}}
plot.on_scan_segment(
{
"data": {
"x": {"x": {"value": 1}},
"y1": {"y1": {"value": 1}},
"y2": {"y2": {"value": 3}},
}
},
{"scanID": "test", "scan_number": 1, "scan_report_devices": ["x"]},
)
plot.clearData()
plot.redraw_scan(
{"x": {"x": {"value": 2}}, "y1": {"y1": {"value": 2}}, "y2": {"y2": {"value": 4}}}
plot.on_new_scan({}, {})
plot.on_scan_segment(
{
"data": {
"x": {"x": {"value": 2}},
"y1": {"y1": {"value": 2}},
"y2": {"y2": {"value": 4}},
}
},
{"scanID": "test", "scan_number": 1, "scan_report_devices": ["x"]},
)
assert all(plot.scan_curves["y1"].getData()[0] == [2])
@ -57,8 +83,7 @@ def test_scan_plot_redraws_dap(qtbot):
plot.y_channel_list = ["dap.y1", "dap.y2"]
plot.initialize()
plot.redraw_dap({"y1": {"x": [1], "y": [1]}, "y2": {"x": [2], "y": [2]}})
plot.redraw_dap({"y1": {"x": [1], "y": [1]}, "y2": {"x": [2], "y": [2]}}, {})
assert all(plot.dap_curves["y1"].getData()[0] == [1])
assert all(plot.dap_curves["y2"].getData()[1] == [2])