Skip to content

Commit

Permalink
add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
tlambert03 committed Jul 10, 2024
1 parent 5e9b6d6 commit 480cbdd
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,37 +21,37 @@ def __init__(
) -> None:
super().__init__(parent=parent)

self.selector = RelativePointPlanSelector()
self._selector = RelativePointPlanSelector()
# graphics scene to draw the well and the fovs
self.well_view = WellView()
self._well_view = WellView()

# main
layout = QHBoxLayout(self)
layout.addWidget(self.selector, 1)
layout.addWidget(self.well_view, 2)
layout.addWidget(self._selector, 1)
layout.addWidget(self._well_view, 2)

# connect
self.selector.valueChanged.connect(self._on_selector_value_changed)
self.well_view.maxPointsDetected.connect(self._on_view_max_points_detected)
self.well_view.positionClicked.connect(self._on_view_position_clicked)
self._selector.valueChanged.connect(self._on_selector_value_changed)
self._well_view.maxPointsDetected.connect(self._on_view_max_points_detected)
self._well_view.positionClicked.connect(self._on_view_position_clicked)

if plan is not None:
self.setValue(plan)

def value(self) -> useq.RelativeMultiPointPlan:
return self.selector.value()
return self._selector.value()

def setValue(self, plan: useq.RelativeMultiPointPlan) -> None:
self.selector.setValue(plan)
self._selector.setValue(plan)

def _on_selector_value_changed(self, value: useq.RelativeMultiPointPlan) -> None:
self.well_view.setPointsPlan(value)
self._well_view.setPointsPlan(value)
self.valueChanged.emit(value)

def _on_view_max_points_detected(self, value: int) -> None:
self.selector.random_points_wdg.num_points.setValue(value)
self._selector.random_points_wdg.num_points.setValue(value)

def _on_view_position_clicked(self, position: useq.RelativePosition) -> None:
if self.selector.active_plan_type is useq.RandomPoints:
if self._selector.active_plan_type is useq.RandomPoints:
pos_no_name = position.model_copy(update={"name": ""})
self.selector.random_points_wdg.start_at = pos_no_name
self._selector.random_points_wdg.start_at = pos_no_name
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from pymmcore_widgets._util import ResizingGraphicsView

if TYPE_CHECKING:
from PyQt6.QtGui import QMouseEvent
from qtpy.QtGui import QMouseEvent

DATA_POSITION = 1

Expand Down Expand Up @@ -50,6 +50,11 @@ def __init__(self, parent: QWidget | None = None) -> None:
def sizeHint(self) -> QSize:
return QSize(500, 500)

def setWellSize(self, width_mm: float | None, height_mm: float | None) -> None:
"""Set the well size width and height in mm."""
self._well_width_um = (width_mm * 1000) if width_mm else None
self._well_height_um = (height_mm * 1000) if height_mm else None

def setPointsPlan(self, plan: useq.RelativeMultiPointPlan) -> None:
"""Set the plan to use to draw the FOVs."""
self._fov_width_um = plan.fov_width
Expand All @@ -63,21 +68,6 @@ def setPointsPlan(self, plan: useq.RelativeMultiPointPlan) -> None:
# DRAW FOVS
self._draw_fovs(plan)

def mousePressEvent(self, event: QMouseEvent | None) -> None:
if event is None:
return
scene_pos = self.mapToScene(event.pos())
items = self.scene().items(scene_pos)
for item in items:
if pos := item.data(DATA_POSITION):
self.positionClicked.emit(pos)
break

def setWellSize(self, width_mm: float | None, height_mm: float | None) -> None:
"""Set the well size width and height in mm."""
self._well_width_um = (width_mm * 1000) if width_mm else None
self._well_height_um = (height_mm * 1000) if height_mm else None

def _draw_outline(self) -> None:
"""Draw the outline of the well area."""
if self._outline_item:
Expand Down Expand Up @@ -191,3 +181,13 @@ def _scaled_pen_size(self) -> int:
def _resize_to_fit(self) -> None:
self.setSceneRect(self._scene.itemsBoundingRect())
self.resizeEvent(None)

def mousePressEvent(self, event: QMouseEvent | None) -> None:
if event is not None:
scene_pos = self.mapToScene(event.pos())
print(scene_pos)
items = self.scene().items(scene_pos)
for item in items:
if pos := item.data(DATA_POSITION):
self.positionClicked.emit(pos)
break
99 changes: 98 additions & 1 deletion tests/useq_widgets/test_useq_points_plans.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

from typing import TYPE_CHECKING

from useq import GridRowsColumns, OrderMode, RandomPoints, RelativePosition
import pytest
import useq
from qtpy.QtCore import Qt
from qtpy.QtGui import QMouseEvent
from useq import GridRowsColumns, OrderMode, RandomPoints, RelativePosition, Shape

from pymmcore_widgets.useq_widgets import points_plans as pp

Expand Down Expand Up @@ -31,6 +35,8 @@
relative_to="top_left",
)

RELATIVE_POSITION = RelativePosition()


def test_random_points_widget(qtbot: QtBot) -> None:
wdg = pp.RandomPointWidget()
Expand Down Expand Up @@ -86,6 +92,10 @@ def test_point_plan_selector(qtbot: QtBot) -> None:
assert wdg.value() == RANDOM_POINTS
assert wdg.random_radio_btn.isChecked()

wdg.setValue(RELATIVE_POSITION)
assert wdg.value() == RELATIVE_POSITION
assert wdg.single_radio_btn.isChecked()

wdg.setValue(GRID_ROWS_COLS)
assert wdg.value() == GRID_ROWS_COLS
assert wdg.grid_radio_btn.isChecked()
Expand All @@ -99,3 +109,90 @@ def test_point_plan_selector(qtbot: QtBot) -> None:
"fov_height": GRID_ROWS_COLS.fov_height,
}
)


def test_points_plan_widget(qtbot: QtBot) -> None:
"""PointsPlanWidget is a RelativePointPlanSelector combined with a graphics view."""
wdg = pp.PointsPlanWidget()
wdg.show()
qtbot.addWidget(wdg)

for plan in (RANDOM_POINTS, RELATIVE_POSITION, GRID_ROWS_COLS):
with qtbot.waitSignal(wdg.valueChanged):
wdg.setValue(plan)
assert wdg.value() == plan


@pytest.mark.parametrize(
"plan",
[
RELATIVE_POSITION,
GRID_ROWS_COLS,
RANDOM_POINTS,
RANDOM_POINTS.replace(shape=Shape.ELLIPSE),
RANDOM_POINTS.replace(fov_width=None),
RANDOM_POINTS.replace(fov_width=None, fov_height=None),
],
)
def test_points_plan_variants(plan: useq.RelativeMultiPointPlan, qtbot: QtBot) -> None:
"""Test PointsPlanWidget with different plan types."""
wdg = pp.PointsPlanWidget(plan)
wdg.show()
qtbot.addWidget(wdg)
# make sure the view can also render without a well size
wdg._well_view.setWellSize(None, None)
wdg._well_view.setPointsPlan(plan)
assert wdg.value() == plan


def test_clicking_point_changes_first_position(qtbot: QtBot) -> None:
plan = RandomPoints(
num_points=20,
random_seed=0,
fov_width=500,
fov_height=500,
max_width=1000,
max_height=1000,
)
wdg = pp.PointsPlanWidget(plan)
wdg.show()
qtbot.addWidget(wdg)

assert isinstance(wdg.value().start_at, int)

# clicking on a point should change the start_at position
event = QMouseEvent(
QMouseEvent.Type.MouseButtonPress,
wdg._well_view.mapFromScene(0, 0).toPointF(),
Qt.MouseButton.LeftButton,
Qt.MouseButton.LeftButton,
Qt.KeyboardModifier.NoModifier,
)
wdg._well_view.mousePressEvent(event)

new_val = wdg.value()
assert isinstance(new_val.start_at, useq.RelativePosition)
rounded = round(new_val.start_at)
# feel free to relax this if it ever fails tests
assert rounded.x == 122
assert rounded.y == -50


def test_max_points_detected(qtbot: QtBot) -> None:
plan = RandomPoints(
num_points=20,
random_seed=0,
fov_width=500,
fov_height=500,
max_width=1000,
max_height=1000,
allow_overlap=False,
)
wdg = pp.PointsPlanWidget(plan)
wdg.show()
qtbot.addWidget(wdg)

with qtbot.waitSignal(wdg._well_view.maxPointsDetected):
wdg._selector.random_points_wdg.num_points.setValue(100)

assert wdg.value().num_points < 60

0 comments on commit 480cbdd

Please sign in to comment.