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.
|
||||
"""
|
||||
|
||||
@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
|
||||
@rpc_call
|
||||
def line_color(self) -> "str":
|
||||
@ -639,6 +659,26 @@ class CircularROI(RPCBase):
|
||||
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
|
||||
@rpc_call
|
||||
def line_color(self) -> "str":
|
||||
@ -1494,6 +1534,7 @@ class Image(RPCBase):
|
||||
line_width: "int | None" = 5,
|
||||
pos: "tuple[float, float] | None" = (10, 10),
|
||||
size: "tuple[float, float] | None" = (50, 50),
|
||||
movable: "bool" = True,
|
||||
**pg_kwargs,
|
||||
) -> "RectangularROI | CircularROI":
|
||||
"""
|
||||
@ -1505,6 +1546,7 @@ class Image(RPCBase):
|
||||
line_width(int): The line width of the ROI.
|
||||
pos(tuple): The position of the ROI.
|
||||
size(tuple): The size of the ROI.
|
||||
movable(bool): Whether the ROI is movable.
|
||||
**pg_kwargs: Additional arguments for the ROI.
|
||||
|
||||
Returns:
|
||||
@ -2664,6 +2706,26 @@ class RectangularROI(RPCBase):
|
||||
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
|
||||
@rpc_call
|
||||
def line_color(self) -> "str":
|
||||
|
@ -559,6 +559,7 @@ class ImageBase(PlotBase):
|
||||
line_width: int | None = 5,
|
||||
pos: tuple[float, float] | None = (10, 10),
|
||||
size: tuple[float, float] | None = (50, 50),
|
||||
movable: bool = True,
|
||||
**pg_kwargs,
|
||||
) -> RectangularROI | CircularROI:
|
||||
"""
|
||||
@ -570,6 +571,7 @@ class ImageBase(PlotBase):
|
||||
line_width(int): The line width of the ROI.
|
||||
pos(tuple): The position of the ROI.
|
||||
size(tuple): The size of the ROI.
|
||||
movable(bool): Whether the ROI is movable.
|
||||
**pg_kwargs: Additional arguments for the ROI.
|
||||
|
||||
Returns:
|
||||
@ -584,6 +586,7 @@ class ImageBase(PlotBase):
|
||||
parent_image=self,
|
||||
line_width=line_width,
|
||||
label=name,
|
||||
movable=movable,
|
||||
**pg_kwargs,
|
||||
)
|
||||
elif kind == "circle":
|
||||
@ -593,6 +596,7 @@ class ImageBase(PlotBase):
|
||||
parent_image=self,
|
||||
line_width=line_width,
|
||||
label=name,
|
||||
movable=movable,
|
||||
**pg_kwargs,
|
||||
)
|
||||
else:
|
||||
|
@ -107,6 +107,8 @@ class BaseROI(BECConnector):
|
||||
USER_ACCESS = [
|
||||
"label",
|
||||
"label.setter",
|
||||
"movable",
|
||||
"movable.setter",
|
||||
"line_color",
|
||||
"line_color.setter",
|
||||
"line_width",
|
||||
@ -164,11 +166,12 @@ class BaseROI(BECConnector):
|
||||
self._line_color = line_color or "#ffffff"
|
||||
self._line_width = line_width
|
||||
self._description = True
|
||||
self._movable = True # allow moving by default
|
||||
self._movable = movable
|
||||
self.setPen(mkPen(self._line_color, width=self._line_width))
|
||||
|
||||
# Reset Handles to avoid inherited handles from pyqtgraph
|
||||
self.remove_scale_handles() # remove any existing handles from pyqtgraph.RectROI
|
||||
if movable:
|
||||
self.add_scale_handle() # add custom scale handles
|
||||
|
||||
def set_parent(self, parent: Image):
|
||||
@ -189,6 +192,39 @@ class BaseROI(BECConnector):
|
||||
"""
|
||||
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
|
||||
def label(self) -> str:
|
||||
"""
|
||||
@ -411,6 +447,7 @@ class RectangularROI(BaseROI, pg.RectROI):
|
||||
label: str | None = None,
|
||||
line_color: str | None = None,
|
||||
line_width: int = 5,
|
||||
movable: bool = True,
|
||||
resize_handles: bool = True,
|
||||
**extra_pg,
|
||||
):
|
||||
@ -441,6 +478,7 @@ class RectangularROI(BaseROI, pg.RectROI):
|
||||
pos=pos,
|
||||
size=size,
|
||||
pen=pen,
|
||||
movable=movable,
|
||||
**extra_pg,
|
||||
)
|
||||
|
||||
@ -588,6 +626,7 @@ class CircularROI(BaseROI, pg.CircleROI):
|
||||
label: str | None = None,
|
||||
line_color: str | None = None,
|
||||
line_width: int = 5,
|
||||
movable: bool = True,
|
||||
**extra_pg,
|
||||
):
|
||||
"""
|
||||
@ -619,6 +658,7 @@ class CircularROI(BaseROI, pg.CircleROI):
|
||||
pos=pos,
|
||||
size=size,
|
||||
pen=pen,
|
||||
movable=movable,
|
||||
**extra_pg,
|
||||
)
|
||||
self.sigRegionChanged.connect(self._on_region_changed)
|
||||
|
@ -205,3 +205,29 @@ def test_roi_set_position(bec_image_widget_with_roi):
|
||||
pos = roi.pos()
|
||||
assert int(pos.x()) == 10
|
||||
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