mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-13 11:11:49 +02:00
feat(roi): rois can be lock to be not moved by mouse
This commit is contained in:
@ -530,6 +530,26 @@ class BaseROI(RPCBase):
|
|||||||
str: The current name of the ROI.
|
str: The current name of the ROI.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
@rpc_call
|
||||||
|
def movable(self) -> "bool":
|
||||||
|
"""
|
||||||
|
Gets whether this ROI is movable.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if the ROI can be moved, False otherwise.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@movable.setter
|
||||||
|
@rpc_call
|
||||||
|
def movable(self) -> "bool":
|
||||||
|
"""
|
||||||
|
Gets whether this ROI is movable.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if the ROI can be moved, False otherwise.
|
||||||
|
"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@rpc_call
|
@rpc_call
|
||||||
def line_color(self) -> "str":
|
def line_color(self) -> "str":
|
||||||
@ -639,6 +659,26 @@ class CircularROI(RPCBase):
|
|||||||
str: The current name of the ROI.
|
str: The current name of the ROI.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
@rpc_call
|
||||||
|
def movable(self) -> "bool":
|
||||||
|
"""
|
||||||
|
Gets whether this ROI is movable.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if the ROI can be moved, False otherwise.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@movable.setter
|
||||||
|
@rpc_call
|
||||||
|
def movable(self) -> "bool":
|
||||||
|
"""
|
||||||
|
Gets whether this ROI is movable.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if the ROI can be moved, False otherwise.
|
||||||
|
"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@rpc_call
|
@rpc_call
|
||||||
def line_color(self) -> "str":
|
def line_color(self) -> "str":
|
||||||
@ -1494,6 +1534,7 @@ class Image(RPCBase):
|
|||||||
line_width: "int | None" = 5,
|
line_width: "int | None" = 5,
|
||||||
pos: "tuple[float, float] | None" = (10, 10),
|
pos: "tuple[float, float] | None" = (10, 10),
|
||||||
size: "tuple[float, float] | None" = (50, 50),
|
size: "tuple[float, float] | None" = (50, 50),
|
||||||
|
movable: "bool" = True,
|
||||||
**pg_kwargs,
|
**pg_kwargs,
|
||||||
) -> "RectangularROI | CircularROI":
|
) -> "RectangularROI | CircularROI":
|
||||||
"""
|
"""
|
||||||
@ -1505,6 +1546,7 @@ class Image(RPCBase):
|
|||||||
line_width(int): The line width of the ROI.
|
line_width(int): The line width of the ROI.
|
||||||
pos(tuple): The position of the ROI.
|
pos(tuple): The position of the ROI.
|
||||||
size(tuple): The size of the ROI.
|
size(tuple): The size of the ROI.
|
||||||
|
movable(bool): Whether the ROI is movable.
|
||||||
**pg_kwargs: Additional arguments for the ROI.
|
**pg_kwargs: Additional arguments for the ROI.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -2664,6 +2706,26 @@ class RectangularROI(RPCBase):
|
|||||||
str: The current name of the ROI.
|
str: The current name of the ROI.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
@rpc_call
|
||||||
|
def movable(self) -> "bool":
|
||||||
|
"""
|
||||||
|
Gets whether this ROI is movable.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if the ROI can be moved, False otherwise.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@movable.setter
|
||||||
|
@rpc_call
|
||||||
|
def movable(self) -> "bool":
|
||||||
|
"""
|
||||||
|
Gets whether this ROI is movable.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if the ROI can be moved, False otherwise.
|
||||||
|
"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@rpc_call
|
@rpc_call
|
||||||
def line_color(self) -> "str":
|
def line_color(self) -> "str":
|
||||||
|
@ -559,6 +559,7 @@ class ImageBase(PlotBase):
|
|||||||
line_width: int | None = 5,
|
line_width: int | None = 5,
|
||||||
pos: tuple[float, float] | None = (10, 10),
|
pos: tuple[float, float] | None = (10, 10),
|
||||||
size: tuple[float, float] | None = (50, 50),
|
size: tuple[float, float] | None = (50, 50),
|
||||||
|
movable: bool = True,
|
||||||
**pg_kwargs,
|
**pg_kwargs,
|
||||||
) -> RectangularROI | CircularROI:
|
) -> RectangularROI | CircularROI:
|
||||||
"""
|
"""
|
||||||
@ -570,6 +571,7 @@ class ImageBase(PlotBase):
|
|||||||
line_width(int): The line width of the ROI.
|
line_width(int): The line width of the ROI.
|
||||||
pos(tuple): The position of the ROI.
|
pos(tuple): The position of the ROI.
|
||||||
size(tuple): The size of the ROI.
|
size(tuple): The size of the ROI.
|
||||||
|
movable(bool): Whether the ROI is movable.
|
||||||
**pg_kwargs: Additional arguments for the ROI.
|
**pg_kwargs: Additional arguments for the ROI.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -584,6 +586,7 @@ class ImageBase(PlotBase):
|
|||||||
parent_image=self,
|
parent_image=self,
|
||||||
line_width=line_width,
|
line_width=line_width,
|
||||||
label=name,
|
label=name,
|
||||||
|
movable=movable,
|
||||||
**pg_kwargs,
|
**pg_kwargs,
|
||||||
)
|
)
|
||||||
elif kind == "circle":
|
elif kind == "circle":
|
||||||
@ -593,6 +596,7 @@ class ImageBase(PlotBase):
|
|||||||
parent_image=self,
|
parent_image=self,
|
||||||
line_width=line_width,
|
line_width=line_width,
|
||||||
label=name,
|
label=name,
|
||||||
|
movable=movable,
|
||||||
**pg_kwargs,
|
**pg_kwargs,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
@ -107,6 +107,8 @@ class BaseROI(BECConnector):
|
|||||||
USER_ACCESS = [
|
USER_ACCESS = [
|
||||||
"label",
|
"label",
|
||||||
"label.setter",
|
"label.setter",
|
||||||
|
"movable",
|
||||||
|
"movable.setter",
|
||||||
"line_color",
|
"line_color",
|
||||||
"line_color.setter",
|
"line_color.setter",
|
||||||
"line_width",
|
"line_width",
|
||||||
@ -164,12 +166,13 @@ class BaseROI(BECConnector):
|
|||||||
self._line_color = line_color or "#ffffff"
|
self._line_color = line_color or "#ffffff"
|
||||||
self._line_width = line_width
|
self._line_width = line_width
|
||||||
self._description = True
|
self._description = True
|
||||||
self._movable = True # allow moving by default
|
self._movable = movable
|
||||||
self.setPen(mkPen(self._line_color, width=self._line_width))
|
self.setPen(mkPen(self._line_color, width=self._line_width))
|
||||||
|
|
||||||
# Reset Handles to avoid inherited handles from pyqtgraph
|
# Reset Handles to avoid inherited handles from pyqtgraph
|
||||||
self.remove_scale_handles() # remove any existing handles from pyqtgraph.RectROI
|
self.remove_scale_handles() # remove any existing handles from pyqtgraph.RectROI
|
||||||
self.add_scale_handle() # add custom scale handles
|
if movable:
|
||||||
|
self.add_scale_handle() # add custom scale handles
|
||||||
|
|
||||||
def set_parent(self, parent: Image):
|
def set_parent(self, parent: Image):
|
||||||
"""
|
"""
|
||||||
@ -189,6 +192,39 @@ class BaseROI(BECConnector):
|
|||||||
"""
|
"""
|
||||||
return self.parent_image
|
return self.parent_image
|
||||||
|
|
||||||
|
@property
|
||||||
|
def movable(self) -> bool:
|
||||||
|
"""
|
||||||
|
Gets whether this ROI is movable.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if the ROI can be moved, False otherwise.
|
||||||
|
"""
|
||||||
|
return self._movable
|
||||||
|
|
||||||
|
@movable.setter
|
||||||
|
def movable(self, value: bool):
|
||||||
|
"""
|
||||||
|
Sets whether this ROI is movable.
|
||||||
|
|
||||||
|
If the new value is different from the current value, this method updates
|
||||||
|
the internal state and emits the penChanged signal.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value (bool): True to make the ROI movable, False to make it fixed.
|
||||||
|
"""
|
||||||
|
if value != self._movable:
|
||||||
|
self._movable = value
|
||||||
|
# All relevant properties from pyqtgraph to block movement
|
||||||
|
self.translatable = value
|
||||||
|
self.rotatable = value
|
||||||
|
self.resizable = value
|
||||||
|
self.removable = value
|
||||||
|
if value:
|
||||||
|
self.add_scale_handle() # add custom scale handles
|
||||||
|
else:
|
||||||
|
self.remove_scale_handles() # remove custom scale handles
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def label(self) -> str:
|
def label(self) -> str:
|
||||||
"""
|
"""
|
||||||
@ -411,6 +447,7 @@ class RectangularROI(BaseROI, pg.RectROI):
|
|||||||
label: str | None = None,
|
label: str | None = None,
|
||||||
line_color: str | None = None,
|
line_color: str | None = None,
|
||||||
line_width: int = 5,
|
line_width: int = 5,
|
||||||
|
movable: bool = True,
|
||||||
resize_handles: bool = True,
|
resize_handles: bool = True,
|
||||||
**extra_pg,
|
**extra_pg,
|
||||||
):
|
):
|
||||||
@ -441,6 +478,7 @@ class RectangularROI(BaseROI, pg.RectROI):
|
|||||||
pos=pos,
|
pos=pos,
|
||||||
size=size,
|
size=size,
|
||||||
pen=pen,
|
pen=pen,
|
||||||
|
movable=movable,
|
||||||
**extra_pg,
|
**extra_pg,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -588,6 +626,7 @@ class CircularROI(BaseROI, pg.CircleROI):
|
|||||||
label: str | None = None,
|
label: str | None = None,
|
||||||
line_color: str | None = None,
|
line_color: str | None = None,
|
||||||
line_width: int = 5,
|
line_width: int = 5,
|
||||||
|
movable: bool = True,
|
||||||
**extra_pg,
|
**extra_pg,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
@ -619,6 +658,7 @@ class CircularROI(BaseROI, pg.CircleROI):
|
|||||||
pos=pos,
|
pos=pos,
|
||||||
size=size,
|
size=size,
|
||||||
pen=pen,
|
pen=pen,
|
||||||
|
movable=movable,
|
||||||
**extra_pg,
|
**extra_pg,
|
||||||
)
|
)
|
||||||
self.sigRegionChanged.connect(self._on_region_changed)
|
self.sigRegionChanged.connect(self._on_region_changed)
|
||||||
|
@ -205,3 +205,29 @@ def test_roi_set_position(bec_image_widget_with_roi):
|
|||||||
pos = roi.pos()
|
pos = roi.pos()
|
||||||
assert int(pos.x()) == 10
|
assert int(pos.x()) == 10
|
||||||
assert int(pos.y()) == 15
|
assert int(pos.y()) == 15
|
||||||
|
|
||||||
|
|
||||||
|
def test_roi_movable_property(bec_image_widget_with_roi, qtbot):
|
||||||
|
"""Verify BaseROI.movable toggles flags, handles, and emits a signal."""
|
||||||
|
_widget, roi, _ = bec_image_widget_with_roi
|
||||||
|
|
||||||
|
# defaults – ROI is movable
|
||||||
|
assert roi.movable
|
||||||
|
assert roi.translatable and roi.rotatable and roi.resizable and roi.removable
|
||||||
|
assert len(roi.handles) > 0
|
||||||
|
|
||||||
|
# lock it
|
||||||
|
with qtbot.waitSignal(roi.movableChanged) as blocker:
|
||||||
|
roi.movable = False
|
||||||
|
assert blocker.args == [False]
|
||||||
|
assert not roi.movable
|
||||||
|
assert not (roi.translatable or roi.rotatable or roi.resizable or roi.removable)
|
||||||
|
assert len(roi.handles) == 0
|
||||||
|
|
||||||
|
# unlock again
|
||||||
|
with qtbot.waitSignal(roi.movableChanged) as blocker:
|
||||||
|
roi.movable = True
|
||||||
|
assert blocker.args == [True]
|
||||||
|
assert roi.movable
|
||||||
|
assert roi.translatable and roi.rotatable and roi.resizable and roi.removable
|
||||||
|
assert len(roi.handles) > 0
|
||||||
|
Reference in New Issue
Block a user