diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e9372b1..0c38641e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,46 @@ +## 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 diff --git a/bec_widgets/line_plot.py b/bec_widgets/basic_plot.py similarity index 93% rename from bec_widgets/line_plot.py rename to bec_widgets/basic_plot.py index 5fc7d830..acbb3935 100644 --- a/bec_widgets/line_plot.py +++ b/bec_widgets/basic_plot.py @@ -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( diff --git a/bec_widgets/basic_plot.ui b/bec_widgets/basic_plot.ui new file mode 100644 index 00000000..360a922f --- /dev/null +++ b/bec_widgets/basic_plot.ui @@ -0,0 +1,77 @@ + + + Form + + + + 0 + 0 + 845 + 635 + + + + Line Plot + + + + + + Qt::Horizontal + + + false + + + + + + + Debug + + + + + + + + + + + Qt::ElideMiddle + + + + Display + + + + + Device + + + + + X + + + + + Y + + + + + + + + + + GraphicsLayoutWidget + QGraphicsView +
pyqtgraph.h
+
+
+ + +
diff --git a/bec_widgets/bec_dispatcher.py b/bec_widgets/bec_dispatcher.py index 51028e91..c97d085b 100644 --- a/bec_widgets/bec_dispatcher.py +++ b/bec_widgets/bec_dispatcher.py @@ -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) diff --git a/setup.py b/setup.py index 18227563..c8d88a97 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import setup -__version__ = "0.3.0" +__version__ = "0.5.0" if __name__ == "__main__": setup( diff --git a/tests/test_basic_plot.py b/tests/test_basic_plot.py new file mode 100644 index 00000000..80d33d11 --- /dev/null +++ b/tests/test_basic_plot.py @@ -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 diff --git a/tests/test_line_plot.py b/tests/test_line_plot.py deleted file mode 100644 index a561cb34..00000000 --- a/tests/test_line_plot.py +++ /dev/null @@ -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 diff --git a/tests/test_scan_plot.py b/tests/test_scan_plot.py index fac058b9..ec4bcb3b 100644 --- a/tests/test_scan_plot.py +++ b/tests/test_scan_plot.py @@ -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])