summaryrefslogtreecommitdiff
path: root/silx/gui/plot/items/roi.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/plot/items/roi.py')
-rw-r--r--silx/gui/plot/items/roi.py1519
1 files changed, 0 insertions, 1519 deletions
diff --git a/silx/gui/plot/items/roi.py b/silx/gui/plot/items/roi.py
deleted file mode 100644
index 38a1424..0000000
--- a/silx/gui/plot/items/roi.py
+++ /dev/null
@@ -1,1519 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2018-2020 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""This module provides ROI item for the :class:`~silx.gui.plot.PlotWidget`.
-
-.. inheritance-diagram::
- silx.gui.plot.items.roi
- :parts: 1
-"""
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "28/06/2018"
-
-
-import logging
-import numpy
-
-from ... import utils
-from .. import items
-from ...colors import rgba
-from silx.image.shapes import Polygon
-from silx.image._boundingbox import _BoundingBox
-from ....utils.proxy import docstring
-from ..utils.intersections import segments_intersection
-from ._roi_base import _RegionOfInterestBase
-
-# He following imports have to be exposed by this module
-from ._roi_base import RegionOfInterest
-from ._roi_base import HandleBasedROI
-from ._arc_roi import ArcROI # noqa
-from ._roi_base import InteractionModeMixIn # noqa
-from ._roi_base import RoiInteractionMode # noqa
-
-
-logger = logging.getLogger(__name__)
-
-
-class PointROI(RegionOfInterest, items.SymbolMixIn):
- """A ROI identifying a point in a 2D plot."""
-
- ICON = 'add-shape-point'
- NAME = 'point markers'
- SHORT_NAME = "point"
- """Metadata for this kind of ROI"""
-
- _plotShape = "point"
- """Plot shape which is used for the first interaction"""
-
- _DEFAULT_SYMBOL = '+'
- """Default symbol of the PointROI
-
- It overwrite the `SymbolMixIn` class attribte.
- """
-
- def __init__(self, parent=None):
- RegionOfInterest.__init__(self, parent=parent)
- items.SymbolMixIn.__init__(self)
- self._marker = items.Marker()
- self._marker.sigItemChanged.connect(self._pointPositionChanged)
- self._marker.setSymbol(self._DEFAULT_SYMBOL)
- self._marker.sigDragStarted.connect(self._editingStarted)
- self._marker.sigDragFinished.connect(self._editingFinished)
- self.addItem(self._marker)
-
- def setFirstShapePoints(self, points):
- self.setPosition(points[0])
-
- def _updated(self, event=None, checkVisibility=True):
- if event == items.ItemChangedType.NAME:
- label = self.getName()
- self._marker.setText(label)
- elif event == items.ItemChangedType.EDITABLE:
- self._marker._setDraggable(self.isEditable())
- elif event in [items.ItemChangedType.VISIBLE,
- items.ItemChangedType.SELECTABLE]:
- self._updateItemProperty(event, self, self._marker)
- super(PointROI, self)._updated(event, checkVisibility)
-
- def _updatedStyle(self, event, style):
- self._marker.setColor(style.getColor())
-
- def getPosition(self):
- """Returns the position of this ROI
-
- :rtype: numpy.ndarray
- """
- return self._marker.getPosition()
-
- def setPosition(self, pos):
- """Set the position of this ROI
-
- :param numpy.ndarray pos: 2d-coordinate of this point
- """
- self._marker.setPosition(*pos)
-
- @docstring(_RegionOfInterestBase)
- def contains(self, position):
- roiPos = self.getPosition()
- return position[0] == roiPos[0] and position[1] == roiPos[1]
-
- def _pointPositionChanged(self, event):
- """Handle position changed events of the marker"""
- if event is items.ItemChangedType.POSITION:
- self.sigRegionChanged.emit()
-
- def __str__(self):
- params = '%f %f' % self.getPosition()
- return "%s(%s)" % (self.__class__.__name__, params)
-
-
-class CrossROI(HandleBasedROI, items.LineMixIn):
- """A ROI identifying a point in a 2D plot and displayed as a cross
- """
-
- ICON = 'add-shape-cross'
- NAME = 'cross marker'
- SHORT_NAME = "cross"
- """Metadata for this kind of ROI"""
-
- _plotShape = "point"
- """Plot shape which is used for the first interaction"""
-
- def __init__(self, parent=None):
- HandleBasedROI.__init__(self, parent=parent)
- items.LineMixIn.__init__(self)
- self._handle = self.addHandle()
- self._handle.sigItemChanged.connect(self._handlePositionChanged)
- self._handleLabel = self.addLabelHandle()
- self._vmarker = self.addUserHandle(items.YMarker())
- self._vmarker._setSelectable(False)
- self._vmarker._setDraggable(False)
- self._vmarker.setPosition(*self.getPosition())
- self._hmarker = self.addUserHandle(items.XMarker())
- self._hmarker._setSelectable(False)
- self._hmarker._setDraggable(False)
- self._hmarker.setPosition(*self.getPosition())
-
- def _updated(self, event=None, checkVisibility=True):
- if event in [items.ItemChangedType.VISIBLE]:
- markers = (self._vmarker, self._hmarker)
- self._updateItemProperty(event, self, markers)
- super(CrossROI, self)._updated(event, checkVisibility)
-
- def _updateText(self, text):
- self._handleLabel.setText(text)
-
- def _updatedStyle(self, event, style):
- super(CrossROI, self)._updatedStyle(event, style)
- for marker in [self._vmarker, self._hmarker]:
- marker.setColor(style.getColor())
- marker.setLineStyle(style.getLineStyle())
- marker.setLineWidth(style.getLineWidth())
-
- def setFirstShapePoints(self, points):
- pos = points[0]
- self.setPosition(pos)
-
- def getPosition(self):
- """Returns the position of this ROI
-
- :rtype: numpy.ndarray
- """
- return self._handle.getPosition()
-
- def setPosition(self, pos):
- """Set the position of this ROI
-
- :param numpy.ndarray pos: 2d-coordinate of this point
- """
- self._handle.setPosition(*pos)
-
- def _handlePositionChanged(self, event):
- """Handle center marker position updates"""
- if event is items.ItemChangedType.POSITION:
- position = self.getPosition()
- self._handleLabel.setPosition(*position)
- self._vmarker.setPosition(*position)
- self._hmarker.setPosition(*position)
- self.sigRegionChanged.emit()
-
- @docstring(HandleBasedROI)
- def contains(self, position):
- roiPos = self.getPosition()
- return position[0] == roiPos[0] or position[1] == roiPos[1]
-
-
-class LineROI(HandleBasedROI, items.LineMixIn):
- """A ROI identifying a line in a 2D plot.
-
- This ROI provides 1 anchor for each boundary of the line, plus an center
- in the center to translate the full ROI.
- """
-
- ICON = 'add-shape-diagonal'
- NAME = 'line ROI'
- SHORT_NAME = "line"
- """Metadata for this kind of ROI"""
-
- _plotShape = "line"
- """Plot shape which is used for the first interaction"""
-
- def __init__(self, parent=None):
- HandleBasedROI.__init__(self, parent=parent)
- items.LineMixIn.__init__(self)
- self._handleStart = self.addHandle()
- self._handleEnd = self.addHandle()
- self._handleCenter = self.addTranslateHandle()
- self._handleLabel = self.addLabelHandle()
-
- shape = items.Shape("polylines")
- shape.setPoints([[0, 0], [0, 0]])
- shape.setColor(rgba(self.getColor()))
- shape.setFill(False)
- shape.setOverlay(True)
- shape.setLineStyle(self.getLineStyle())
- shape.setLineWidth(self.getLineWidth())
- self.__shape = shape
- self.addItem(shape)
-
- def _updated(self, event=None, checkVisibility=True):
- if event == items.ItemChangedType.VISIBLE:
- self._updateItemProperty(event, self, self.__shape)
- super(LineROI, self)._updated(event, checkVisibility)
-
- def _updatedStyle(self, event, style):
- super(LineROI, self)._updatedStyle(event, style)
- self.__shape.setColor(style.getColor())
- self.__shape.setLineStyle(style.getLineStyle())
- self.__shape.setLineWidth(style.getLineWidth())
-
- def setFirstShapePoints(self, points):
- assert len(points) == 2
- self.setEndPoints(points[0], points[1])
-
- def _updateText(self, text):
- self._handleLabel.setText(text)
-
- def setEndPoints(self, startPoint, endPoint):
- """Set this line location using the ending points
-
- :param numpy.ndarray startPoint: Staring bounding point of the line
- :param numpy.ndarray endPoint: Ending bounding point of the line
- """
- if not numpy.array_equal((startPoint, endPoint), self.getEndPoints()):
- self.__updateEndPoints(startPoint, endPoint)
-
- def __updateEndPoints(self, startPoint, endPoint):
- """Update marker and shape to match given end points
-
- :param numpy.ndarray startPoint: Staring bounding point of the line
- :param numpy.ndarray endPoint: Ending bounding point of the line
- """
- startPoint = numpy.array(startPoint)
- endPoint = numpy.array(endPoint)
- center = (startPoint + endPoint) * 0.5
-
- with utils.blockSignals(self._handleStart):
- self._handleStart.setPosition(startPoint[0], startPoint[1])
- with utils.blockSignals(self._handleEnd):
- self._handleEnd.setPosition(endPoint[0], endPoint[1])
- with utils.blockSignals(self._handleCenter):
- self._handleCenter.setPosition(center[0], center[1])
- with utils.blockSignals(self._handleLabel):
- self._handleLabel.setPosition(center[0], center[1])
-
- line = numpy.array((startPoint, endPoint))
- self.__shape.setPoints(line)
- self.sigRegionChanged.emit()
-
- def getEndPoints(self):
- """Returns bounding points of this ROI.
-
- :rtype: Tuple(numpy.ndarray,numpy.ndarray)
- """
- startPoint = numpy.array(self._handleStart.getPosition())
- endPoint = numpy.array(self._handleEnd.getPosition())
- return (startPoint, endPoint)
-
- def handleDragUpdated(self, handle, origin, previous, current):
- if handle is self._handleStart:
- _start, end = self.getEndPoints()
- self.__updateEndPoints(current, end)
- elif handle is self._handleEnd:
- start, _end = self.getEndPoints()
- self.__updateEndPoints(start, current)
- elif handle is self._handleCenter:
- start, end = self.getEndPoints()
- delta = current - previous
- start += delta
- end += delta
- self.setEndPoints(start, end)
-
- @docstring(_RegionOfInterestBase)
- def contains(self, position):
- bottom_left = position[0], position[1]
- bottom_right = position[0] + 1, position[1]
- top_left = position[0], position[1] + 1
- top_right = position[0] + 1, position[1] + 1
-
- points = self.__shape.getPoints()
- line_pt1 = points[0]
- line_pt2 = points[1]
-
- bb1 = _BoundingBox.from_points(points)
- if not bb1.contains(position):
- return False
-
- return (
- segments_intersection(seg1_start_pt=line_pt1, seg1_end_pt=line_pt2,
- seg2_start_pt=bottom_left, seg2_end_pt=bottom_right) or
- segments_intersection(seg1_start_pt=line_pt1, seg1_end_pt=line_pt2,
- seg2_start_pt=bottom_right, seg2_end_pt=top_right) or
- segments_intersection(seg1_start_pt=line_pt1, seg1_end_pt=line_pt2,
- seg2_start_pt=top_right, seg2_end_pt=top_left) or
- segments_intersection(seg1_start_pt=line_pt1, seg1_end_pt=line_pt2,
- seg2_start_pt=top_left, seg2_end_pt=bottom_left)
- ) is not None
-
- def __str__(self):
- start, end = self.getEndPoints()
- params = start[0], start[1], end[0], end[1]
- params = 'start: %f %f; end: %f %f' % params
- return "%s(%s)" % (self.__class__.__name__, params)
-
-
-class HorizontalLineROI(RegionOfInterest, items.LineMixIn):
- """A ROI identifying an horizontal line in a 2D plot."""
-
- ICON = 'add-shape-horizontal'
- NAME = 'horizontal line ROI'
- SHORT_NAME = "hline"
- """Metadata for this kind of ROI"""
-
- _plotShape = "hline"
- """Plot shape which is used for the first interaction"""
-
- def __init__(self, parent=None):
- RegionOfInterest.__init__(self, parent=parent)
- items.LineMixIn.__init__(self)
- self._marker = items.YMarker()
- self._marker.sigItemChanged.connect(self._linePositionChanged)
- self._marker.sigDragStarted.connect(self._editingStarted)
- self._marker.sigDragFinished.connect(self._editingFinished)
- self.addItem(self._marker)
-
- def _updated(self, event=None, checkVisibility=True):
- if event == items.ItemChangedType.NAME:
- label = self.getName()
- self._marker.setText(label)
- elif event == items.ItemChangedType.EDITABLE:
- self._marker._setDraggable(self.isEditable())
- elif event in [items.ItemChangedType.VISIBLE,
- items.ItemChangedType.SELECTABLE]:
- self._updateItemProperty(event, self, self._marker)
- super(HorizontalLineROI, self)._updated(event, checkVisibility)
-
- def _updatedStyle(self, event, style):
- self._marker.setColor(style.getColor())
- self._marker.setLineStyle(style.getLineStyle())
- self._marker.setLineWidth(style.getLineWidth())
-
- def setFirstShapePoints(self, points):
- pos = points[0, 1]
- if pos == self.getPosition():
- return
- self.setPosition(pos)
-
- def getPosition(self):
- """Returns the position of this line if the horizontal axis
-
- :rtype: float
- """
- pos = self._marker.getPosition()
- return pos[1]
-
- def setPosition(self, pos):
- """Set the position of this ROI
-
- :param float pos: Horizontal position of this line
- """
- self._marker.setPosition(0, pos)
-
- @docstring(_RegionOfInterestBase)
- def contains(self, position):
- return position[1] == self.getPosition()
-
- def _linePositionChanged(self, event):
- """Handle position changed events of the marker"""
- if event is items.ItemChangedType.POSITION:
- self.sigRegionChanged.emit()
-
- def __str__(self):
- params = 'y: %f' % self.getPosition()
- return "%s(%s)" % (self.__class__.__name__, params)
-
-
-class VerticalLineROI(RegionOfInterest, items.LineMixIn):
- """A ROI identifying a vertical line in a 2D plot."""
-
- ICON = 'add-shape-vertical'
- NAME = 'vertical line ROI'
- SHORT_NAME = "vline"
- """Metadata for this kind of ROI"""
-
- _plotShape = "vline"
- """Plot shape which is used for the first interaction"""
-
- def __init__(self, parent=None):
- RegionOfInterest.__init__(self, parent=parent)
- items.LineMixIn.__init__(self)
- self._marker = items.XMarker()
- self._marker.sigItemChanged.connect(self._linePositionChanged)
- self._marker.sigDragStarted.connect(self._editingStarted)
- self._marker.sigDragFinished.connect(self._editingFinished)
- self.addItem(self._marker)
-
- def _updated(self, event=None, checkVisibility=True):
- if event == items.ItemChangedType.NAME:
- label = self.getName()
- self._marker.setText(label)
- elif event == items.ItemChangedType.EDITABLE:
- self._marker._setDraggable(self.isEditable())
- elif event in [items.ItemChangedType.VISIBLE,
- items.ItemChangedType.SELECTABLE]:
- self._updateItemProperty(event, self, self._marker)
- super(VerticalLineROI, self)._updated(event, checkVisibility)
-
- def _updatedStyle(self, event, style):
- self._marker.setColor(style.getColor())
- self._marker.setLineStyle(style.getLineStyle())
- self._marker.setLineWidth(style.getLineWidth())
-
- def setFirstShapePoints(self, points):
- pos = points[0, 0]
- self.setPosition(pos)
-
- def getPosition(self):
- """Returns the position of this line if the horizontal axis
-
- :rtype: float
- """
- pos = self._marker.getPosition()
- return pos[0]
-
- def setPosition(self, pos):
- """Set the position of this ROI
-
- :param float pos: Horizontal position of this line
- """
- self._marker.setPosition(pos, 0)
-
- @docstring(RegionOfInterest)
- def contains(self, position):
- return position[0] == self.getPosition()
-
- def _linePositionChanged(self, event):
- """Handle position changed events of the marker"""
- if event is items.ItemChangedType.POSITION:
- self.sigRegionChanged.emit()
-
- def __str__(self):
- params = 'x: %f' % self.getPosition()
- return "%s(%s)" % (self.__class__.__name__, params)
-
-
-class RectangleROI(HandleBasedROI, items.LineMixIn):
- """A ROI identifying a rectangle in a 2D plot.
-
- This ROI provides 1 anchor for each corner, plus an anchor in the
- center to translate the full ROI.
- """
-
- ICON = 'add-shape-rectangle'
- NAME = 'rectangle ROI'
- SHORT_NAME = "rectangle"
- """Metadata for this kind of ROI"""
-
- _plotShape = "rectangle"
- """Plot shape which is used for the first interaction"""
-
- def __init__(self, parent=None):
- HandleBasedROI.__init__(self, parent=parent)
- items.LineMixIn.__init__(self)
- self._handleTopLeft = self.addHandle()
- self._handleTopRight = self.addHandle()
- self._handleBottomLeft = self.addHandle()
- self._handleBottomRight = self.addHandle()
- self._handleCenter = self.addTranslateHandle()
- self._handleLabel = self.addLabelHandle()
-
- shape = items.Shape("rectangle")
- shape.setPoints([[0, 0], [0, 0]])
- shape.setFill(False)
- shape.setOverlay(True)
- shape.setLineStyle(self.getLineStyle())
- shape.setLineWidth(self.getLineWidth())
- shape.setColor(rgba(self.getColor()))
- self.__shape = shape
- self.addItem(shape)
-
- def _updated(self, event=None, checkVisibility=True):
- if event in [items.ItemChangedType.VISIBLE]:
- self._updateItemProperty(event, self, self.__shape)
- super(RectangleROI, self)._updated(event, checkVisibility)
-
- def _updatedStyle(self, event, style):
- super(RectangleROI, self)._updatedStyle(event, style)
- self.__shape.setColor(style.getColor())
- self.__shape.setLineStyle(style.getLineStyle())
- self.__shape.setLineWidth(style.getLineWidth())
-
- def setFirstShapePoints(self, points):
- assert len(points) == 2
- self._setBound(points)
-
- def _setBound(self, points):
- """Initialize the rectangle from a bunch of points"""
- top = max(points[:, 1])
- bottom = min(points[:, 1])
- left = min(points[:, 0])
- right = max(points[:, 0])
- size = right - left, top - bottom
- self._updateGeometry(origin=(left, bottom), size=size)
-
- def _updateText(self, text):
- self._handleLabel.setText(text)
-
- def getCenter(self):
- """Returns the central point of this rectangle
-
- :rtype: numpy.ndarray([float,float])
- """
- pos = self._handleCenter.getPosition()
- return numpy.array(pos)
-
- def getOrigin(self):
- """Returns the corner point with the smaller coordinates
-
- :rtype: numpy.ndarray([float,float])
- """
- pos = self._handleBottomLeft.getPosition()
- return numpy.array(pos)
-
- def getSize(self):
- """Returns the size of this rectangle
-
- :rtype: numpy.ndarray([float,float])
- """
- vmin = self._handleBottomLeft.getPosition()
- vmax = self._handleTopRight.getPosition()
- vmin, vmax = numpy.array(vmin), numpy.array(vmax)
- return vmax - vmin
-
- def setOrigin(self, position):
- """Set the origin position of this ROI
-
- :param numpy.ndarray position: Location of the smaller corner of the ROI
- """
- size = self.getSize()
- self.setGeometry(origin=position, size=size)
-
- def setSize(self, size):
- """Set the size of this ROI
-
- :param numpy.ndarray size: Size of the center of the ROI
- """
- origin = self.getOrigin()
- self.setGeometry(origin=origin, size=size)
-
- def setCenter(self, position):
- """Set the size of this ROI
-
- :param numpy.ndarray position: Location of the center of the ROI
- """
- size = self.getSize()
- self.setGeometry(center=position, size=size)
-
- def setGeometry(self, origin=None, size=None, center=None):
- """Set the geometry of the ROI
- """
- if ((origin is None or numpy.array_equal(origin, self.getOrigin())) and
- (center is None or numpy.array_equal(center, self.getCenter())) and
- numpy.array_equal(size, self.getSize())):
- return # Nothing has changed
-
- self._updateGeometry(origin, size, center)
-
- def _updateGeometry(self, origin=None, size=None, center=None):
- """Forced update of the geometry of the ROI"""
- if origin is not None:
- origin = numpy.array(origin)
- size = numpy.array(size)
- points = numpy.array([origin, origin + size])
- center = origin + size * 0.5
- elif center is not None:
- center = numpy.array(center)
- size = numpy.array(size)
- points = numpy.array([center - size * 0.5, center + size * 0.5])
- else:
- raise ValueError("Origin or center expected")
-
- with utils.blockSignals(self._handleBottomLeft):
- self._handleBottomLeft.setPosition(points[0, 0], points[0, 1])
- with utils.blockSignals(self._handleBottomRight):
- self._handleBottomRight.setPosition(points[1, 0], points[0, 1])
- with utils.blockSignals(self._handleTopLeft):
- self._handleTopLeft.setPosition(points[0, 0], points[1, 1])
- with utils.blockSignals(self._handleTopRight):
- self._handleTopRight.setPosition(points[1, 0], points[1, 1])
- with utils.blockSignals(self._handleCenter):
- self._handleCenter.setPosition(center[0], center[1])
- with utils.blockSignals(self._handleLabel):
- self._handleLabel.setPosition(points[0, 0], points[0, 1])
-
- self.__shape.setPoints(points)
- self.sigRegionChanged.emit()
-
- @docstring(HandleBasedROI)
- def contains(self, position):
- assert isinstance(position, (tuple, list, numpy.array))
- points = self.__shape.getPoints()
- bb1 = _BoundingBox.from_points(points)
- return bb1.contains(position)
-
- def handleDragUpdated(self, handle, origin, previous, current):
- if handle is self._handleCenter:
- # It is the center anchor
- size = self.getSize()
- self._updateGeometry(center=current, size=size)
- else:
- opposed = {
- self._handleBottomLeft: self._handleTopRight,
- self._handleTopRight: self._handleBottomLeft,
- self._handleBottomRight: self._handleTopLeft,
- self._handleTopLeft: self._handleBottomRight,
- }
- handle2 = opposed[handle]
- current2 = handle2.getPosition()
- points = numpy.array([current, current2])
-
- # Switch handles if they were crossed by interaction
- if self._handleBottomLeft.getXPosition() > self._handleBottomRight.getXPosition():
- self._handleBottomLeft, self._handleBottomRight = self._handleBottomRight, self._handleBottomLeft
-
- if self._handleTopLeft.getXPosition() > self._handleTopRight.getXPosition():
- self._handleTopLeft, self._handleTopRight = self._handleTopRight, self._handleTopLeft
-
- if self._handleBottomLeft.getYPosition() > self._handleTopLeft.getYPosition():
- self._handleBottomLeft, self._handleTopLeft = self._handleTopLeft, self._handleBottomLeft
-
- if self._handleBottomRight.getYPosition() > self._handleTopRight.getYPosition():
- self._handleBottomRight, self._handleTopRight = self._handleTopRight, self._handleBottomRight
-
- self._setBound(points)
-
- def __str__(self):
- origin = self.getOrigin()
- w, h = self.getSize()
- params = origin[0], origin[1], w, h
- params = 'origin: %f %f; width: %f; height: %f' % params
- return "%s(%s)" % (self.__class__.__name__, params)
-
-
-class CircleROI(HandleBasedROI, items.LineMixIn):
- """A ROI identifying a circle in a 2D plot.
-
- This ROI provides 1 anchor at the center to translate the circle,
- and one anchor on the perimeter to change the radius.
- """
-
- ICON = 'add-shape-circle'
- NAME = 'circle ROI'
- SHORT_NAME = "circle"
- """Metadata for this kind of ROI"""
-
- _kind = "Circle"
- """Label for this kind of ROI"""
-
- _plotShape = "line"
- """Plot shape which is used for the first interaction"""
-
- def __init__(self, parent=None):
- items.LineMixIn.__init__(self)
- HandleBasedROI.__init__(self, parent=parent)
- self._handlePerimeter = self.addHandle()
- self._handleCenter = self.addTranslateHandle()
- self._handleCenter.sigItemChanged.connect(self._centerPositionChanged)
- self._handleLabel = self.addLabelHandle()
-
- shape = items.Shape("polygon")
- shape.setPoints([[0, 0], [0, 0]])
- shape.setColor(rgba(self.getColor()))
- shape.setFill(False)
- shape.setOverlay(True)
- shape.setLineStyle(self.getLineStyle())
- shape.setLineWidth(self.getLineWidth())
- self.__shape = shape
- self.addItem(shape)
-
- self.__radius = 0
-
- def _updated(self, event=None, checkVisibility=True):
- if event == items.ItemChangedType.VISIBLE:
- self._updateItemProperty(event, self, self.__shape)
- super(CircleROI, self)._updated(event, checkVisibility)
-
- def _updatedStyle(self, event, style):
- super(CircleROI, self)._updatedStyle(event, style)
- self.__shape.setColor(style.getColor())
- self.__shape.setLineStyle(style.getLineStyle())
- self.__shape.setLineWidth(style.getLineWidth())
-
- def setFirstShapePoints(self, points):
- assert len(points) == 2
- self._setRay(points)
-
- def _setRay(self, points):
- """Initialize the circle from the center point and a
- perimeter point."""
- center = points[0]
- radius = numpy.linalg.norm(points[0] - points[1])
- self.setGeometry(center=center, radius=radius)
-
- def _updateText(self, text):
- self._handleLabel.setText(text)
-
- def getCenter(self):
- """Returns the central point of this rectangle
-
- :rtype: numpy.ndarray([float,float])
- """
- pos = self._handleCenter.getPosition()
- return numpy.array(pos)
-
- def getRadius(self):
- """Returns the radius of this circle
-
- :rtype: float
- """
- return self.__radius
-
- def setCenter(self, position):
- """Set the center point of this ROI
-
- :param numpy.ndarray position: Location of the center of the circle
- """
- self._handleCenter.setPosition(*position)
-
- def setRadius(self, radius):
- """Set the size of this ROI
-
- :param float size: Radius of the circle
- """
- radius = float(radius)
- if radius != self.__radius:
- self.__radius = radius
- self._updateGeometry()
-
- def setGeometry(self, center, radius):
- """Set the geometry of the ROI
- """
- if numpy.array_equal(center, self.getCenter()):
- self.setRadius(radius)
- else:
- self.__radius = float(radius) # Update radius directly
- self.setCenter(center) # Calls _updateGeometry
-
- def _updateGeometry(self):
- """Update the handles and shape according to given parameters"""
- center = self.getCenter()
- perimeter_point = numpy.array([center[0] + self.__radius, center[1]])
-
- self._handlePerimeter.setPosition(perimeter_point[0], perimeter_point[1])
- self._handleLabel.setPosition(center[0], center[1])
-
- nbpoints = 27
- angles = numpy.arange(nbpoints) * 2.0 * numpy.pi / nbpoints
- circleShape = numpy.array((numpy.cos(angles) * self.__radius,
- numpy.sin(angles) * self.__radius)).T
- circleShape += center
- self.__shape.setPoints(circleShape)
- self.sigRegionChanged.emit()
-
- def _centerPositionChanged(self, event):
- """Handle position changed events of the center marker"""
- if event is items.ItemChangedType.POSITION:
- self._updateGeometry()
-
- def handleDragUpdated(self, handle, origin, previous, current):
- if handle is self._handlePerimeter:
- center = self.getCenter()
- self.setRadius(numpy.linalg.norm(center - current))
-
- @docstring(HandleBasedROI)
- def contains(self, position):
- return numpy.linalg.norm(self.getCenter() - position) <= self.getRadius()
-
- def __str__(self):
- center = self.getCenter()
- radius = self.getRadius()
- params = center[0], center[1], radius
- params = 'center: %f %f; radius: %f;' % params
- return "%s(%s)" % (self.__class__.__name__, params)
-
-
-class EllipseROI(HandleBasedROI, items.LineMixIn):
- """A ROI identifying an oriented ellipse in a 2D plot.
-
- This ROI provides 1 anchor at the center to translate the circle,
- and two anchors on the perimeter to modify the major-radius and
- minor-radius. These two anchors also allow to change the orientation.
- """
-
- ICON = 'add-shape-ellipse'
- NAME = 'ellipse ROI'
- SHORT_NAME = "ellipse"
- """Metadata for this kind of ROI"""
-
- _plotShape = "line"
- """Plot shape which is used for the first interaction"""
-
- def __init__(self, parent=None):
- items.LineMixIn.__init__(self)
- HandleBasedROI.__init__(self, parent=parent)
- self._handleAxis0 = self.addHandle()
- self._handleAxis1 = self.addHandle()
- self._handleCenter = self.addTranslateHandle()
- self._handleCenter.sigItemChanged.connect(self._centerPositionChanged)
- self._handleLabel = self.addLabelHandle()
-
- shape = items.Shape("polygon")
- shape.setPoints([[0, 0], [0, 0]])
- shape.setColor(rgba(self.getColor()))
- shape.setFill(False)
- shape.setOverlay(True)
- shape.setLineStyle(self.getLineStyle())
- shape.setLineWidth(self.getLineWidth())
- self.__shape = shape
- self.addItem(shape)
-
- self._radius = 0., 0.
- self._orientation = 0. # angle in radians between the X-axis and the _handleAxis0
-
- def _updated(self, event=None, checkVisibility=True):
- if event == items.ItemChangedType.VISIBLE:
- self._updateItemProperty(event, self, self.__shape)
- super(EllipseROI, self)._updated(event, checkVisibility)
-
- def _updatedStyle(self, event, style):
- super(EllipseROI, self)._updatedStyle(event, style)
- self.__shape.setColor(style.getColor())
- self.__shape.setLineStyle(style.getLineStyle())
- self.__shape.setLineWidth(style.getLineWidth())
-
- def setFirstShapePoints(self, points):
- assert len(points) == 2
- self._setRay(points)
-
- @staticmethod
- def _calculateOrientation(p0, p1):
- """return angle in radians between the vector p0-p1
- and the X axis
-
- :param p0: first point coordinates (x, y)
- :param p1: second point coordinates
- :return:
- """
- vector = (p1[0] - p0[0], p1[1] - p0[1])
- x_unit_vector = (1, 0)
- norm = numpy.linalg.norm(vector)
- if norm != 0:
- theta = numpy.arccos(numpy.dot(vector, x_unit_vector) / norm)
- else:
- theta = 0
- if vector[1] < 0:
- # arccos always returns values in range [0, pi]
- theta = 2 * numpy.pi - theta
- return theta
-
- def _setRay(self, points):
- """Initialize the circle from the center point and a
- perimeter point."""
- center = points[0]
- radius = numpy.linalg.norm(points[0] - points[1])
- orientation = self._calculateOrientation(points[0], points[1])
- self.setGeometry(center=center,
- radius=(radius, radius),
- orientation=orientation)
-
- def _updateText(self, text):
- self._handleLabel.setText(text)
-
- def getCenter(self):
- """Returns the central point of this rectangle
-
- :rtype: numpy.ndarray([float,float])
- """
- pos = self._handleCenter.getPosition()
- return numpy.array(pos)
-
- def getMajorRadius(self):
- """Returns the half-diameter of the major axis.
-
- :rtype: float
- """
- return max(self._radius)
-
- def getMinorRadius(self):
- """Returns the half-diameter of the minor axis.
-
- :rtype: float
- """
- return min(self._radius)
-
- def getOrientation(self):
- """Return angle in radians between the horizontal (X) axis
- and the major axis of the ellipse in [0, 2*pi[
-
- :rtype: float:
- """
- return self._orientation
-
- def setCenter(self, center):
- """Set the center point of this ROI
-
- :param numpy.ndarray position: Coordinates (X, Y) of the center
- of the ellipse
- """
- self._handleCenter.setPosition(*center)
-
- def setMajorRadius(self, radius):
- """Set the half-diameter of the major axis of the ellipse.
-
- :param float radius:
- Major radius of the ellipsis. Must be a positive value.
- """
- if self._radius[0] > self._radius[1]:
- newRadius = radius, self._radius[1]
- else:
- newRadius = self._radius[0], radius
- self.setGeometry(radius=newRadius)
-
- def setMinorRadius(self, radius):
- """Set the half-diameter of the minor axis of the ellipse.
-
- :param float radius:
- Minor radius of the ellipsis. Must be a positive value.
- """
- if self._radius[0] > self._radius[1]:
- newRadius = self._radius[0], radius
- else:
- newRadius = radius, self._radius[1]
- self.setGeometry(radius=newRadius)
-
- def setOrientation(self, orientation):
- """Rotate the ellipse
-
- :param float orientation: Angle in radians between the horizontal and
- the major axis.
- :return:
- """
- self.setGeometry(orientation=orientation)
-
- def setGeometry(self, center=None, radius=None, orientation=None):
- """
-
- :param center: (X, Y) coordinates
- :param float majorRadius:
- :param float minorRadius:
- :param float orientation: angle in radians between the major axis and the
- horizontal
- :return:
- """
- if center is None:
- center = self.getCenter()
-
- if radius is None:
- radius = self._radius
- else:
- radius = float(radius[0]), float(radius[1])
-
- if orientation is None:
- orientation = self._orientation
- else:
- # ensure that we store the orientation in range [0, 2*pi
- orientation = numpy.mod(orientation, 2 * numpy.pi)
-
- if (numpy.array_equal(center, self.getCenter()) or
- radius != self._radius or
- orientation != self._orientation):
-
- # Update parameters directly
- self._radius = radius
- self._orientation = orientation
-
- if numpy.array_equal(center, self.getCenter()):
- self._updateGeometry()
- else:
- # This will call _updateGeometry
- self.setCenter(center)
-
- def _updateGeometry(self):
- """Update shape and markers"""
- center = self.getCenter()
-
- orientation = self.getOrientation()
- if self._radius[1] > self._radius[0]:
- # _handleAxis1 is the major axis
- orientation -= numpy.pi / 2
-
- point0 = numpy.array([center[0] + self._radius[0] * numpy.cos(orientation),
- center[1] + self._radius[0] * numpy.sin(orientation)])
- point1 = numpy.array([center[0] - self._radius[1] * numpy.sin(orientation),
- center[1] + self._radius[1] * numpy.cos(orientation)])
- with utils.blockSignals(self._handleAxis0):
- self._handleAxis0.setPosition(*point0)
- with utils.blockSignals(self._handleAxis1):
- self._handleAxis1.setPosition(*point1)
- with utils.blockSignals(self._handleLabel):
- self._handleLabel.setPosition(*center)
-
- nbpoints = 27
- angles = numpy.arange(nbpoints) * 2.0 * numpy.pi / nbpoints
- X = (self._radius[0] * numpy.cos(angles) * numpy.cos(orientation)
- - self._radius[1] * numpy.sin(angles) * numpy.sin(orientation))
- Y = (self._radius[0] * numpy.cos(angles) * numpy.sin(orientation)
- + self._radius[1] * numpy.sin(angles) * numpy.cos(orientation))
-
- ellipseShape = numpy.array((X, Y)).T
- ellipseShape += center
- self.__shape.setPoints(ellipseShape)
- self.sigRegionChanged.emit()
-
- def handleDragUpdated(self, handle, origin, previous, current):
- if handle in (self._handleAxis0, self._handleAxis1):
- center = self.getCenter()
- orientation = self._calculateOrientation(center, current)
- distance = numpy.linalg.norm(center - current)
-
- if handle is self._handleAxis1:
- if self._radius[0] > distance:
- # _handleAxis1 is not the major axis, rotate -90 degrees
- orientation -= numpy.pi / 2
- radius = self._radius[0], distance
-
- else: # _handleAxis0
- if self._radius[1] > distance:
- # _handleAxis0 is not the major axis, rotate +90 degrees
- orientation += numpy.pi / 2
- radius = distance, self._radius[1]
-
- self.setGeometry(radius=radius, orientation=orientation)
-
- def _centerPositionChanged(self, event):
- """Handle position changed events of the center marker"""
- if event is items.ItemChangedType.POSITION:
- self._updateGeometry()
-
- @docstring(HandleBasedROI)
- def contains(self, position):
- major, minor = self.getMajorRadius(), self.getMinorRadius()
- delta = self.getOrientation()
- x, y = position - self.getCenter()
- return ((x*numpy.cos(delta) + y*numpy.sin(delta))**2/major**2 +
- (x*numpy.sin(delta) - y*numpy.cos(delta))**2/minor**2) <= 1
-
- def __str__(self):
- center = self.getCenter()
- major = self.getMajorRadius()
- minor = self.getMinorRadius()
- orientation = self.getOrientation()
- params = center[0], center[1], major, minor, orientation
- params = 'center: %f %f; major radius: %f: minor radius: %f; orientation: %f' % params
- return "%s(%s)" % (self.__class__.__name__, params)
-
-
-class PolygonROI(HandleBasedROI, items.LineMixIn):
- """A ROI identifying a closed polygon in a 2D plot.
-
- This ROI provides 1 anchor for each point of the polygon.
- """
-
- ICON = 'add-shape-polygon'
- NAME = 'polygon ROI'
- SHORT_NAME = "polygon"
- """Metadata for this kind of ROI"""
-
- _plotShape = "polygon"
- """Plot shape which is used for the first interaction"""
-
- def __init__(self, parent=None):
- HandleBasedROI.__init__(self, parent=parent)
- items.LineMixIn.__init__(self)
- self._handleLabel = self.addLabelHandle()
- self._handleCenter = self.addTranslateHandle()
- self._handlePoints = []
- self._points = numpy.empty((0, 2))
- self._handleClose = None
-
- self._polygon_shape = None
- shape = self.__createShape()
- self.__shape = shape
- self.addItem(shape)
-
- def _updated(self, event=None, checkVisibility=True):
- if event in [items.ItemChangedType.VISIBLE]:
- self._updateItemProperty(event, self, self.__shape)
- super(PolygonROI, self)._updated(event, checkVisibility)
-
- def _updatedStyle(self, event, style):
- super(PolygonROI, self)._updatedStyle(event, style)
- self.__shape.setColor(style.getColor())
- self.__shape.setLineStyle(style.getLineStyle())
- self.__shape.setLineWidth(style.getLineWidth())
- if self._handleClose is not None:
- color = self._computeHandleColor(style.getColor())
- self._handleClose.setColor(color)
-
- def __createShape(self, interaction=False):
- kind = "polygon" if not interaction else "polylines"
- shape = items.Shape(kind)
- shape.setPoints([[0, 0], [0, 0]])
- shape.setFill(False)
- shape.setOverlay(True)
- style = self.getCurrentStyle()
- shape.setLineStyle(style.getLineStyle())
- shape.setLineWidth(style.getLineWidth())
- shape.setColor(rgba(style.getColor()))
- return shape
-
- def setFirstShapePoints(self, points):
- if self._handleClose is not None:
- self._handleClose.setPosition(*points[0])
- self.setPoints(points)
-
- def creationStarted(self):
- """"Called when the ROI creation interaction was started.
- """
- # Handle to see where to close the polygon
- self._handleClose = self.addUserHandle()
- self._handleClose.setSymbol("o")
- color = self._computeHandleColor(rgba(self.getColor()))
- self._handleClose.setColor(color)
-
- # Hide the center while creating the first shape
- self._handleCenter.setSymbol("")
-
- # In interaction replace the polygon by a line, to display something unclosed
- self.removeItem(self.__shape)
- self.__shape = self.__createShape(interaction=True)
- self.__shape.setPoints(self._points)
- self.addItem(self.__shape)
-
- def isBeingCreated(self):
- """Returns true if the ROI is in creation step"""
- return self._handleClose is not None
-
- def creationFinalized(self):
- """"Called when the ROI creation interaction was finalized.
- """
- self.removeHandle(self._handleClose)
- self._handleClose = None
- self.removeItem(self.__shape)
- self.__shape = self.__createShape()
- self.__shape.setPoints(self._points)
- self.addItem(self.__shape)
- # Hide the center while creating the first shape
- self._handleCenter.setSymbol("+")
- for handle in self._handlePoints:
- handle.setSymbol("s")
-
- def _updateText(self, text):
- self._handleLabel.setText(text)
-
- def getPoints(self):
- """Returns the list of the points of this polygon.
-
- :rtype: numpy.ndarray
- """
- return self._points.copy()
-
- def setPoints(self, points):
- """Set the position of this ROI
-
- :param numpy.ndarray pos: 2d-coordinate of this point
- """
- assert(len(points.shape) == 2 and points.shape[1] == 2)
-
- if numpy.array_equal(points, self._points):
- return # Nothing has changed
-
- self._polygon_shape = None
-
- # Update the needed handles
- while len(self._handlePoints) != len(points):
- if len(self._handlePoints) < len(points):
- handle = self.addHandle()
- self._handlePoints.append(handle)
- if self.isBeingCreated():
- handle.setSymbol("")
- else:
- handle = self._handlePoints.pop(-1)
- self.removeHandle(handle)
-
- for handle, position in zip(self._handlePoints, points):
- with utils.blockSignals(handle):
- handle.setPosition(position[0], position[1])
-
- if len(points) > 0:
- if not self.isHandleBeingDragged():
- vmin = numpy.min(points, axis=0)
- vmax = numpy.max(points, axis=0)
- center = (vmax + vmin) * 0.5
- with utils.blockSignals(self._handleCenter):
- self._handleCenter.setPosition(center[0], center[1])
-
- num = numpy.argmin(points[:, 1])
- pos = points[num]
- with utils.blockSignals(self._handleLabel):
- self._handleLabel.setPosition(pos[0], pos[1])
-
- if len(points) == 0:
- self._points = numpy.empty((0, 2))
- else:
- self._points = points
- self.__shape.setPoints(self._points)
- self.sigRegionChanged.emit()
-
- def translate(self, x, y):
- points = self.getPoints()
- delta = numpy.array([x, y])
- self.setPoints(points)
- self.setPoints(points + delta)
-
- def handleDragUpdated(self, handle, origin, previous, current):
- if handle is self._handleCenter:
- delta = current - previous
- self.translate(delta[0], delta[1])
- else:
- points = self.getPoints()
- num = self._handlePoints.index(handle)
- points[num] = current
- self.setPoints(points)
-
- def handleDragFinished(self, handle, origin, current):
- points = self._points
- if len(points) > 0:
- # Only update the center at the end
- # To avoid to disturb the interaction
- vmin = numpy.min(points, axis=0)
- vmax = numpy.max(points, axis=0)
- center = (vmax + vmin) * 0.5
- with utils.blockSignals(self._handleCenter):
- self._handleCenter.setPosition(center[0], center[1])
-
- def __str__(self):
- points = self._points
- params = '; '.join('%f %f' % (pt[0], pt[1]) for pt in points)
- return "%s(%s)" % (self.__class__.__name__, params)
-
- @docstring(HandleBasedROI)
- def contains(self, position):
- bb1 = _BoundingBox.from_points(self.getPoints())
- if bb1.contains(position) is False:
- return False
-
- if self._polygon_shape is None:
- self._polygon_shape = Polygon(vertices=self.getPoints())
-
- # warning: both the polygon and the value are inverted
- return self._polygon_shape.is_inside(row=position[0], col=position[1])
-
- def _setControlPoints(self, points):
- RegionOfInterest._setControlPoints(self, points=points)
- self._polygon_shape = None
-
-
-class HorizontalRangeROI(RegionOfInterest, items.LineMixIn):
- """A ROI identifying an horizontal range in a 1D plot."""
-
- ICON = 'add-range-horizontal'
- NAME = 'horizontal range ROI'
- SHORT_NAME = "hrange"
-
- _plotShape = "line"
- """Plot shape which is used for the first interaction"""
-
- def __init__(self, parent=None):
- RegionOfInterest.__init__(self, parent=parent)
- items.LineMixIn.__init__(self)
- self._markerMin = items.XMarker()
- self._markerMax = items.XMarker()
- self._markerCen = items.XMarker()
- self._markerCen.setLineStyle(" ")
- self._markerMin._setConstraint(self.__positionMinConstraint)
- self._markerMax._setConstraint(self.__positionMaxConstraint)
- self._markerMin.sigDragStarted.connect(self._editingStarted)
- self._markerMin.sigDragFinished.connect(self._editingFinished)
- self._markerMax.sigDragStarted.connect(self._editingStarted)
- self._markerMax.sigDragFinished.connect(self._editingFinished)
- self._markerCen.sigDragStarted.connect(self._editingStarted)
- self._markerCen.sigDragFinished.connect(self._editingFinished)
- self.addItem(self._markerCen)
- self.addItem(self._markerMin)
- self.addItem(self._markerMax)
- self.__filterReentrant = utils.LockReentrant()
-
- def setFirstShapePoints(self, points):
- vmin = min(points[:, 0])
- vmax = max(points[:, 0])
- self._updatePos(vmin, vmax)
-
- def _updated(self, event=None, checkVisibility=True):
- if event == items.ItemChangedType.NAME:
- self._updateText()
- elif event == items.ItemChangedType.EDITABLE:
- self._updateEditable()
- self._updateText()
- elif event == items.ItemChangedType.LINE_STYLE:
- markers = [self._markerMin, self._markerMax]
- self._updateItemProperty(event, self, markers)
- elif event in [items.ItemChangedType.VISIBLE,
- items.ItemChangedType.SELECTABLE]:
- markers = [self._markerMin, self._markerMax, self._markerCen]
- self._updateItemProperty(event, self, markers)
- super(HorizontalRangeROI, self)._updated(event, checkVisibility)
-
- def _updatedStyle(self, event, style):
- markers = [self._markerMin, self._markerMax, self._markerCen]
- for m in markers:
- m.setColor(style.getColor())
- m.setLineWidth(style.getLineWidth())
-
- def _updateText(self):
- text = self.getName()
- if self.isEditable():
- self._markerMin.setText("")
- self._markerCen.setText(text)
- else:
- self._markerMin.setText(text)
- self._markerCen.setText("")
-
- def _updateEditable(self):
- editable = self.isEditable()
- self._markerMin._setDraggable(editable)
- self._markerMax._setDraggable(editable)
- self._markerCen._setDraggable(editable)
- if self.isEditable():
- self._markerMin.sigItemChanged.connect(self._minPositionChanged)
- self._markerMax.sigItemChanged.connect(self._maxPositionChanged)
- self._markerCen.sigItemChanged.connect(self._cenPositionChanged)
- self._markerCen.setLineStyle(":")
- else:
- self._markerMin.sigItemChanged.disconnect(self._minPositionChanged)
- self._markerMax.sigItemChanged.disconnect(self._maxPositionChanged)
- self._markerCen.sigItemChanged.disconnect(self._cenPositionChanged)
- self._markerCen.setLineStyle(" ")
-
- def _updatePos(self, vmin, vmax, force=False):
- """Update marker position and emit signal.
-
- :param float vmin:
- :param float vmax:
- :param bool force:
- True to update even if already at the right position.
- """
- if not force and numpy.array_equal((vmin, vmax), self.getRange()):
- return # Nothing has changed
-
- center = (vmin + vmax) * 0.5
- with self.__filterReentrant:
- with utils.blockSignals(self._markerMin):
- self._markerMin.setPosition(vmin, 0)
- with utils.blockSignals(self._markerCen):
- self._markerCen.setPosition(center, 0)
- with utils.blockSignals(self._markerMax):
- self._markerMax.setPosition(vmax, 0)
- self.sigRegionChanged.emit()
-
- def setRange(self, vmin, vmax):
- """Set the range of this ROI.
-
- :param float vmin: Staring location of the range
- :param float vmax: Ending location of the range
- """
- if vmin is None or vmax is None:
- err = "Can't set vmin or vmax to None"
- raise ValueError(err)
- if vmin > vmax:
- err = "Can't set vmin and vmax because vmin >= vmax " \
- "vmin = %s, vmax = %s" % (vmin, vmax)
- raise ValueError(err)
- self._updatePos(vmin, vmax)
-
- def getRange(self):
- """Returns the range of this ROI.
-
- :rtype: Tuple[float,float]
- """
- vmin = self.getMin()
- vmax = self.getMax()
- return vmin, vmax
-
- def setMin(self, vmin):
- """Set the min of this ROI.
-
- :param float vmin: New min
- """
- vmax = self.getMax()
- self._updatePos(vmin, vmax)
-
- def getMin(self):
- """Returns the min value of this ROI.
-
- :rtype: float
- """
- return self._markerMin.getPosition()[0]
-
- def setMax(self, vmax):
- """Set the max of this ROI.
-
- :param float vmax: New max
- """
- vmin = self.getMin()
- self._updatePos(vmin, vmax)
-
- def getMax(self):
- """Returns the max value of this ROI.
-
- :rtype: float
- """
- return self._markerMax.getPosition()[0]
-
- def setCenter(self, center):
- """Set the center of this ROI.
-
- :param float center: New center
- """
- vmin, vmax = self.getRange()
- previousCenter = (vmin + vmax) * 0.5
- delta = center - previousCenter
- self._updatePos(vmin + delta, vmax + delta)
-
- def getCenter(self):
- """Returns the center location of this ROI.
-
- :rtype: float
- """
- vmin, vmax = self.getRange()
- return (vmin + vmax) * 0.5
-
- def __positionMinConstraint(self, x, y):
- """Constraint of the min marker"""
- if self.__filterReentrant.locked():
- # Ignore the constraint when we set an explicit value
- return x, y
- vmax = self.getMax()
- if vmax is None:
- return x, y
- return min(x, vmax), y
-
- def __positionMaxConstraint(self, x, y):
- """Constraint of the max marker"""
- if self.__filterReentrant.locked():
- # Ignore the constraint when we set an explicit value
- return x, y
- vmin = self.getMin()
- if vmin is None:
- return x, y
- return max(x, vmin), y
-
- def _minPositionChanged(self, event):
- """Handle position changed events of the marker"""
- if event is items.ItemChangedType.POSITION:
- marker = self.sender()
- self._updatePos(marker.getXPosition(), self.getMax(), force=True)
-
- def _maxPositionChanged(self, event):
- """Handle position changed events of the marker"""
- if event is items.ItemChangedType.POSITION:
- marker = self.sender()
- self._updatePos(self.getMin(), marker.getXPosition(), force=True)
-
- def _cenPositionChanged(self, event):
- """Handle position changed events of the marker"""
- if event is items.ItemChangedType.POSITION:
- marker = self.sender()
- self.setCenter(marker.getXPosition())
-
- @docstring(HandleBasedROI)
- def contains(self, position):
- return self.getMin() <= position[0] <= self.getMax()
-
- def __str__(self):
- vrange = self.getRange()
- params = 'min: %f; max: %f' % vrange
- return "%s(%s)" % (self.__class__.__name__, params)