|
|
|
@@ -14,6 +14,33 @@ from .conftest import create_widget
|
|
|
|
|
# pylint: disable = redefined-outer-name
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _FakeClickEvent:
|
|
|
|
|
"""Minimal public-API stand-in for a pyqtgraph MouseClickEvent."""
|
|
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
|
self,
|
|
|
|
|
scene_pos: QPointF,
|
|
|
|
|
button: Qt.MouseButton = Qt.MouseButton.LeftButton,
|
|
|
|
|
double: bool = False,
|
|
|
|
|
):
|
|
|
|
|
self._scene_pos = scene_pos
|
|
|
|
|
self._button = button
|
|
|
|
|
self._double = double
|
|
|
|
|
self.accepted = False
|
|
|
|
|
|
|
|
|
|
def button(self):
|
|
|
|
|
return self._button
|
|
|
|
|
|
|
|
|
|
def scenePos(self):
|
|
|
|
|
return self._scene_pos
|
|
|
|
|
|
|
|
|
|
def double(self):
|
|
|
|
|
return self._double
|
|
|
|
|
|
|
|
|
|
def accept(self):
|
|
|
|
|
self.accepted = True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def plot_widget_with_crosshair(qtbot):
|
|
|
|
|
widget = pg.PlotWidget()
|
|
|
|
@@ -22,6 +49,7 @@ def plot_widget_with_crosshair(qtbot):
|
|
|
|
|
|
|
|
|
|
widget.plot(x=[1, 2, 3], y=[4, 5, 6], name="Curve 1")
|
|
|
|
|
plot_item = widget.getPlotItem()
|
|
|
|
|
plot_item.vb.setRange(xRange=(0, 4), yRange=(0, 10), padding=0)
|
|
|
|
|
crosshair = Crosshair(plot_item=plot_item, precision=3)
|
|
|
|
|
|
|
|
|
|
yield crosshair, plot_item
|
|
|
|
@@ -38,20 +66,17 @@ def image_widget_with_crosshair(qtbot):
|
|
|
|
|
|
|
|
|
|
widget.addItem(image_item)
|
|
|
|
|
plot_item = widget.getPlotItem()
|
|
|
|
|
plot_item.vb.setRange(xRange=(0, 100), yRange=(0, 100), padding=0)
|
|
|
|
|
crosshair = Crosshair(plot_item=plot_item, precision=3)
|
|
|
|
|
|
|
|
|
|
yield crosshair, plot_item
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_mouse_moved_lines(plot_widget_with_crosshair):
|
|
|
|
|
crosshair, plot_item = plot_widget_with_crosshair
|
|
|
|
|
|
|
|
|
|
pos_in_view = QPointF(2, 5)
|
|
|
|
|
pos_in_scene = plot_item.vb.mapViewToScene(pos_in_view)
|
|
|
|
|
event_mock = [pos_in_scene]
|
|
|
|
|
crosshair, _ = plot_widget_with_crosshair
|
|
|
|
|
|
|
|
|
|
# Simulate mouse movement
|
|
|
|
|
crosshair.mouse_moved(event_mock)
|
|
|
|
|
crosshair.mouse_moved(manual_pos=(2, 5))
|
|
|
|
|
|
|
|
|
|
# Check that the vertical line is indeed at x=2
|
|
|
|
|
assert np.isclose(crosshair.v_line.pos().x(), 2)
|
|
|
|
@@ -59,7 +84,7 @@ def test_mouse_moved_lines(plot_widget_with_crosshair):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_mouse_moved_signals(plot_widget_with_crosshair):
|
|
|
|
|
crosshair, plot_item = plot_widget_with_crosshair
|
|
|
|
|
crosshair, _ = plot_widget_with_crosshair
|
|
|
|
|
|
|
|
|
|
emitted_values_1D = []
|
|
|
|
|
|
|
|
|
@@ -68,43 +93,40 @@ def test_mouse_moved_signals(plot_widget_with_crosshair):
|
|
|
|
|
|
|
|
|
|
crosshair.coordinatesChanged1D.connect(slot)
|
|
|
|
|
|
|
|
|
|
pos_in_view = QPointF(2, 5)
|
|
|
|
|
pos_in_scene = plot_item.vb.mapViewToScene(pos_in_view)
|
|
|
|
|
event_mock = [pos_in_scene]
|
|
|
|
|
|
|
|
|
|
crosshair.mouse_moved(event_mock)
|
|
|
|
|
crosshair.mouse_moved(manual_pos=(2, 5))
|
|
|
|
|
|
|
|
|
|
# Assert the expected behavior
|
|
|
|
|
assert emitted_values_1D == [("Curve 1", 2, 5)]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_mouse_moved_signals_outside(plot_widget_with_crosshair):
|
|
|
|
|
crosshair, plot_item = plot_widget_with_crosshair
|
|
|
|
|
crosshair, _ = plot_widget_with_crosshair
|
|
|
|
|
|
|
|
|
|
# Create a slot that will store the emitted values as tuples
|
|
|
|
|
emitted_values_1D = []
|
|
|
|
|
emitted_positions = []
|
|
|
|
|
|
|
|
|
|
def slot(coordinates):
|
|
|
|
|
emitted_values_1D.append(coordinates)
|
|
|
|
|
|
|
|
|
|
# Connect the signal to the custom slot
|
|
|
|
|
crosshair.coordinatesChanged1D.connect(slot)
|
|
|
|
|
crosshair.crosshairChanged.connect(emitted_positions.append)
|
|
|
|
|
|
|
|
|
|
# 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)
|
|
|
|
|
crosshair.mouse_moved(manual_pos=(2, 5))
|
|
|
|
|
emitted_positions.clear()
|
|
|
|
|
emitted_values_1D.clear()
|
|
|
|
|
crosshair.mouse_moved(manual_pos=(22, 55))
|
|
|
|
|
|
|
|
|
|
# Assert the expected behavior
|
|
|
|
|
assert emitted_values_1D == []
|
|
|
|
|
assert emitted_positions == []
|
|
|
|
|
assert np.isclose(crosshair.v_line.pos().x(), 2)
|
|
|
|
|
assert np.isclose(crosshair.h_line.pos().y(), 5)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_mouse_moved_signals_2D(image_widget_with_crosshair):
|
|
|
|
|
crosshair, plot_item = image_widget_with_crosshair
|
|
|
|
|
image_item = plot_item.items[0]
|
|
|
|
|
crosshair, _ = image_widget_with_crosshair
|
|
|
|
|
|
|
|
|
|
emitted_values_2D = []
|
|
|
|
|
|
|
|
|
@@ -113,17 +135,16 @@ def test_mouse_moved_signals_2D(image_widget_with_crosshair):
|
|
|
|
|
|
|
|
|
|
crosshair.coordinatesChanged2D.connect(slot)
|
|
|
|
|
|
|
|
|
|
pos_in_view = QPointF(21.0, 55.0)
|
|
|
|
|
pos_in_scene = plot_item.vb.mapViewToScene(pos_in_view)
|
|
|
|
|
event_mock = [pos_in_scene]
|
|
|
|
|
|
|
|
|
|
crosshair.mouse_moved(event_mock)
|
|
|
|
|
crosshair.mouse_moved(manual_pos=(21.0, 55.0))
|
|
|
|
|
|
|
|
|
|
assert emitted_values_2D == [("ImageItem", 21, 55)]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_mouse_moved_signals_2D_outside(image_widget_with_crosshair):
|
|
|
|
|
def test_mouse_moved_signals_2D_outside_image_bounds_clamps_inside_view_range(
|
|
|
|
|
image_widget_with_crosshair,
|
|
|
|
|
):
|
|
|
|
|
crosshair, plot_item = image_widget_with_crosshair
|
|
|
|
|
plot_item.vb.setRange(xRange=(0, 300), yRange=(0, 600), padding=0)
|
|
|
|
|
|
|
|
|
|
emitted_values_2D = []
|
|
|
|
|
|
|
|
|
@@ -132,23 +153,34 @@ def test_mouse_moved_signals_2D_outside(image_widget_with_crosshair):
|
|
|
|
|
|
|
|
|
|
crosshair.coordinatesChanged2D.connect(slot)
|
|
|
|
|
|
|
|
|
|
pos_in_view = QPointF(220.0, 555.0)
|
|
|
|
|
pos_in_scene = plot_item.vb.mapViewToScene(pos_in_view)
|
|
|
|
|
event_mock = [pos_in_scene]
|
|
|
|
|
crosshair.mouse_moved(manual_pos=(220.0, 555.0))
|
|
|
|
|
|
|
|
|
|
crosshair.mouse_moved(event_mock)
|
|
|
|
|
assert emitted_values_2D == [("ImageItem", 99, 99)]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_mouse_moved_signals_2D_outside_view_range_ignored(image_widget_with_crosshair):
|
|
|
|
|
crosshair, _ = image_widget_with_crosshair
|
|
|
|
|
|
|
|
|
|
emitted_values_2D = []
|
|
|
|
|
emitted_positions = []
|
|
|
|
|
|
|
|
|
|
crosshair.coordinatesChanged2D.connect(emitted_values_2D.append)
|
|
|
|
|
crosshair.crosshairChanged.connect(emitted_positions.append)
|
|
|
|
|
|
|
|
|
|
crosshair.mouse_moved(manual_pos=(21.0, 55.0))
|
|
|
|
|
emitted_positions.clear()
|
|
|
|
|
emitted_values_2D.clear()
|
|
|
|
|
crosshair.mouse_moved(manual_pos=(220.0, 555.0))
|
|
|
|
|
|
|
|
|
|
assert emitted_values_2D == []
|
|
|
|
|
assert emitted_positions == []
|
|
|
|
|
assert np.isclose(crosshair.v_line.pos().x(), 21.0)
|
|
|
|
|
assert np.isclose(crosshair.h_line.pos().y(), 55.0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_marker_positions_after_mouse_move(plot_widget_with_crosshair):
|
|
|
|
|
crosshair, plot_item = plot_widget_with_crosshair
|
|
|
|
|
|
|
|
|
|
pos_in_view = QPointF(2, 5)
|
|
|
|
|
pos_in_scene = plot_item.vb.mapViewToScene(pos_in_view)
|
|
|
|
|
event_mock = [pos_in_scene]
|
|
|
|
|
|
|
|
|
|
crosshair.mouse_moved(event_mock)
|
|
|
|
|
crosshair, _ = plot_widget_with_crosshair
|
|
|
|
|
crosshair.mouse_moved(manual_pos=(2, 5))
|
|
|
|
|
|
|
|
|
|
marker = crosshair.marker_moved_1d["Curve 1"]
|
|
|
|
|
marker_x, marker_y = marker.getData()
|
|
|
|
@@ -172,7 +204,7 @@ def test_scale_emitted_coordinates(plot_widget_with_crosshair):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_crosshair_changed_signal(plot_widget_with_crosshair):
|
|
|
|
|
crosshair, plot_item = plot_widget_with_crosshair
|
|
|
|
|
crosshair, _ = plot_widget_with_crosshair
|
|
|
|
|
|
|
|
|
|
emitted_positions = []
|
|
|
|
|
|
|
|
|
@@ -181,11 +213,7 @@ def test_crosshair_changed_signal(plot_widget_with_crosshair):
|
|
|
|
|
|
|
|
|
|
crosshair.crosshairChanged.connect(slot)
|
|
|
|
|
|
|
|
|
|
pos_in_view = QPointF(2, 5)
|
|
|
|
|
pos_in_scene = plot_item.vb.mapViewToScene(pos_in_view)
|
|
|
|
|
event_mock = [pos_in_scene]
|
|
|
|
|
|
|
|
|
|
crosshair.mouse_moved(event_mock)
|
|
|
|
|
crosshair.mouse_moved(manual_pos=(2, 5))
|
|
|
|
|
|
|
|
|
|
x, y = emitted_positions[0]
|
|
|
|
|
|
|
|
|
@@ -193,33 +221,33 @@ def test_crosshair_changed_signal(plot_widget_with_crosshair):
|
|
|
|
|
assert np.isclose(y, 5)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_crosshair_clicked_signal(qtbot, plot_widget_with_crosshair):
|
|
|
|
|
def test_crosshair_clicked_signal(plot_widget_with_crosshair):
|
|
|
|
|
crosshair, plot_item = plot_widget_with_crosshair
|
|
|
|
|
|
|
|
|
|
emitted_positions = []
|
|
|
|
|
emitted_view_positions = []
|
|
|
|
|
|
|
|
|
|
def slot(position):
|
|
|
|
|
emitted_positions.append(position)
|
|
|
|
|
|
|
|
|
|
crosshair.crosshairClicked.connect(slot)
|
|
|
|
|
crosshair.positionClicked.connect(emitted_view_positions.append)
|
|
|
|
|
|
|
|
|
|
x_data = 2
|
|
|
|
|
y_data = 5
|
|
|
|
|
crosshair.is_log_x = True
|
|
|
|
|
crosshair.is_log_y = True
|
|
|
|
|
plot_item.vb.setRange(xRange=(0, 1), yRange=(0, 1), padding=0)
|
|
|
|
|
|
|
|
|
|
# Map data coordinates to scene coordinates
|
|
|
|
|
pos_in_scene = plot_item.vb.mapViewToScene(QPointF(x_data, y_data))
|
|
|
|
|
# Map scene coordinates to widget coordinates
|
|
|
|
|
graphics_view = plot_item.vb.scene().views()[0]
|
|
|
|
|
qtbot.waitExposed(graphics_view)
|
|
|
|
|
pos_in_widget = graphics_view.mapFromScene(pos_in_scene)
|
|
|
|
|
|
|
|
|
|
# Simulate mouse click
|
|
|
|
|
qtbot.mouseClick(graphics_view.viewport(), Qt.LeftButton, pos=pos_in_widget)
|
|
|
|
|
known_view_point = QPointF(np.log10(2), np.log10(5))
|
|
|
|
|
pos_in_scene = plot_item.vb.mapViewToScene(known_view_point)
|
|
|
|
|
crosshair.mouse_clicked(_FakeClickEvent(pos_in_scene))
|
|
|
|
|
|
|
|
|
|
x, y = emitted_positions[0]
|
|
|
|
|
view_x, view_y = emitted_view_positions[0]
|
|
|
|
|
|
|
|
|
|
assert np.isclose(round(x, 1), 2)
|
|
|
|
|
assert np.isclose(round(y, 1), 5)
|
|
|
|
|
assert np.isclose(x, 2)
|
|
|
|
|
assert np.isclose(y, 5)
|
|
|
|
|
assert np.isclose(view_x, known_view_point.x())
|
|
|
|
|
assert np.isclose(view_y, known_view_point.y())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_update_coord_label_1D(plot_widget_with_crosshair):
|
|
|
|
@@ -388,21 +416,18 @@ def test_ignore_invisible_curves_on_move(qtbot, mocked_client):
|
|
|
|
|
c0 = wf.plot(x=[1, 2, 3], y=[1, 4, 9], name="Curve_0")
|
|
|
|
|
c1 = wf.plot(x=[1, 2, 3], y=[2, 5, 10], name="Curve_1")
|
|
|
|
|
wf.hook_crosshair()
|
|
|
|
|
wf.crosshair.plot_item.vb.setRange(xRange=(0, 4), yRange=(0, 10), padding=0)
|
|
|
|
|
|
|
|
|
|
# # Simulate a mouse move at (2,5)
|
|
|
|
|
pos_in_view = QPointF(2, 5)
|
|
|
|
|
pos_in_scene = wf.plot_item.vb.mapViewToScene(pos_in_view)
|
|
|
|
|
event_mock = [pos_in_scene]
|
|
|
|
|
|
|
|
|
|
# 1) Both curves visible: expect markers for both
|
|
|
|
|
wf.crosshair.clear_markers()
|
|
|
|
|
wf.crosshair.mouse_moved(event_mock)
|
|
|
|
|
wf.crosshair.mouse_moved(manual_pos=(2, 5))
|
|
|
|
|
assert set(wf.crosshair.marker_moved_1d.keys()) == {"Curve_0", "Curve_1"}
|
|
|
|
|
|
|
|
|
|
# 2) Hide Curve B and repeat: only Curve_0 should remain
|
|
|
|
|
c1.setVisible(False)
|
|
|
|
|
wf.crosshair.clear_markers()
|
|
|
|
|
wf.crosshair.mouse_moved(event_mock)
|
|
|
|
|
wf.crosshair.mouse_moved(manual_pos=(2, 5))
|
|
|
|
|
qtbot.wait(200)
|
|
|
|
|
assert set(wf.crosshair.marker_moved_1d.keys()) == {"Curve_0"}
|
|
|
|
|
|
|
|
|
@@ -412,25 +437,6 @@ def test_ignore_invisible_curves_on_move(qtbot, mocked_client):
|
|
|
|
|
###############################################
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _FakeClickEvent:
|
|
|
|
|
"""Minimal stand-in for a pyqtgraph MouseClickEvent for routing tests."""
|
|
|
|
|
|
|
|
|
|
def __init__(self, scene_pos, button=Qt.LeftButton, double=False):
|
|
|
|
|
self._scenePos = scene_pos
|
|
|
|
|
self._button = button
|
|
|
|
|
self._double = double
|
|
|
|
|
self.accepted = False
|
|
|
|
|
|
|
|
|
|
def button(self):
|
|
|
|
|
return self._button
|
|
|
|
|
|
|
|
|
|
def double(self):
|
|
|
|
|
return self._double
|
|
|
|
|
|
|
|
|
|
def accept(self):
|
|
|
|
|
self.accepted = True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_set_pin_1d_creates_marker_and_emits(plot_widget_with_crosshair):
|
|
|
|
|
crosshair, _ = plot_widget_with_crosshair
|
|
|
|
|
pinned = []
|
|
|
|
|