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:
@@ -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
|
||||
|
||||
@@ -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})")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user