mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-14 03:31:50 +02:00
fix: crosshair snaps to correct coordinates also with logx and logy
This commit is contained in:
@ -13,6 +13,8 @@ class Crosshair(QObject):
|
|||||||
|
|
||||||
def __init__(self, plot_item, precision=None, parent=None):
|
def __init__(self, plot_item, precision=None, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
self.is_log_y = None
|
||||||
|
self.is_log_x = None
|
||||||
self.plot_item = plot_item
|
self.plot_item = plot_item
|
||||||
self.precision = precision
|
self.precision = precision
|
||||||
self.v_line = pg.InfiniteLine(angle=90, movable=False)
|
self.v_line = pg.InfiniteLine(angle=90, movable=False)
|
||||||
@ -62,9 +64,6 @@ class Crosshair(QObject):
|
|||||||
for x_data, y_data in data:
|
for x_data, y_data in data:
|
||||||
closest_x, closest_y = self.closest_x_y_value(x, x_data, y_data)
|
closest_x, closest_y = self.closest_x_y_value(x, x_data, y_data)
|
||||||
y_values.append(closest_y)
|
y_values.append(closest_y)
|
||||||
if self.precision is not None:
|
|
||||||
x = round(x, self.precision)
|
|
||||||
y_values = [round(y_val, self.precision) for y_val in y_values]
|
|
||||||
return x, y_values
|
return x, y_values
|
||||||
elif isinstance(data[0], np.ndarray): # 2D plot
|
elif isinstance(data[0], np.ndarray): # 2D plot
|
||||||
x_idx = int(np.clip(x, 0, data[0].shape[0] - 1))
|
x_idx = int(np.clip(x, 0, data[0].shape[0] - 1))
|
||||||
@ -89,27 +88,54 @@ 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):
|
||||||
|
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):
|
||||||
mouse_point = self.plot_item.vb.mapSceneToView(pos)
|
mouse_point = self.plot_item.vb.mapSceneToView(pos)
|
||||||
x, y_values = self.snap_to_data(mouse_point.x(), mouse_point.y())
|
x, y = mouse_point.x(), mouse_point.y()
|
||||||
|
if self.is_log_x:
|
||||||
|
x = 10**x
|
||||||
|
if self.is_log_y:
|
||||||
|
y = 10**y
|
||||||
|
x, y_values = self.snap_to_data(x, y)
|
||||||
self.v_line.setPos(mouse_point.x())
|
self.v_line.setPos(mouse_point.x())
|
||||||
self.h_line.setPos(mouse_point.y())
|
self.h_line.setPos(mouse_point.y())
|
||||||
if isinstance(y_values, list): # 1D plot
|
if isinstance(y_values, list): # 1D plot
|
||||||
self.coordinatesChanged1D.emit(x, y_values)
|
self.coordinatesChanged1D.emit(
|
||||||
|
round(x, self.precision), [round(y_val, self.precision) for y_val in y_values]
|
||||||
|
)
|
||||||
for i, y_val in enumerate(y_values):
|
for i, y_val in enumerate(y_values):
|
||||||
self.marker_moved_1d[i].setData([x], [y_val])
|
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)],
|
||||||
|
)
|
||||||
else: # 2D plot
|
else: # 2D plot
|
||||||
self.coordinatesChanged2D.emit(x, y_values)
|
self.coordinatesChanged2D.emit(x, y_values)
|
||||||
|
|
||||||
def mouse_clicked(self, event):
|
def mouse_clicked(self, event):
|
||||||
|
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_values = self.snap_to_data(mouse_point.x(), mouse_point.y())
|
x, y = mouse_point.x(), mouse_point.y()
|
||||||
|
if self.is_log_x:
|
||||||
|
x = 10**x
|
||||||
|
if self.is_log_y:
|
||||||
|
y = 10**y
|
||||||
|
x, y_values = self.snap_to_data(x, y)
|
||||||
if isinstance(y_values, list): # 1D plot
|
if isinstance(y_values, list): # 1D plot
|
||||||
self.coordinatesClicked1D.emit(x, y_values)
|
self.coordinatesClicked1D.emit(
|
||||||
|
round(x, self.precision), [round(y_val, self.precision) for y_val in y_values]
|
||||||
|
)
|
||||||
for i, y_val in enumerate(y_values):
|
for i, y_val in enumerate(y_values):
|
||||||
self.marker_clicked_1d[i].setData([x], [y_val])
|
self.marker_clicked_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)],
|
||||||
|
)
|
||||||
else: # 2D plot
|
else: # 2D plot
|
||||||
self.coordinatesClicked2D.emit(x, y_values)
|
self.coordinatesClicked2D.emit(x, y_values)
|
||||||
self.marker_2d.setPos([x, y_values])
|
self.marker_2d.setPos([x, y_values])
|
||||||
|
|
||||||
|
def check_log(self):
|
||||||
|
# Check if plot uses log scale
|
||||||
|
self.is_log_x = self.plot_item.ctrl.logXCheck.isChecked()
|
||||||
|
self.is_log_y = self.plot_item.ctrl.logYCheck.isChecked()
|
||||||
|
@ -29,7 +29,7 @@ class ExampleApp(QWidget):
|
|||||||
# PlotWidget
|
# PlotWidget
|
||||||
self.plot_widget_1d = pg.PlotWidget(title="1D PlotWidget with multiple curves")
|
self.plot_widget_1d = pg.PlotWidget(title="1D PlotWidget with multiple curves")
|
||||||
self.plot_item_1d = self.plot_widget_1d.getPlotItem()
|
self.plot_item_1d = self.plot_widget_1d.getPlotItem()
|
||||||
self.plot_item_1d.setLogMode(True, False)
|
self.plot_item_1d.setLogMode(True, True)
|
||||||
|
|
||||||
# 1D Datasets
|
# 1D Datasets
|
||||||
self.x_data = np.linspace(0, 10, 1000)
|
self.x_data = np.linspace(0, 10, 1000)
|
||||||
@ -37,9 +37,6 @@ class ExampleApp(QWidget):
|
|||||||
def gauss(x, mu, sigma):
|
def gauss(x, mu, sigma):
|
||||||
return (1 / (sigma * np.sqrt(2 * np.pi))) * np.exp(-0.5 * ((x - mu) / sigma) ** 2)
|
return (1 / (sigma * np.sqrt(2 * np.pi))) * np.exp(-0.5 * ((x - mu) / sigma) ** 2)
|
||||||
|
|
||||||
mu = 1 # mean
|
|
||||||
sigma = 1 # standard deviation
|
|
||||||
|
|
||||||
# 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),
|
np.sin(self.x_data),
|
||||||
@ -47,8 +44,8 @@ class ExampleApp(QWidget):
|
|||||||
np.sin(2 * self.x_data),
|
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, mu, sigma)]
|
self.y_value_list = [gauss(self.x_data, 1, 1), gauss(self.x_data, 1.5, 3)]
|
||||||
self.curve_names = ["Gauss"] # ,"Sine", "Cosine", "Sine2x"]
|
self.curve_names = ["Gauss(1,1)", "Gauss(1.5,3)"] # ,"Sine", "Cosine", "Sine2x"]
|
||||||
|
|
||||||
# Curves
|
# Curves
|
||||||
color_list = ["#384c6b", "#e28a2b", "#5E3023", "#e41a1c", "#984e83", "#4daf4a"]
|
color_list = ["#384c6b", "#e28a2b", "#5E3023", "#e41a1c", "#984e83", "#4daf4a"]
|
||||||
@ -66,7 +63,7 @@ class ExampleApp(QWidget):
|
|||||||
# 2D Plot
|
# 2D Plot
|
||||||
##########################
|
##########################
|
||||||
self.plot_widget_2d = pg.PlotWidget(title="2D plot with crosshair and ROI square")
|
self.plot_widget_2d = pg.PlotWidget(title="2D plot with crosshair and ROI square")
|
||||||
self.data_2D = np.random.random((100, 100))
|
self.data_2D = np.random.random((100, 200))
|
||||||
self.plot_item_2d = self.plot_widget_2d.getPlotItem()
|
self.plot_item_2d = self.plot_widget_2d.getPlotItem()
|
||||||
self.image_item = pg.ImageItem(self.data_2D)
|
self.image_item = pg.ImageItem(self.data_2D)
|
||||||
self.plot_item_2d.addItem(self.image_item)
|
self.plot_item_2d.addItem(self.image_item)
|
||||||
@ -83,7 +80,7 @@ class ExampleApp(QWidget):
|
|||||||
# Signals & Cross-hairs
|
# Signals & Cross-hairs
|
||||||
##########################
|
##########################
|
||||||
# 1D
|
# 1D
|
||||||
self.crosshair_1d = Crosshair(self.plot_item_1d, precision=2)
|
self.crosshair_1d = Crosshair(self.plot_item_1d, precision=4)
|
||||||
self.crosshair_1d.coordinatesChanged1D.connect(
|
self.crosshair_1d.coordinatesChanged1D.connect(
|
||||||
lambda x, y: self.update_table(self.table, x, y, column=0)
|
lambda x, y: self.update_table(self.table, x, y, column=0)
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user