mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-14 03:31:50 +02:00
Merge branch 'frontend'
This commit is contained in:
@ -11,7 +11,14 @@ class Crosshair(QObject):
|
|||||||
coordinatesChanged2D = pyqtSignal(float, float)
|
coordinatesChanged2D = pyqtSignal(float, float)
|
||||||
coordinatesClicked2D = pyqtSignal(float, float)
|
coordinatesClicked2D = pyqtSignal(float, float)
|
||||||
|
|
||||||
def __init__(self, plot_item, precision=None, parent=None):
|
def __init__(self, plot_item: pg.PlotItem, precision: int = None, parent=None):
|
||||||
|
"""
|
||||||
|
Crosshair for 1D and 2D plots.
|
||||||
|
Args:
|
||||||
|
plot_item (pyqtgraph.PlotItem): The plot item to which the crosshair will be attached.
|
||||||
|
precision (int, optional): Number of decimal places to round the coordinates to. Defaults to None.
|
||||||
|
parent (QObject, optional): Parent object for the QObject. Defaults to None.
|
||||||
|
"""
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.is_log_y = None
|
self.is_log_y = None
|
||||||
self.is_log_x = None
|
self.is_log_x = None
|
||||||
@ -26,61 +33,93 @@ class Crosshair(QObject):
|
|||||||
)
|
)
|
||||||
self.plot_item.scene().sigMouseClicked.connect(self.mouse_clicked)
|
self.plot_item.scene().sigMouseClicked.connect(self.mouse_clicked)
|
||||||
|
|
||||||
# Add marker for clicked and selected point
|
# Initialize markers
|
||||||
data = self.get_data()
|
self.marker_moved_1d = []
|
||||||
if isinstance(data, list): # 1D plot
|
self.marker_clicked_1d = []
|
||||||
num_curves = len(data)
|
self.marker_2d = None
|
||||||
self.marker_moved_1d = []
|
self.update_markers()
|
||||||
self.marker_clicked_1d = []
|
|
||||||
for i in range(num_curves):
|
def update_markers(self):
|
||||||
color = plot_item.listDataItems()[i].opts["pen"].color()
|
"""Update the markers for the crosshair, creating new ones if necessary."""
|
||||||
|
|
||||||
|
# Clear existing markers
|
||||||
|
for marker in self.marker_moved_1d + self.marker_clicked_1d:
|
||||||
|
self.plot_item.removeItem(marker)
|
||||||
|
if self.marker_2d:
|
||||||
|
self.plot_item.removeItem(self.marker_2d)
|
||||||
|
|
||||||
|
# Create new markers
|
||||||
|
self.marker_moved_1d = []
|
||||||
|
self.marker_clicked_1d = []
|
||||||
|
self.marker_2d = None
|
||||||
|
for item in self.plot_item.items:
|
||||||
|
if isinstance(item, pg.PlotDataItem): # 1D plot
|
||||||
|
pen = item.opts["pen"]
|
||||||
|
color = pen.color() if hasattr(pen, "color") else pg.mkColor(pen)
|
||||||
marker_moved = pg.ScatterPlotItem(
|
marker_moved = pg.ScatterPlotItem(
|
||||||
size=10, pen=pg.mkPen(color), brush=pg.mkBrush(None)
|
size=10, pen=pg.mkPen(color), brush=pg.mkBrush(None)
|
||||||
) # Hollow
|
)
|
||||||
marker_clicked = pg.ScatterPlotItem(
|
marker_clicked = pg.ScatterPlotItem(
|
||||||
size=10, pen=pg.mkPen(None), brush=pg.mkBrush(color)
|
size=10, pen=pg.mkPen(None), brush=pg.mkBrush(color)
|
||||||
) # Full
|
)
|
||||||
self.marker_moved_1d.append(marker_moved)
|
self.marker_moved_1d.append(marker_moved)
|
||||||
self.marker_clicked_1d.append(marker_clicked)
|
self.marker_clicked_1d.append(marker_clicked)
|
||||||
self.plot_item.addItem(marker_moved)
|
self.plot_item.addItem(marker_moved)
|
||||||
self.plot_item.addItem(marker_clicked)
|
self.plot_item.addItem(marker_clicked)
|
||||||
else: # 2D plot
|
elif isinstance(item, pg.ImageItem): # 2D plot
|
||||||
self.marker_2d = pg.ROI([0, 0], size=[1, 1], pen=pg.mkPen("r", width=2), movable=False)
|
self.marker_2d = pg.ROI(
|
||||||
self.plot_item.addItem(self.marker_2d)
|
[0, 0], size=[1, 1], pen=pg.mkPen("r", width=2), movable=False
|
||||||
|
)
|
||||||
|
self.plot_item.addItem(self.marker_2d)
|
||||||
|
|
||||||
def get_data(self):
|
def snap_to_data(self, x, y) -> tuple:
|
||||||
curves = []
|
"""
|
||||||
|
Finds the nearest data points to the given x and y coordinates.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
x: The x-coordinate
|
||||||
|
y: The y-coordinate
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple: The nearest x and y values
|
||||||
|
"""
|
||||||
|
y_values_1d = []
|
||||||
|
x_values_1d = []
|
||||||
|
image_2d = None
|
||||||
|
|
||||||
|
# Iterate through items in the plot
|
||||||
for item in self.plot_item.items:
|
for item in self.plot_item.items:
|
||||||
if isinstance(item, pg.PlotDataItem): # 1D plot
|
if isinstance(item, pg.PlotDataItem): # 1D plot
|
||||||
curves.append((item.xData, item.yData))
|
x_data, y_data = item.xData, item.yData
|
||||||
|
if x_data is not None and y_data is not None:
|
||||||
|
if self.is_log_x:
|
||||||
|
min_x_data = np.min(x_data[x_data > 0])
|
||||||
|
else:
|
||||||
|
min_x_data = np.min(x_data)
|
||||||
|
max_x_data = np.max(x_data)
|
||||||
|
if x < min_x_data or x > max_x_data:
|
||||||
|
return None, None
|
||||||
|
closest_x, closest_y = self.closest_x_y_value(x, x_data, y_data)
|
||||||
|
y_values_1d.append(closest_y)
|
||||||
|
x_values_1d.append(closest_x)
|
||||||
elif isinstance(item, pg.ImageItem): # 2D plot
|
elif isinstance(item, pg.ImageItem): # 2D plot
|
||||||
return item.image, None
|
image_2d = item.image
|
||||||
|
|
||||||
return curves
|
# Handle 1D plot
|
||||||
|
if y_values_1d:
|
||||||
|
if all(v is None for v in x_values_1d) or all(v is None for v in y_values_1d):
|
||||||
|
return None, None
|
||||||
|
return x, y_values_1d
|
||||||
|
|
||||||
# if curves:
|
# Handle 2D plot
|
||||||
# return curves
|
if image_2d is not None:
|
||||||
# else:
|
x_idx = int(np.clip(x, 0, image_2d.shape[0] - 1))
|
||||||
# return None
|
y_idx = int(np.clip(y, 0, image_2d.shape[1] - 1))
|
||||||
|
|
||||||
def snap_to_data(self, x, y):
|
|
||||||
data = self.get_data()
|
|
||||||
# if data is None: #TODO hadle if data are None
|
|
||||||
# return x, y
|
|
||||||
|
|
||||||
if isinstance(data, list): # 1D plot
|
|
||||||
y_values = []
|
|
||||||
for x_data, y_data in data:
|
|
||||||
closest_x, closest_y = self.closest_x_y_value(x, x_data, y_data)
|
|
||||||
y_values.append(closest_y)
|
|
||||||
return x, y_values
|
|
||||||
elif isinstance(data[0], np.ndarray): # 2D plot
|
|
||||||
x_idx = int(np.clip(x, 0, data[0].shape[0] - 1))
|
|
||||||
y_idx = int(np.clip(y, 0, data[0].shape[1] - 1))
|
|
||||||
return x_idx, y_idx
|
return x_idx, y_idx
|
||||||
return x, y
|
|
||||||
|
|
||||||
def closest_x_y_value(self, input_value, list_x, list_y):
|
return None, None
|
||||||
|
|
||||||
|
def closest_x_y_value(self, input_value: float, list_x: list, list_y: list) -> tuple:
|
||||||
"""
|
"""
|
||||||
Find the closest x and y value to the input value.
|
Find the closest x and y value to the input value.
|
||||||
|
|
||||||
@ -97,6 +136,11 @@ class Crosshair(QObject):
|
|||||||
return list_x[i], list_y[i]
|
return list_x[i], list_y[i]
|
||||||
|
|
||||||
def mouse_moved(self, event):
|
def mouse_moved(self, event):
|
||||||
|
"""Handles the mouse moved event, updating the crosshair position and emitting signals.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
event: The mouse moved event
|
||||||
|
"""
|
||||||
self.check_log()
|
self.check_log()
|
||||||
pos = event[0]
|
pos = event[0]
|
||||||
if self.plot_item.vb.sceneBoundingRect().contains(pos):
|
if self.plot_item.vb.sceneBoundingRect().contains(pos):
|
||||||
@ -111,44 +155,61 @@ class Crosshair(QObject):
|
|||||||
y = 10**y
|
y = 10**y
|
||||||
x, y_values = self.snap_to_data(x, y)
|
x, y_values = self.snap_to_data(x, y)
|
||||||
|
|
||||||
if isinstance(y_values, list): # 1D plot
|
for item in self.plot_item.items:
|
||||||
self.coordinatesChanged1D.emit(
|
if isinstance(item, pg.PlotDataItem):
|
||||||
round(x, self.precision), [round(y_val, self.precision) for y_val in y_values]
|
if x is None or all(v is None for v in y_values):
|
||||||
)
|
return
|
||||||
for i, y_val in enumerate(y_values):
|
self.coordinatesChanged1D.emit(
|
||||||
self.marker_moved_1d[i].setData(
|
round(x, self.precision),
|
||||||
[x if not self.is_log_x else np.log10(x)],
|
[round(y_val, self.precision) for y_val in y_values],
|
||||||
[y_val if not self.is_log_y else np.log10(y_val)],
|
|
||||||
)
|
)
|
||||||
else: # 2D plot
|
for i, y_val in enumerate(y_values):
|
||||||
self.coordinatesChanged2D.emit(x, y_values)
|
self.marker_moved_1d[i].setData(
|
||||||
|
[x if not self.is_log_x else np.log10(x)],
|
||||||
|
[y_val if not self.is_log_y else np.log10(y_val)],
|
||||||
|
)
|
||||||
|
elif isinstance(item, pg.ImageItem):
|
||||||
|
if x is None or y_values is None:
|
||||||
|
return
|
||||||
|
self.coordinatesChanged2D.emit(x, y_values)
|
||||||
|
|
||||||
def mouse_clicked(self, event):
|
def mouse_clicked(self, event):
|
||||||
|
"""Handles the mouse clicked event, updating the crosshair position and emitting signals.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
event: The mouse clicked event
|
||||||
|
"""
|
||||||
self.check_log()
|
self.check_log()
|
||||||
if self.plot_item.vb.sceneBoundingRect().contains(event._scenePos):
|
if self.plot_item.vb.sceneBoundingRect().contains(event._scenePos):
|
||||||
mouse_point = self.plot_item.vb.mapSceneToView(event._scenePos)
|
mouse_point = self.plot_item.vb.mapSceneToView(event._scenePos)
|
||||||
x, y = mouse_point.x(), mouse_point.y()
|
x, y = mouse_point.x(), mouse_point.y()
|
||||||
|
|
||||||
if self.is_log_x:
|
if self.is_log_x:
|
||||||
x = 10**x
|
x = 10**x
|
||||||
if self.is_log_y:
|
if self.is_log_y:
|
||||||
y = 10**y
|
y = 10**y
|
||||||
x, y_values = self.snap_to_data(x, y)
|
x, y_values = self.snap_to_data(x, y)
|
||||||
if isinstance(y_values, list): # 1D plot
|
|
||||||
self.coordinatesClicked1D.emit(
|
for item in self.plot_item.items:
|
||||||
round(x, self.precision), [round(y_val, self.precision) for y_val in y_values]
|
if isinstance(item, pg.PlotDataItem):
|
||||||
)
|
if x is None or all(v is None for v in y_values):
|
||||||
for i, y_val in enumerate(y_values):
|
return
|
||||||
self.marker_clicked_1d[i].setData(
|
self.coordinatesClicked1D.emit(
|
||||||
[x if not self.is_log_x else np.log10(x)],
|
round(x, self.precision),
|
||||||
[y_val if not self.is_log_y else np.log10(y_val)],
|
[round(y_val, self.precision) for y_val in y_values],
|
||||||
)
|
)
|
||||||
else: # 2D plot
|
for i, y_val in enumerate(y_values):
|
||||||
self.coordinatesClicked2D.emit(x, y_values)
|
self.marker_clicked_1d[i].setData(
|
||||||
self.marker_2d.setPos([x, y_values])
|
[x if not self.is_log_x else np.log10(x)],
|
||||||
|
[y_val if not self.is_log_y else np.log10(y_val)],
|
||||||
|
)
|
||||||
|
elif isinstance(item, pg.ImageItem):
|
||||||
|
if x is None or y_values is None:
|
||||||
|
return
|
||||||
|
self.coordinatesClicked2D.emit(x, y_values)
|
||||||
|
self.marker_2d.setPos([x, y_values])
|
||||||
|
|
||||||
def check_log(self):
|
def check_log(self):
|
||||||
"""
|
"""Checks if the x or y axis is in log scale and updates the internal state accordingly."""
|
||||||
Check if x or y axis is in log scale
|
|
||||||
"""
|
|
||||||
self.is_log_x = self.plot_item.ctrl.logXCheck.isChecked()
|
self.is_log_x = self.plot_item.ctrl.logXCheck.isChecked()
|
||||||
self.is_log_y = self.plot_item.ctrl.logYCheck.isChecked()
|
self.is_log_y = self.plot_item.ctrl.logYCheck.isChecked()
|
||||||
|
@ -8,6 +8,7 @@ from PyQt5.QtWidgets import (
|
|||||||
QHBoxLayout,
|
QHBoxLayout,
|
||||||
QTableWidget,
|
QTableWidget,
|
||||||
QTableWidgetItem,
|
QTableWidgetItem,
|
||||||
|
QSpinBox,
|
||||||
)
|
)
|
||||||
from pyqtgraph import mkPen
|
from pyqtgraph import mkPen
|
||||||
from pyqtgraph.Qt import QtCore
|
from pyqtgraph.Qt import QtCore
|
||||||
@ -40,25 +41,15 @@ class ExampleApp(QWidget):
|
|||||||
|
|
||||||
# same convention as in line_plot.py
|
# same convention as in line_plot.py
|
||||||
self.y_value_list = [
|
self.y_value_list = [
|
||||||
np.sin(self.x_data),
|
gauss(self.x_data, 1, 1),
|
||||||
np.cos(self.x_data),
|
gauss(self.x_data, 1.5, 3),
|
||||||
np.sin(2 * self.x_data),
|
abs(np.sin(self.x_data)),
|
||||||
|
abs(np.cos(self.x_data)),
|
||||||
|
abs(np.sin(2 * self.x_data)),
|
||||||
] # List of y-values for multiple curves
|
] # List of y-values for multiple curves
|
||||||
|
|
||||||
self.y_value_list = [gauss(self.x_data, 1, 1), gauss(self.x_data, 1.5, 3)]
|
self.curve_names = ["Gauss(1,1)", "Gauss(1.5,3)", "Abs(Sine)", "Abs(Cosine)", "Abs(Sine2x)"]
|
||||||
self.curve_names = ["Gauss(1,1)", "Gauss(1.5,3)"] # ,"Sine", "Cosine", "Sine2x"]
|
|
||||||
|
|
||||||
# Curves
|
|
||||||
color_list = ["#384c6b", "#e28a2b", "#5E3023", "#e41a1c", "#984e83", "#4daf4a"]
|
|
||||||
self.plot_item_1d.addLegend()
|
|
||||||
self.curves = []
|
self.curves = []
|
||||||
for ii, y_value in enumerate(self.y_value_list):
|
|
||||||
pen = mkPen(color=color_list[ii], width=2, style=QtCore.Qt.DashLine)
|
|
||||||
curve = pg.PlotDataItem(
|
|
||||||
self.x_data, y_value, pen=pen, skipFiniteCheck=True, name=self.curve_names[ii]
|
|
||||||
)
|
|
||||||
self.plot_item_1d.addItem(curve)
|
|
||||||
self.curves.append(curve)
|
|
||||||
|
|
||||||
##########################
|
##########################
|
||||||
# 2D Plot
|
# 2D Plot
|
||||||
@ -78,24 +69,13 @@ class ExampleApp(QWidget):
|
|||||||
self.table.resizeColumnsToContents()
|
self.table.resizeColumnsToContents()
|
||||||
|
|
||||||
##########################
|
##########################
|
||||||
# Signals & Cross-hairs
|
# Spinbox for N curves
|
||||||
##########################
|
##########################
|
||||||
# 1D
|
self.spin_box = QSpinBox()
|
||||||
self.crosshair_1d = Crosshair(self.plot_item_1d, precision=4)
|
self.spin_box.setMinimum(0)
|
||||||
self.crosshair_1d.coordinatesChanged1D.connect(
|
self.spin_box.setMaximum(len(self.y_value_list))
|
||||||
lambda x, y: self.update_table(self.table, x, y, column=0)
|
self.spin_box.setValue(2)
|
||||||
)
|
self.spin_box.valueChanged.connect(lambda: self.update_curves(self.spin_box.value()))
|
||||||
self.crosshair_1d.coordinatesClicked1D.connect(
|
|
||||||
lambda x, y: self.update_table(self.table, x, y, column=1)
|
|
||||||
)
|
|
||||||
# 2D
|
|
||||||
self.crosshair_2d = Crosshair(self.plot_item_2d)
|
|
||||||
self.crosshair_2d.coordinatesChanged2D.connect(
|
|
||||||
lambda x, y: self.moved_label_2d.setText(f"Mouse Moved Coordinates (2D): x={x}, y={y}")
|
|
||||||
)
|
|
||||||
self.crosshair_2d.coordinatesClicked2D.connect(
|
|
||||||
lambda x, y: self.clicked_label_2d.setText(f"Clicked Coordinates (2D): x={x}, y={y}")
|
|
||||||
)
|
|
||||||
|
|
||||||
##########################
|
##########################
|
||||||
# Adding widgets to layout
|
# Adding widgets to layout
|
||||||
@ -105,6 +85,12 @@ class ExampleApp(QWidget):
|
|||||||
self.column1 = QVBoxLayout()
|
self.column1 = QVBoxLayout()
|
||||||
self.layout.addLayout(self.column1)
|
self.layout.addLayout(self.column1)
|
||||||
|
|
||||||
|
# SpinBox
|
||||||
|
self.spin_row = QHBoxLayout()
|
||||||
|
self.column1.addLayout(self.spin_row)
|
||||||
|
self.spin_row.addWidget(QLabel("Number of curves:"))
|
||||||
|
self.spin_row.addWidget(self.spin_box)
|
||||||
|
|
||||||
# label
|
# label
|
||||||
self.clicked_label_1d = QLabel("Clicked Coordinates (1D):")
|
self.clicked_label_1d = QLabel("Clicked Coordinates (1D):")
|
||||||
self.column1.addWidget(self.clicked_label_1d)
|
self.column1.addWidget(self.clicked_label_1d)
|
||||||
@ -128,12 +114,53 @@ class ExampleApp(QWidget):
|
|||||||
# 2D plot
|
# 2D plot
|
||||||
self.column2.addWidget(self.plot_widget_2d)
|
self.column2.addWidget(self.plot_widget_2d)
|
||||||
|
|
||||||
|
self.update_curves(2) # just Gaussian curves
|
||||||
|
|
||||||
|
def hook_crosshair(self):
|
||||||
|
self.crosshair_1d = Crosshair(self.plot_item_1d, precision=10)
|
||||||
|
self.crosshair_1d.coordinatesChanged1D.connect(
|
||||||
|
lambda x, y: self.update_table(self.table, x, y, column=0)
|
||||||
|
)
|
||||||
|
self.crosshair_1d.coordinatesClicked1D.connect(
|
||||||
|
lambda x, y: self.update_table(self.table, x, y, column=1)
|
||||||
|
)
|
||||||
|
# 2D
|
||||||
|
self.crosshair_2d = Crosshair(self.plot_item_2d)
|
||||||
|
self.crosshair_2d.coordinatesChanged2D.connect(
|
||||||
|
lambda x, y: self.moved_label_2d.setText(f"Mouse Moved Coordinates (2D): x={x}, y={y}")
|
||||||
|
)
|
||||||
|
self.crosshair_2d.coordinatesClicked2D.connect(
|
||||||
|
lambda x, y: self.clicked_label_2d.setText(f"Clicked Coordinates (2D): x={x}, y={y}")
|
||||||
|
)
|
||||||
|
|
||||||
def update_table(self, table_widget, x, y_values, column):
|
def update_table(self, table_widget, x, y_values, column):
|
||||||
"""Update the table with the new coordinates"""
|
"""Update the table with the new coordinates"""
|
||||||
for i, y in enumerate(y_values):
|
for i, y in enumerate(y_values):
|
||||||
table_widget.setItem(i, column, QTableWidgetItem(f"({x}, {y})"))
|
table_widget.setItem(i, column, QTableWidgetItem(f"({x}, {y})"))
|
||||||
table_widget.resizeColumnsToContents()
|
table_widget.resizeColumnsToContents()
|
||||||
|
|
||||||
|
def update_curves(self, num_curves):
|
||||||
|
"""Update the number of curves"""
|
||||||
|
|
||||||
|
self.plot_item_1d.clear()
|
||||||
|
|
||||||
|
# Curves
|
||||||
|
color_list = ["#384c6b", "#e28a2b", "#5E3023", "#e41a1c", "#984e83", "#4daf4a"]
|
||||||
|
self.plot_item_1d.addLegend()
|
||||||
|
self.curves = []
|
||||||
|
|
||||||
|
y_value_list = self.y_value_list[:num_curves]
|
||||||
|
|
||||||
|
for ii, y_value in enumerate(y_value_list):
|
||||||
|
pen = mkPen(color=color_list[ii], width=2, style=QtCore.Qt.DashLine)
|
||||||
|
curve = pg.PlotDataItem(
|
||||||
|
self.x_data, y_value, pen=pen, skipFiniteCheck=True, name=self.curve_names[ii]
|
||||||
|
)
|
||||||
|
self.plot_item_1d.addItem(curve)
|
||||||
|
self.curves.append(curve)
|
||||||
|
|
||||||
|
self.hook_crosshair()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app = QApplication([])
|
app = QApplication([])
|
||||||
|
149
tests/test_crosshair.py
Normal file
149
tests/test_crosshair.py
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
import numpy as np
|
||||||
|
import pyqtgraph as pg
|
||||||
|
from PyQt5.QtCore import QPointF
|
||||||
|
|
||||||
|
from bec_widgets.qt_utils import Crosshair
|
||||||
|
|
||||||
|
|
||||||
|
def test_mouse_moved_lines(qtbot):
|
||||||
|
# Create a PlotWidget and add a PlotItem
|
||||||
|
plot_widget = pg.PlotWidget(title="1D PlotWidget with multiple curves")
|
||||||
|
plot_item = plot_widget.getPlotItem()
|
||||||
|
plot_item.plot([1, 2, 3], [4, 5, 6])
|
||||||
|
|
||||||
|
# Create a Crosshair instance
|
||||||
|
crosshair = Crosshair(plot_item=plot_item, precision=2)
|
||||||
|
|
||||||
|
# Connect the signals to slots that will store the emitted values
|
||||||
|
emitted_values_1D = []
|
||||||
|
crosshair.coordinatesChanged1D.connect(emitted_values_1D.append)
|
||||||
|
|
||||||
|
# Simulate a mouse moved event at a specific position
|
||||||
|
pos_in_view = QPointF(2, 5)
|
||||||
|
pos_in_scene = plot_item.vb.mapViewToScene(pos_in_view)
|
||||||
|
event_mock = [pos_in_scene]
|
||||||
|
|
||||||
|
# Call the mouse_moved method
|
||||||
|
crosshair.mouse_moved(event_mock)
|
||||||
|
|
||||||
|
# Assert the expected behavior
|
||||||
|
assert crosshair.v_line.pos().x() == 2
|
||||||
|
assert crosshair.h_line.pos().y() == 5
|
||||||
|
|
||||||
|
|
||||||
|
def test_mouse_moved_signals(qtbot):
|
||||||
|
# Create a PlotWidget and add a PlotItem
|
||||||
|
plot_widget = pg.PlotWidget(title="1D PlotWidget with multiple curves")
|
||||||
|
plot_item = plot_widget.getPlotItem()
|
||||||
|
plot_item.plot([1, 2, 3], [4, 5, 6])
|
||||||
|
|
||||||
|
# Create a Crosshair instance
|
||||||
|
crosshair = Crosshair(plot_item=plot_item, precision=2)
|
||||||
|
|
||||||
|
# Create a slot that will store the emitted values as tuples
|
||||||
|
emitted_values_1D = []
|
||||||
|
|
||||||
|
def slot(x, y_values):
|
||||||
|
emitted_values_1D.append((x, y_values))
|
||||||
|
|
||||||
|
# Connect the signal to the custom slot
|
||||||
|
crosshair.coordinatesChanged1D.connect(slot)
|
||||||
|
|
||||||
|
# Simulate a mouse moved event at a specific position
|
||||||
|
pos_in_view = QPointF(2, 5)
|
||||||
|
pos_in_scene = plot_item.vb.mapViewToScene(pos_in_view)
|
||||||
|
event_mock = [pos_in_scene]
|
||||||
|
|
||||||
|
# Call the mouse_moved method
|
||||||
|
crosshair.mouse_moved(event_mock)
|
||||||
|
|
||||||
|
# Assert the expected behavior
|
||||||
|
assert emitted_values_1D == [(2.0, [5.0])]
|
||||||
|
|
||||||
|
|
||||||
|
def test_mouse_moved_signals_outside(qtbot):
|
||||||
|
# Create a PlotWidget and add a PlotItem
|
||||||
|
plot_widget = pg.PlotWidget(title="1D PlotWidget with multiple curves")
|
||||||
|
plot_item = plot_widget.getPlotItem()
|
||||||
|
plot_item.plot([1, 2, 3], [4, 5, 6])
|
||||||
|
|
||||||
|
# Create a Crosshair instance
|
||||||
|
crosshair = Crosshair(plot_item=plot_item, precision=2)
|
||||||
|
|
||||||
|
# Create a slot that will store the emitted values as tuples
|
||||||
|
emitted_values_1D = []
|
||||||
|
|
||||||
|
def slot(x, y_values):
|
||||||
|
emitted_values_1D.append((x, y_values))
|
||||||
|
|
||||||
|
# Connect the signal to the custom slot
|
||||||
|
crosshair.coordinatesChanged1D.connect(slot)
|
||||||
|
|
||||||
|
# Simulate a mouse moved event at a specific position
|
||||||
|
pos_in_view = QPointF(22, 55)
|
||||||
|
pos_in_scene = plot_item.vb.mapViewToScene(pos_in_view)
|
||||||
|
event_mock = [pos_in_scene]
|
||||||
|
|
||||||
|
# Call the mouse_moved method
|
||||||
|
crosshair.mouse_moved(event_mock)
|
||||||
|
|
||||||
|
# Assert the expected behavior
|
||||||
|
assert emitted_values_1D == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_mouse_moved_signals_2D(qtbot):
|
||||||
|
# write similar test for 2D plot
|
||||||
|
|
||||||
|
# Create a PlotWidget and add a PlotItem
|
||||||
|
plot_widget = pg.PlotWidget(title="2D plot with crosshair and ROI square")
|
||||||
|
data_2D = np.random.random((100, 200))
|
||||||
|
plot_item = plot_widget.getPlotItem()
|
||||||
|
image_item = pg.ImageItem(data_2D)
|
||||||
|
plot_item.addItem(image_item)
|
||||||
|
# Create a Crosshair instance
|
||||||
|
crosshair = Crosshair(plot_item=plot_item)
|
||||||
|
# Create a slot that will store the emitted values as tuples
|
||||||
|
emitted_values_2D = []
|
||||||
|
|
||||||
|
def slot(x, y):
|
||||||
|
emitted_values_2D.append((x, y))
|
||||||
|
|
||||||
|
# Connect the signal to the custom slot
|
||||||
|
crosshair.coordinatesChanged2D.connect(slot)
|
||||||
|
# Simulate a mouse moved event at a specific position
|
||||||
|
pos_in_view = QPointF(22.0, 55.0)
|
||||||
|
pos_in_scene = plot_item.vb.mapViewToScene(pos_in_view)
|
||||||
|
event_mock = [pos_in_scene]
|
||||||
|
# Call the mouse_moved method
|
||||||
|
crosshair.mouse_moved(event_mock)
|
||||||
|
# Assert the expected behavior
|
||||||
|
assert emitted_values_2D == [(22.0, 55.0)]
|
||||||
|
|
||||||
|
|
||||||
|
def test_mouse_moved_signals_2D_outside(qtbot):
|
||||||
|
# write similar test for 2D plot
|
||||||
|
|
||||||
|
# Create a PlotWidget and add a PlotItem
|
||||||
|
plot_widget = pg.PlotWidget(title="2D plot with crosshair and ROI square")
|
||||||
|
data_2D = np.random.random((100, 200))
|
||||||
|
plot_item = plot_widget.getPlotItem()
|
||||||
|
image_item = pg.ImageItem(data_2D)
|
||||||
|
plot_item.addItem(image_item)
|
||||||
|
# Create a Crosshair instance
|
||||||
|
crosshair = Crosshair(plot_item=plot_item, precision=2)
|
||||||
|
# Create a slot that will store the emitted values as tuples
|
||||||
|
emitted_values_2D = []
|
||||||
|
|
||||||
|
def slot(x, y):
|
||||||
|
emitted_values_2D.append((x, y))
|
||||||
|
|
||||||
|
# Connect the signal to the custom slot
|
||||||
|
crosshair.coordinatesChanged2D.connect(slot)
|
||||||
|
# Simulate a mouse moved event at a specific position
|
||||||
|
pos_in_view = QPointF(220.0, 555.0)
|
||||||
|
pos_in_scene = plot_item.vb.mapViewToScene(pos_in_view)
|
||||||
|
event_mock = [pos_in_scene]
|
||||||
|
# Call the mouse_moved method
|
||||||
|
crosshair.mouse_moved(event_mock)
|
||||||
|
# Assert the expected behavior
|
||||||
|
assert emitted_values_2D == []
|
Reference in New Issue
Block a user