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

fix(examples): outdated examples removed (mca_plot.py, stream_plot.py, motor_example.py)

This commit is contained in:
2024-05-29 20:45:21 +02:00
parent 855be3551a
commit ddc9510c2b
12 changed files with 20 additions and 2253 deletions

View File

@ -2,18 +2,16 @@ import os
import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtWidgets
import qdarktheme
from qtconsole.inprocess import QtInProcessKernelManager
from qtconsole.rich_jupyter_widget import RichJupyterWidget
from qtpy.QtCore import QSize
from qtpy.QtGui import QIcon
from qtpy.QtWidgets import QApplication, QVBoxLayout, QWidget
from bec_widgets.cli.rpc_register import RPCRegister
from bec_widgets.utils import BECDispatcher, UILoader
from bec_widgets.widgets import BECFigure
from bec_widgets.widgets.dock.dock_area import BECDockArea
from bec_widgets.widgets.spiral_progress_bar.spiral_progress_bar import SpiralProgressBar
class JupyterConsoleWidget(RichJupyterWidget): # pragma: no cover:
@ -26,7 +24,6 @@ class JupyterConsoleWidget(RichJupyterWidget): # pragma: no cover:
self.kernel_client.start_channels()
self.kernel_manager.kernel.shell.push({"np": np, "pg": pg})
# self.set_console_font_size(70)
def shutdown_kernel(self):
self.kernel_client.stop_channels()
@ -46,27 +43,22 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
self.ui.splitter.setSizes([200, 100])
self.safe_close = False
# self.figure.clean_signal.connect(self.confirm_close)
self.register = RPCRegister()
self.register.add_rpc(self.figure)
# console push
self.console.kernel_manager.kernel.shell.push(
{
"fig": self.figure,
"register": self.register,
"dock": self.dock,
"w1": self.w1,
"w2": self.w2,
"w3": self.w3,
"d0": self.d0,
"d1": self.d1,
"d2": self.d2,
"d3": self.d3,
"fig0": self.fig0,
"fig1": self.fig1,
"fig2": self.fig2,
"bar": self.bar,
"b2a": self.button_2_a,
"b2b": self.button_2_b,
"b2c": self.button_2_c,
"bec": self.figure.client,
"scans": self.figure.client.scans,
"dev": self.figure.client.device_manager.devices,
@ -111,25 +103,22 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
self.c1 = self.w1.get_config()
def _init_dock(self):
self.button_1 = QtWidgets.QPushButton("Button 1 ")
self.button_2_a = QtWidgets.QPushButton("Button to be added at place 0,0 in d3")
self.button_2_b = QtWidgets.QPushButton("button after without postions specified")
self.button_2_c = QtWidgets.QPushButton("button super late")
self.button_3 = QtWidgets.QPushButton("Button above Figure ")
self.bar = SpiralProgressBar()
self.label_2 = QtWidgets.QLabel("label which is added separately")
self.label_3 = QtWidgets.QLabel("Label above figure")
self.d0 = self.dock.add_dock(name="dock_0")
self.fig0 = self.d0.add_widget_bec("BECFigure")
self.fig0.image("eiger", vrange=(0, 100))
self.d1 = self.dock.add_dock(widget=self.button_1, position="left")
self.d1.addWidget(self.label_2)
self.d2 = self.dock.add_dock(widget=self.bar, position="right")
self.d3 = self.dock.add_dock(name="figure")
self.fig_dock3 = BECFigure()
self.fig_dock3.plot(x_name="samx", y_name="bpm4d")
self.d3.add_widget(self.label_3)
self.d3.add_widget(self.button_3)
self.d3.add_widget(self.fig_dock3)
self.d1 = self.dock.add_dock(name="dock_1", position="right")
self.fig1 = self.d1.add_widget_bec("BECFigure")
self.fig1.plot(x_name="samx", y_name="bpm4i")
self.fig1.plot(x_name="samx", y_name="bpm3a")
self.d2 = self.dock.add_dock(name="dock_2", position="bottom")
self.fig2 = self.d2.add_widget_bec("BECFigure", row=0, col=0)
self.fig2.motor_map(x_name="samx", y_name="samy")
self.fig2.plot(x_name="samx", y_name="bpm4i")
self.bar = self.d2.add_widget_bec("SpiralProgressBar", row=0, col=1)
self.bar.set_diameter(200)
self.dock.save_state()
@ -155,6 +144,7 @@ if __name__ == "__main__": # pragma: no cover
app = QApplication(sys.argv)
app.setApplicationName("Jupyter Console")
app.setApplicationDisplayName("Jupyter Console")
qdarktheme.setup_theme("auto")
icon = QIcon()
icon.addFile(os.path.join(module_path, "assets", "terminal_icon.png"), size=QSize(48, 48))
app.setWindowIcon(icon)

View File

@ -1,159 +0,0 @@
# import simulation_progress as SP
import numpy as np
import pyqtgraph as pg
from bec_lib import messages
from bec_lib.endpoints import MessageEndpoints
from qtpy.QtCore import Signal as pyqtSignal
from qtpy.QtCore import Slot as pyqtSlot
from qtpy.QtWidgets import QApplication, QVBoxLayout, QWidget
class StreamApp(QWidget):
update_signal = pyqtSignal()
new_scan_id = pyqtSignal(str)
def __init__(self, device, sub_device):
super().__init__()
pg.setConfigOptions(background="w", foreground="k")
self.init_ui()
self.setWindowTitle("MCA readout")
self.data = None
self.scan_id = None
self.stream_consumer = None
self.device = device
self.sub_device = sub_device
self.start_device_consumer()
# self.start_device_consumer(self.device) # for simulation
self.new_scan_id.connect(self.create_new_stream_consumer)
self.update_signal.connect(self.plot_new)
def init_ui(self):
# Create layout and add widgets
self.layout = QVBoxLayout()
self.setLayout(self.layout)
# Create plot
self.glw = pg.GraphicsLayoutWidget()
self.layout.addWidget(self.glw)
# Create Plot and add ImageItem
self.plot_item = pg.PlotItem()
self.plot_item.setAspectLocked(False)
self.imageItem = pg.ImageItem()
# self.plot_item1D = pg.PlotItem()
# self.plot_item.addItem(self.imageItem)
# self.plot_item.addItem(self.plot_item1D)
# Setting up histogram
# self.hist = pg.HistogramLUTItem()
# self.hist.setImageItem(self.imageItem)
# self.hist.gradient.loadPreset("magma")
# self.update_hist()
# Adding Items to Graphical Layout
self.glw.addItem(self.plot_item)
# self.glw.addItem(self.hist)
@pyqtSlot(str)
def create_new_stream_consumer(self, scan_id: str):
print(f"Creating new stream consumer for scan_id: {scan_id}")
self.connect_stream_consumer(scan_id, self.device)
def connect_stream_consumer(self, scan_id, device):
if self.stream_consumer is not None:
self.stream_consumer.shutdown()
self.stream_consumer = connector.stream_consumer(
topics=MessageEndpoints.device_async_readback(scan_id=scan_id, device=device),
cb=self._streamer_cb,
parent=self,
)
self.stream_consumer.start()
def start_device_consumer(self):
self.device_consumer = connector.consumer(
topics=MessageEndpoints.scan_status(), cb=self._device_cv, parent=self
)
self.device_consumer.start()
# def start_device_consumer(self, device): #for simulation
# self.device_consumer = connector.consumer(
# topics=MessageEndpoints.device_status(device), cb=self._device_cv, parent=self
# )
#
# self.device_consumer.start()
def plot_new(self):
print(f"Printing data from plot update: {self.data}")
self.plot_item.plot(self.data[0])
# self.imageItem.setImage(self.data, autoLevels=False)
@staticmethod
def _streamer_cb(msg, *, parent, **_kwargs) -> None:
msgMCS = msg.value
print(msgMCS)
row = msgMCS.content["signals"][parent.sub_device]
metadata = msgMCS.metadata
# Check if the current number of rows is odd
# if parent.data is not None and parent.data.shape[0] % 2 == 1:
# row = np.flip(row) # Flip the row
print(f"Printing data from callback update: {row}")
parent.data = np.array([row])
# if parent.data is None:
# parent.data = np.array([row])
# else:
# parent.data = np.vstack((parent.data, row))
parent.update_signal.emit()
@staticmethod
def _device_cv(msg, *, parent, **_kwargs) -> None:
print("Getting ScanID")
msgDEV = msg.value
current_scan_id = msgDEV.content["scan_id"]
if parent.scan_id is None:
parent.scan_id = current_scan_id
parent.new_scan_id.emit(current_scan_id)
print(f"New scan_id: {current_scan_id}")
if current_scan_id != parent.scan_id:
parent.scan_id = current_scan_id
# parent.data = None
# parent.imageItem.clear()
parent.new_scan_id.emit(current_scan_id)
print(f"New scan_id: {current_scan_id}")
if __name__ == "__main__":
import argparse
from bec_lib.redis_connector import RedisConnector
parser = argparse.ArgumentParser(description="Stream App.")
parser.add_argument("--port", type=str, default="pc15543:6379", help="Port for RedisConnector")
parser.add_argument("--device", type=str, default="mcs", help="Device name")
parser.add_argument("--sub_device", type=str, default="mca4", help="Sub-device name")
args = parser.parse_args()
connector = RedisConnector(args.port)
app = QApplication([])
streamApp = StreamApp(device=args.device, sub_device=args.sub_device)
streamApp.show()
app.exec()

View File

@ -1,28 +0,0 @@
import time
from bec_lib import messages
from bec_lib.endpoints import MessageEndpoints
from bec_lib.redis_connector import RedisConnector
connector = RedisConnector("localhost:6379")
metadata = {}
scan_id = "ScanID1"
metadata.update(
{"scan_id": scan_id, "async_update": "append"} # this will be different for each scan
)
for ii in range(20):
data = {"mca1": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], "mca2": [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]}
msg = messages.DeviceMessage(signals=data, metadata=metadata).dumps()
connector.xadd(
topic=MessageEndpoints.device_async_readback(
scan_id=scan_id, device="mca"
), # scan_id will be different for each scan
msg={"data": msg}, # TODO should be msg_dict
expire=1800,
)
print(f"Sent {ii}")
time.sleep(0.5)

View File

@ -1,17 +0,0 @@
selected_motors:
motor_x: "samx"
motor_y: "samy"
plot_motors:
max_points: 1000
num_dim_points: 100
scatter_size: 5
precision: 3
mode_lock: False # "Individual" or "Start/Stop". False to unlock
extra_columns:
- sample name: "sample 1"
- step_x [mu]: 25
- step_y [mu]: 25
- exp_time [s]: 1
- start: 1
- tilt [deg]: 0

View File

@ -1,10 +0,0 @@
redis:
host: pc15543
port: 6379
mongodb:
host: localhost
port: 27017
scibec:
host: http://localhost
port: 3030
beamline: MyBeamline

View File

@ -1,17 +0,0 @@
selected_motors:
motor_x: "samx"
motor_y: "samy"
plot_motors:
max_points: 1000
num_dim_points: 100
scatter_size: 5
precision: 3
mode_lock: Start/Stop # "Individual" or "Start/Stop"
extra_columns:
- sample name: "sample 1"
- step_x [mu]: 25
- step_y [mu]: 25
- exp_time [s]: 1
- start: 1
- tilt [deg]: 0

File diff suppressed because it is too large Load Diff

View File

@ -1,148 +0,0 @@
<?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="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QSplitter" name="splitter_plot">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QWidget" name="glw_plot_placeholder" native="true"/>
<widget class="QWidget" name="glw_image_placeholder" native="true"/>
</widget>
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,1,1,15">
<item>
<widget class="QPushButton" name="pushButton_generate">
<property name="text">
<string>Generate 1D and 2D data without stream</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>1st angle of azimutal segment (deg)</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QDoubleSpinBox" name="doubleSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximum">
<double>360.000000000000000</double>
</property>
<property name="singleStep">
<double>0.250000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>f1amp</string>
</property>
</item>
<item>
<property name="text">
<string>f2amp</string>
</property>
</item>
<item>
<property name="text">
<string>f2 phase</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Precision</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_precision">
<property name="value">
<number>4</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTableWidget" name="cursor_table">
<property name="textElideMode">
<enum>Qt::ElideMiddle</enum>
</property>
<column>
<property name="text">
<string>Display</string>
</property>
</column>
<column>
<property name="text">
<string>X</string>
</property>
</column>
<column>
<property name="text">
<string>Y</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -1,342 +0,0 @@
import os
import threading
import time
import numpy as np
import pyqtgraph
import pyqtgraph as pg
from bec_lib import messages
from bec_lib.endpoints import MessageEndpoints
from bec_lib.redis_connector import RedisConnector
from pyqtgraph import mkBrush, mkPen
from pyqtgraph.Qt import QtCore, QtWidgets
from qtpy.QtCore import Signal, Slot
from qtpy.QtWidgets import QTableWidgetItem, QVBoxLayout
from bec_widgets.utils import Colors, Crosshair, UILoader
from bec_widgets.utils.bec_dispatcher import BECDispatcher
class StreamPlot(QtWidgets.QWidget):
update_signal = Signal()
roi_signal = Signal(tuple)
def __init__(self, name="", y_value_list=["gauss_bpm"], client=None, parent=None) -> None:
"""
Basic plot widget for displaying scan data.
Args:
name (str, optional): Name of the plot. Defaults to "".
y_value_list (list, optional): List of signals to be plotted. Defaults to ["gauss_bpm"].
"""
# Client and device manager from BEC
self.client = BECDispatcher().client if client is None else client
super(StreamPlot, self).__init__()
# Set style for pyqtgraph plots
pg.setConfigOption("background", "w")
pg.setConfigOption("foreground", "k")
current_path = os.path.dirname(__file__)
self.ui = UILoader().load_ui(os.path.join(current_path, "line_plot.ui"), self)
self._idle_time = 100
self.connector = RedisConnector(["localhost:6379"])
self.y_value_list = y_value_list
self.previous_y_value_list = None
self.plotter_data_x = []
self.plotter_data_y = []
self.plotter_scan_id = None
self._current_proj = None
self._current_metadata_ep = "px_stream/projection_{}/metadata"
self.proxy_update = pg.SignalProxy(self.update_signal, rateLimit=25, slot=self.update)
self._data_retriever_thread_exit_event = threading.Event()
self.data_retriever = threading.Thread(
target=self.on_projection, args=(self._data_retriever_thread_exit_event,), daemon=True
)
self.data_retriever.start()
##########################
# UI
##########################
self.init_ui()
self.init_curves()
self.hook_crosshair()
def close(self):
super().close()
self._data_retriever_thread_exit_event.set()
self.data_retriever.join()
def init_ui(self):
"""Setup all ui elements"""
##########################
# 1D Plot
##########################
# LabelItem for ROI
self.label_plot = pg.LabelItem(justify="center")
self.glw_plot_layout = QVBoxLayout(self.ui.glw_plot_placeholder)
self.glw_plot = pg.GraphicsLayoutWidget()
self.glw_plot_layout.addWidget(self.glw_plot)
self.glw_plot.addItem(self.label_plot)
self.label_plot.setText("ROI region")
# ROI selector - so far from [-1,1] #TODO update to scale with xrange
self.roi_selector = pg.LinearRegionItem([-1, 1])
self.glw_plot.nextRow() # TODO update of cursor
self.label_plot_moved = pg.LabelItem(justify="center")
self.glw_plot.addItem(self.label_plot_moved)
self.label_plot_moved.setText("Actual coordinates (X, Y)")
# Label for coordinates clicked
self.glw_plot.nextRow()
self.label_plot_clicked = pg.LabelItem(justify="center")
self.glw_plot.addItem(self.label_plot_clicked)
self.label_plot_clicked.setText("Clicked coordinates (X, Y)")
# 1D PlotItem
self.glw_plot.nextRow()
self.plot = pg.PlotItem()
self.plot.setLogMode(True, True)
self.glw_plot.addItem(self.plot)
self.plot.addLegend()
##########################
# 2D Plot
##########################
# Label for coordinates moved
self.label_image_moved = pg.LabelItem(justify="center")
self.glw_image_layout = QVBoxLayout(self.ui.glw_image_placeholder)
self.glw_image = pg.GraphicsLayoutWidget()
self.glw_plot_layout.addWidget(self.glw_image)
self.glw_image.addItem(self.label_image_moved)
self.label_image_moved.setText("Actual coordinates (X, Y)")
# Label for coordinates clicked
self.glw_image.nextRow()
self.label_image_clicked = pg.LabelItem(justify="center")
self.glw_image.addItem(self.label_image_clicked)
self.label_image_clicked.setText("Clicked coordinates (X, Y)")
# TODO try to lock aspect ratio with view
# # Create a window
# win = pg.GraphicsLayoutWidget()
# win.show()
#
# # Create a ViewBox
# view = win.addViewBox()
#
# # Lock the aspect ratio
# view.setAspectLocked(True)
# # Create an ImageItem
# image_item = pg.ImageItem(np.random.random((100, 100)))
#
# # Add the ImageItem to the ViewBox
# view.addItem(image_item)
# 2D ImageItem
self.glw_image.nextRow()
self.plot_image = pg.PlotItem()
self.glw_image.addItem(self.plot_image)
def init_curves(self):
# init of 1D plot
self.plot.clear()
self.curves = []
self.pens = []
self.brushs = []
self.color_list = Colors.golden_angle_color(colormap="CET-R2", num=len(self.y_value_list))
for ii, y_value in enumerate(self.y_value_list):
pen = mkPen(color=self.color_list[ii], width=2, style=QtCore.Qt.DashLine)
brush = mkBrush(color=self.color_list[ii])
curve = pg.PlotDataItem(symbolBrush=brush, pen=pen, skipFiniteCheck=True, name=y_value)
self.plot.addItem(curve)
self.curves.append(curve)
self.pens.append(pen)
self.brushs.append(brush)
# check if roi selector is in the plot
if self.roi_selector not in self.plot.items:
self.plot.addItem(self.roi_selector)
# init of 2D plot
self.plot_image.clear()
self.img = pg.ImageItem()
self.plot_image.addItem(self.img)
# hooking signals
self.hook_crosshair()
self.init_table()
def splitter_sizes(self): ...
def hook_crosshair(self):
self.crosshair_1d = Crosshair(self.plot, precision=4)
self.crosshair_1d.coordinatesChanged1D.connect(
lambda x, y: self.label_plot_moved.setText(f"Moved : ({x}, {y})")
)
self.crosshair_1d.coordinatesClicked1D.connect(
lambda x, y: self.label_plot_clicked.setText(f"Moved : ({x}, {y})")
)
self.crosshair_1d.coordinatesChanged1D.connect(
lambda x, y: self.update_table(table_widget=self.cursor_table, x=x, y_values=y)
)
self.crosshair_2D = Crosshair(self.plot_image)
self.crosshair_2D.coordinatesChanged2D.connect(
lambda x, y: self.label_image_moved.setText(f"Moved : ({x}, {y})")
)
self.crosshair_2D.coordinatesClicked2D.connect(
lambda x, y: self.label_image_clicked.setText(f"Moved : ({x}, {y})")
)
# ROI
self.roi_selector.sigRegionChangeFinished.connect(self.get_roi_region)
def get_roi_region(self):
"""For testing purpose now, get roi region and print it to self.label as tuple"""
region = self.roi_selector.getRegion()
self.label_plot.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 = messages.DeviceMessage(signals=return_dict).dumps()
self.connector.set_and_publish("px_stream/gui_event", msg=msg)
self.roi_signal.emit(region)
def init_table(self):
# Init number of rows in table according to n of devices
self.ui.cursor_table.setRowCount(len(self.y_value_list))
# self.table.setHorizontalHeaderLabels(["(X, Y) - Moved", "(X, Y) - Clicked"]) #TODO can be dynamic
self.ui.cursor_table.setVerticalHeaderLabels(self.y_value_list)
self.ui.cursor_table.resizeColumnsToContents()
def update_table(self, table_widget, x, y_values):
for i, y in enumerate(y_values):
table_widget.setItem(i, 1, QTableWidgetItem(str(x)))
table_widget.setItem(i, 2, QTableWidgetItem(str(y)))
table_widget.resizeColumnsToContents()
def update(self):
"""Update the plot with the new data."""
# check if QTable was initialised and if list of devices was changed
# if self.y_value_list != self.previous_y_value_list:
# self.setup_cursor_table()
# self.previous_y_value_list = self.y_value_list.copy() if self.y_value_list else None
self.curves[0].setData(self.plotter_data_x[0], self.plotter_data_y[0])
@staticmethod
def flip_even_rows(arr):
arr_copy = np.copy(arr) # Create a writable copy
arr_copy[1::2, :] = arr_copy[1::2, ::-1]
return arr_copy
@staticmethod
def remove_curve_by_name(plot: pyqtgraph.PlotItem, name: str) -> None:
# def remove_curve_by_name(plot: pyqtgraph.PlotItem, checkbox: QtWidgets.QCheckBox, name: str) -> None:
"""Removes a curve from the given plot by the specified name.
Args:
plot (pyqtgraph.PlotItem): The plot from which to remove the curve.
name (str): The name of the curve to remove.
"""
# if checkbox.isChecked():
for item in plot.items:
if isinstance(item, pg.PlotDataItem) and getattr(item, "opts", {}).get("name") == name:
plot.removeItem(item)
return
# else:
# return
def on_projection(self, exit_event):
while not exit_event.is_set():
if self._current_proj is None:
time.sleep(0.1)
continue
endpoint = f"px_stream/projection_{self._current_proj}/data"
msgs = self.client.connector.lrange(topic=endpoint, start=-1, end=-1)
data = msgs
if not data:
continue
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()
@Slot(dict, dict)
def on_dap_update(self, data: dict, metadata: dict):
flipped_data = self.flip_even_rows(data["data"]["z"])
self.img.setImage(flipped_data)
@Slot(dict, dict)
def new_proj(self, content: dict, _metadata: dict):
proj_nr = content["signals"]["proj_nr"]
endpoint = f"px_stream/projection_{proj_nr}/metadata"
msg_raw = self.client.connector.get(topic=endpoint)
msg = messages.DeviceMessage.loads(msg_raw)
self._current_q = msg.content["signals"]["q"]
self._current_norm = msg.content["signals"]["norm_sum"]
self._current_metadata = msg.content["signals"]["metadata"]
self.plotter_data_x = [self._current_q]
self._current_proj = proj_nr
if __name__ == "__main__":
import argparse
# from bec_widgets import ctrl_c # TODO uncomment when ctrl_c is ready to be compatible with qtpy
parser = argparse.ArgumentParser()
parser.add_argument(
"--signals", help="specify recorded signals", nargs="+", default=["gauss_bpm"]
)
# default = ["gauss_bpm", "bpm4i", "bpm5i", "bpm6i", "xert"],
value = parser.parse_args()
print(f"Plotting signals for: {', '.join(value.signals)}")
# Client from dispatcher
bec_dispatcher = BECDispatcher()
client = bec_dispatcher.client
app = QtWidgets.QApplication([])
# ctrl_c.setup(app) # TODO uncomment when ctrl_c is ready to be compatible with qtpy
plot = StreamPlot(y_value_list=value.signals, client=client)
bec_dispatcher.connect_slot(plot.new_proj, "px_stream/proj_nr")
bec_dispatcher.connect_slot(
plot.on_dap_update, MessageEndpoints.processed_data("px_dap_worker")
)
plot.show()
# client.callbacks.register("scan_segment", plot, sync=False)
app.exec()

View File

@ -1,158 +0,0 @@
# pylint: disable = no-name-in-module,missing-class-docstring, missing-module-docstring
import threading
from unittest import mock
import numpy as np
import pytest
from bec_lib import messages
from bec_lib.redis_connector import RedisConnector
from pytestqt import qtbot
from bec_widgets.examples.stream_plot.stream_plot import StreamPlot
@pytest.fixture(scope="function")
def stream_app(qtbot):
"""Helper function to set up the StreamPlot widget."""
client = mock.MagicMock()
widget = StreamPlot(client=client)
qtbot.addWidget(widget)
qtbot.waitExposed(widget)
yield widget
widget.close()
def test_roi_signals_emitted(qtbot, stream_app):
region = (0.1, 0.9)
with qtbot.waitSignal(stream_app.roi_signal, timeout=1000) as blocker:
stream_app.roi_signal.emit(region)
assert blocker.signal_triggered
assert blocker.args == [region]
def test_update_signals_emitted(qtbot, stream_app):
# Mimic data coming from the data stream
stream_app.plotter_data_x = [list(range(10))] # Replace with the actual x data
stream_app.plotter_data_y = [list(range(10))] # Replace with the actual y data
# Initialize curves
stream_app.init_curves()
with qtbot.waitSignal(stream_app.update_signal, timeout=1000) as blocker:
stream_app.update_signal.emit()
assert blocker.signal_triggered
def test_ui_initialization(qtbot, stream_app):
"""Checking the UI creation."""
# Check if UI elements are initialized correctly
assert stream_app.label_plot is not None
assert stream_app.label_plot_moved is not None
assert stream_app.label_plot_clicked is not None
assert stream_app.label_image_moved is not None
assert stream_app.label_image_clicked is not None
# Check if plots are initialized correctly
assert stream_app.plot is not None
assert stream_app.plot_image is not None
# Check if ROI selector is initialized correctly
assert stream_app.roi_selector is not None
def test_1d_plotting_data(qtbot, stream_app):
# Set up some mock data
x_data = [list(range(10))]
y_data = [list(range(10))]
# Manually set the data attributes
stream_app.plotter_data_x = x_data
stream_app.plotter_data_y = y_data
stream_app.y_value_list = ["Curve 1"]
# Initialize curves and update the plot
stream_app.init_curves()
stream_app.update() # This should update the plot with the new data
# Check the data on the plot
for idx, curve in enumerate(stream_app.curves):
np.testing.assert_array_equal(curve.xData, x_data[0]) # Access the first list of x_data
np.testing.assert_array_equal(
curve.yData, y_data[idx]
) # Access the list of y_data for each curve without additional indexing
def test_flip_even_rows(qtbot, stream_app):
# Create a numpy array with some known data
original_array = np.array(
[[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20]]
)
# Call flip_even_rows on the original array
flipped_array = stream_app.flip_even_rows(original_array)
# Expected array flipped along the rows with even indices
expected_array = np.array(
[[1, 2, 3, 4, 5], [10, 9, 8, 7, 6], [11, 12, 13, 14, 15], [20, 19, 18, 17, 16]]
)
# Check that flip_even_rows returned the expected result
np.testing.assert_array_equal(flipped_array, expected_array)
def test_on_dap_update(qtbot, stream_app):
"""2D image rendering by dap update"""
# Create some mock data to be "received" by the slot
data_dict = {"data": {"z": np.random.rand(10, 10)}}
metadata_dict = {}
# Trigger the slot
stream_app.on_dap_update(data_dict, metadata_dict)
# Apply the same transformation to the test data
expected_data = stream_app.flip_even_rows(data_dict["data"]["z"])
# Now check the state of the StreamPlot object
# For example, check the data of the image plot:
np.testing.assert_array_equal(stream_app.img.image, expected_data)
####################
# Until Here
####################
# def test_new_proj(qtbot, stream_app): #TODO this test is not working, does it make sense testing even?
# # Create some mock content to be "received" by the slot
# content_dict = {"signals": {"proj_nr": 1}}
# metadata_dict = {}
#
# # Manually create some mock data that new_proj would work with
# # This step may need to be adjusted to fit the actual behavior of new_proj
# mock_data = {
# "q": np.array([1, 2, 3, 4, 5]),
# "norm_sum": np.array([6, 7, 8, 9, 10]),
# "metadata": "some_metadata",
# }
#
# # Assume the RedisConnector client would return this data when new_proj is called
# mock_message = mock.MagicMock(spec=messages.DeviceMessage)
# mock_message.__getitem__.side_effect = lambda key: mock_data[key]
# stream_app.client.producer.get = mock.MagicMock(return_value=mock_message.dumps())
#
# # Trigger the slot
# stream_app.new_proj(content_dict, metadata_dict)
#
# # Now check the state of the StreamPlot object
# # For example, check that the plotter_data_x attribute was updated correctly:
# np.testing.assert_array_equal(stream_app.plotter_data_x, [mock_data["q"]])
# assert stream_app._current_proj == 1
# assert stream_app._current_q == mock_data["q"]
# assert stream_app._current_norm == mock_data["norm_sum"]
# assert stream_app._current_metadata == mock_data["metadata"]
# def test_connection_creation(qtbot, stream_app): #TODO maybe test connections in a different way?
# assert isinstance(stream_app.producer, RedisConnector)
# assert isinstance(stream_app.data_retriever, threading.Thread)
# assert stream_app.data_retriever.is_alive()