1
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2026-03-09 02:07:55 +01:00

feat: cursor universal for 1D and 2D

* in 1D snapping to the closest curve
* 2D getting int coordinates
This commit is contained in:
wyzula-jan
2023-08-10 14:30:45 +02:00
parent 5353fed7bf
commit f75554bd7b
2 changed files with 39 additions and 26 deletions

View File

@@ -1,5 +1,5 @@
import pyqtgraph as pg
import numpy as np
import pyqtgraph as pg
from PyQt5.QtCore import pyqtSignal, QObject
@@ -7,11 +7,10 @@ class Crosshair(QObject):
coordinatesChanged = pyqtSignal(float, float)
dataPointClicked = pyqtSignal(float, float)
def __init__(self, plot_item, is_image=False, parent=None):
def __init__(self, plot_item, precision=None, parent=None):
super().__init__(parent)
self.plot_item = plot_item
self.data_shape = None
self.is_image = is_image
self.precision = precision
self.v_line = pg.InfiniteLine(angle=90, movable=False)
self.h_line = pg.InfiniteLine(angle=0, movable=False)
self.plot_item.addItem(self.v_line, ignoreBounds=True)
@@ -21,26 +20,44 @@ class Crosshair(QObject):
)
self.plot_item.scene().sigMouseClicked.connect(self.mouse_clicked)
def get_data(self):
x_data, y_data = [], []
for item in self.plot_item.items:
if isinstance(item, pg.ImageItem):
return item.image, None # Return image data for 2D plot
elif isinstance(item, pg.PlotDataItem):
x_data.extend(item.xData)
y_data.extend(item.yData)
if x_data and y_data:
return np.array(x_data), np.array(y_data) # Return x and y data for 1D plot
return None, None
def mouse_moved(self, event):
pos = event[0]
if self.data_shape is not None and self.plot_item.vb.sceneBoundingRect().contains(pos):
if self.plot_item.vb.sceneBoundingRect().contains(pos):
mouse_point = self.plot_item.vb.mapSceneToView(pos)
x = int(np.clip(np.round(mouse_point.x()), 0, self.data_shape[1] - 1))
y = int(np.clip(np.round(mouse_point.y()), 0, self.data_shape[0] - 1))
x, y = self.snap_to_data(mouse_point.x(), mouse_point.y())
self.v_line.setPos(x)
self.h_line.setPos(y)
self.coordinatesChanged.emit(x, y)
def mouse_clicked(self, event):
if self.data_shape is not None and 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)
x = int(np.clip(np.round(mouse_point.x()), 0, self.data_shape[1] - 1))
y = int(np.clip(np.round(mouse_point.y()), 0, self.data_shape[0] - 1))
self.v_line.setPos(x)
self.h_line.setPos(y)
x, y = self.snap_to_data(mouse_point.x(), mouse_point.y())
self.dataPointClicked.emit(x, y)
def set_data_shape(self, shape):
self.data_shape = shape
def snap_to_data(self, x, y):
x_data, y_data = self.get_data()
if x_data is not None and y_data is not None:
distance = (x_data - x) ** 2 + (y_data - y) ** 2
index = np.argmin(distance)
x, y = x_data[index], y_data[index]
if self.precision is not None:
x, y = round(x, self.precision), round(y, self.precision)
return x, y
elif x_data is not None and y_data is None: # For 2D plot (ImageItem)
x_idx = int(np.clip(x, 0, x_data.shape[1] - 1))
y_idx = int(np.clip(y, 0, x_data.shape[0] - 1))
return x_idx, y_idx
return x, y

View File

@@ -19,14 +19,13 @@ label_1d_move = win.addLabel("1D move label", row=0, col=0)
label_1d_click = win.addLabel("1D click label", row=1, col=0)
plot_item_1d = win.addPlot(row=2, col=0)
x_data = np.linspace(0, 10, 1000)
y_data = np.sin(x_data)
plot_item_1d.plot(x_data, y_data)
#
crosshair_1d = add_crosshair(plot_item_1d)
crosshair_1d.set_data_shape((len(y_data), len(x_data)))
y_data_sine = np.sin(x_data)
y_data_cosine = np.cos(x_data)
plot_item_1d.plot(x_data, y_data_sine)
plot_item_1d.plot(x_data, y_data_cosine)
crosshair_1d = Crosshair(plot_item_1d, precision=2)
#
def on_coordinates_changed_1d(x, y):
label_1d_move.setText(f"1D Moved: ({x}, {y})")
@@ -35,7 +34,6 @@ def on_data_point_clicked_1d(x, y):
label_1d_click.setText(f"1D Clicked: ({x}, {y})")
#
crosshair_1d.coordinatesChanged.connect(on_coordinates_changed_1d)
crosshair_1d.dataPointClicked.connect(on_data_point_clicked_1d)
@@ -50,11 +48,9 @@ img = np.random.normal(size=(100, 100))
image_item = pg.ImageItem(img)
plot_item_2d.addItem(image_item)
crosshair_2d = add_crosshair(plot_item_2d, is_image=True)
crosshair_2d.set_data_shape(img.shape)
crosshair_2d = Crosshair(plot_item_2d, precision=2)
#
def on_coordinates_changed_2d(x, y):
label_2d_move.setText(f"2D Moved: ({x}, {y})")