overhaul/cleanup of ROI code, particularly for PolyLineROI
(should be no major API changes here)
This commit is contained in:
parent
0e1b57cf01
commit
26c73d3583
@ -245,15 +245,16 @@ class ROI(GraphicsObject):
|
|||||||
def handleMoveStarted(self):
|
def handleMoveStarted(self):
|
||||||
self.preMoveState = self.getState()
|
self.preMoveState = self.getState()
|
||||||
|
|
||||||
def addTranslateHandle(self, pos, axes=None, item=None, name=None):
|
def addTranslateHandle(self, pos, axes=None, item=None, name=None, index=None):
|
||||||
pos = Point(pos)
|
pos = Point(pos)
|
||||||
return self.addHandle({'name': name, 'type': 't', 'pos': pos, 'item': item})
|
return self.addHandle({'name': name, 'type': 't', 'pos': pos, 'item': item}, index=index)
|
||||||
|
|
||||||
def addFreeHandle(self, pos, axes=None, item=None, name=None):
|
def addFreeHandle(self, pos=None, axes=None, item=None, name=None, index=None):
|
||||||
pos = Point(pos)
|
if pos is not None:
|
||||||
return self.addHandle({'name': name, 'type': 'f', 'pos': pos, 'item': item})
|
pos = Point(pos)
|
||||||
|
return self.addHandle({'name': name, 'type': 'f', 'pos': pos, 'item': item}, index=index)
|
||||||
|
|
||||||
def addScaleHandle(self, pos, center, axes=None, item=None, name=None, lockAspect=False):
|
def addScaleHandle(self, pos, center, axes=None, item=None, name=None, lockAspect=False, index=None):
|
||||||
pos = Point(pos)
|
pos = Point(pos)
|
||||||
center = Point(center)
|
center = Point(center)
|
||||||
info = {'name': name, 'type': 's', 'center': center, 'pos': pos, 'item': item, 'lockAspect': lockAspect}
|
info = {'name': name, 'type': 's', 'center': center, 'pos': pos, 'item': item, 'lockAspect': lockAspect}
|
||||||
@ -261,53 +262,92 @@ class ROI(GraphicsObject):
|
|||||||
info['xoff'] = True
|
info['xoff'] = True
|
||||||
if pos.y() == center.y():
|
if pos.y() == center.y():
|
||||||
info['yoff'] = True
|
info['yoff'] = True
|
||||||
return self.addHandle(info)
|
return self.addHandle(info, index=index)
|
||||||
|
|
||||||
def addRotateHandle(self, pos, center, item=None, name=None):
|
def addRotateHandle(self, pos, center, item=None, name=None, index=None):
|
||||||
pos = Point(pos)
|
pos = Point(pos)
|
||||||
center = Point(center)
|
center = Point(center)
|
||||||
return self.addHandle({'name': name, 'type': 'r', 'center': center, 'pos': pos, 'item': item})
|
return self.addHandle({'name': name, 'type': 'r', 'center': center, 'pos': pos, 'item': item}, index=index)
|
||||||
|
|
||||||
def addScaleRotateHandle(self, pos, center, item=None, name=None):
|
def addScaleRotateHandle(self, pos, center, item=None, name=None, index=None):
|
||||||
pos = Point(pos)
|
pos = Point(pos)
|
||||||
center = Point(center)
|
center = Point(center)
|
||||||
if pos[0] != center[0] and pos[1] != center[1]:
|
if pos[0] != center[0] and pos[1] != center[1]:
|
||||||
raise Exception("Scale/rotate handles must have either the same x or y coordinate as their center point.")
|
raise Exception("Scale/rotate handles must have either the same x or y coordinate as their center point.")
|
||||||
return self.addHandle({'name': name, 'type': 'sr', 'center': center, 'pos': pos, 'item': item})
|
return self.addHandle({'name': name, 'type': 'sr', 'center': center, 'pos': pos, 'item': item}, index=index)
|
||||||
|
|
||||||
def addRotateFreeHandle(self, pos, center, axes=None, item=None, name=None):
|
def addRotateFreeHandle(self, pos, center, axes=None, item=None, name=None, index=None):
|
||||||
pos = Point(pos)
|
pos = Point(pos)
|
||||||
center = Point(center)
|
center = Point(center)
|
||||||
return self.addHandle({'name': name, 'type': 'rf', 'center': center, 'pos': pos, 'item': item})
|
return self.addHandle({'name': name, 'type': 'rf', 'center': center, 'pos': pos, 'item': item}, index=index)
|
||||||
|
|
||||||
def addHandle(self, info):
|
def addHandle(self, info, index=None):
|
||||||
|
## If a Handle was not supplied, create it now
|
||||||
if 'item' not in info or info['item'] is None:
|
if 'item' not in info or info['item'] is None:
|
||||||
#print "BEFORE ADD CHILD:", self.childItems()
|
|
||||||
h = Handle(self.handleSize, typ=info['type'], pen=self.handlePen, parent=self)
|
h = Handle(self.handleSize, typ=info['type'], pen=self.handlePen, parent=self)
|
||||||
#print "AFTER ADD CHILD:", self.childItems()
|
|
||||||
h.setPos(info['pos'] * self.state['size'])
|
h.setPos(info['pos'] * self.state['size'])
|
||||||
info['item'] = h
|
info['item'] = h
|
||||||
else:
|
else:
|
||||||
h = info['item']
|
h = info['item']
|
||||||
iid = len(self.handles)
|
if info['pos'] is None:
|
||||||
h.connectROI(self, iid)
|
info['pos'] = h.pos()
|
||||||
#h.mouseMoveEvent = lambda ev: self.pointMoveEvent(iid, ev)
|
|
||||||
#h.mousePressEvent = lambda ev: self.pointPressEvent(iid, ev)
|
## connect the handle to this ROI
|
||||||
#h.mouseReleaseEvent = lambda ev: self.pointReleaseEvent(iid, ev)
|
#iid = len(self.handles)
|
||||||
self.handles.append(info)
|
h.connectROI(self)
|
||||||
|
if index is None:
|
||||||
|
self.handles.append(info)
|
||||||
|
else:
|
||||||
|
self.handles.insert(index, info)
|
||||||
|
|
||||||
h.setZValue(self.zValue()+1)
|
h.setZValue(self.zValue()+1)
|
||||||
#if self.isSelected():
|
self.stateChanged()
|
||||||
#h.show()
|
|
||||||
#else:
|
|
||||||
#h.hide()
|
|
||||||
return h
|
return h
|
||||||
|
|
||||||
def replaceHandle(self, ind, handle):
|
def indexOfHandle(self, handle):
|
||||||
oldHandle = self.handles[ind]
|
if isinstance(handle, Handle):
|
||||||
self.handles[ind] = handle
|
index = [i for i, info in enumerate(self.handles) if info['item'] is handle]
|
||||||
oldHandle['item'].disconnectROI(self)
|
if len(index) == 0:
|
||||||
handle['item'].connectROI(self, ind)
|
raise Exception("Cannot remove handle; it is not attached to this ROI")
|
||||||
|
return index[0]
|
||||||
|
else:
|
||||||
|
return handle
|
||||||
|
|
||||||
|
def removeHandle(self, handle):
|
||||||
|
"""Remove a handle from this ROI. Argument may be either a Handle instance or the integer index of the handle."""
|
||||||
|
index = self.indexOfHandle(handle)
|
||||||
|
|
||||||
|
handle = self.handles[index]['item']
|
||||||
|
self.handles.pop(index)
|
||||||
|
handle.disconnectROI(self)
|
||||||
|
if len(handle.rois) == 0:
|
||||||
|
self.scene().removeItem(handle)
|
||||||
|
self.stateChanged()
|
||||||
|
|
||||||
|
def replaceHandle(self, oldHandle, newHandle):
|
||||||
|
"""Replace one handle in the ROI for another. This is useful when connecting multiple ROIs together.
|
||||||
|
*oldHandle* may be a Handle instance or the index of a handle."""
|
||||||
|
#print "========================="
|
||||||
|
#print "replace", oldHandle, newHandle
|
||||||
|
#print self
|
||||||
|
#print self.handles
|
||||||
|
#print "-----------------"
|
||||||
|
index = self.indexOfHandle(oldHandle)
|
||||||
|
info = self.handles[index]
|
||||||
|
self.removeHandle(index)
|
||||||
|
info['item'] = newHandle
|
||||||
|
info['pos'] = newHandle.pos()
|
||||||
|
self.addHandle(info, index=index)
|
||||||
|
#print self.handles
|
||||||
|
|
||||||
|
def checkRemoveHandle(self, handle):
|
||||||
|
## This is used when displaying a Handle's context menu to determine
|
||||||
|
## whether removing is allowed.
|
||||||
|
## Subclasses may wish to override this to disable the menu entry.
|
||||||
|
## Note: by default, handles are not user-removable even if this method returns True.
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def getLocalHandlePositions(self, index=None):
|
def getLocalHandlePositions(self, index=None):
|
||||||
"""Returns the position of a handle in ROI coordinates"""
|
"""Returns the position of a handle in ROI coordinates"""
|
||||||
if index == None:
|
if index == None:
|
||||||
@ -451,19 +491,20 @@ class ROI(GraphicsObject):
|
|||||||
#self.movePoint(pt, ev.scenePos(), ev.modifiers())
|
#self.movePoint(pt, ev.scenePos(), ev.modifiers())
|
||||||
|
|
||||||
|
|
||||||
def checkPointMove(self, pt, pos, modifiers):
|
def checkPointMove(self, handle, pos, modifiers):
|
||||||
"""When handles move, they must ask the ROI if the move is acceptable.
|
"""When handles move, they must ask the ROI if the move is acceptable.
|
||||||
By default, this always returns True. Subclasses may wish override.
|
By default, this always returns True. Subclasses may wish override.
|
||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def movePoint(self, pt, pos, modifiers=QtCore.Qt.KeyboardModifier(), finish=True):
|
def movePoint(self, handle, pos, modifiers=QtCore.Qt.KeyboardModifier(), finish=True):
|
||||||
## called by Handles when they are moved.
|
## called by Handles when they are moved.
|
||||||
## pos is the new position of the handle in scene coords, as requested by the handle.
|
## pos is the new position of the handle in scene coords, as requested by the handle.
|
||||||
|
|
||||||
newState = self.stateCopy()
|
newState = self.stateCopy()
|
||||||
h = self.handles[pt]
|
index = self.indexOfHandle(handle)
|
||||||
|
h = self.handles[index]
|
||||||
p0 = self.mapToScene(h['pos'] * self.state['size'])
|
p0 = self.mapToScene(h['pos'] * self.state['size'])
|
||||||
p1 = Point(pos)
|
p1 = Point(pos)
|
||||||
|
|
||||||
@ -485,7 +526,9 @@ class ROI(GraphicsObject):
|
|||||||
self.translate(p1-p0, snap=snap, update=False)
|
self.translate(p1-p0, snap=snap, update=False)
|
||||||
|
|
||||||
elif h['type'] == 'f':
|
elif h['type'] == 'f':
|
||||||
|
newPos = self.mapFromScene(pos)
|
||||||
h['item'].setPos(self.mapFromScene(pos))
|
h['item'].setPos(self.mapFromScene(pos))
|
||||||
|
h['pos'] = newPos
|
||||||
self.freeHandleMoved = True
|
self.freeHandleMoved = True
|
||||||
#self.sigRegionChanged.emit(self) ## should be taken care of by call to stateChanged()
|
#self.sigRegionChanged.emit(self) ## should be taken care of by call to stateChanged()
|
||||||
|
|
||||||
@ -641,7 +684,7 @@ class ROI(GraphicsObject):
|
|||||||
if self.state[k] != self.lastState[k]:
|
if self.state[k] != self.lastState[k]:
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
|
self.prepareGeometryChange()
|
||||||
if changed:
|
if changed:
|
||||||
## Move all handles to match the current configuration of the ROI
|
## Move all handles to match the current configuration of the ROI
|
||||||
for h in self.handles:
|
for h in self.handles:
|
||||||
@ -935,12 +978,15 @@ class Handle(UIGraphicsItem):
|
|||||||
'sr': (12, 0),
|
'sr': (12, 0),
|
||||||
'rf': (12, 0),
|
'rf': (12, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sigClicked = QtCore.Signal(object, object) # self, event
|
||||||
|
sigRemoveRequested = QtCore.Signal(object) # self
|
||||||
|
|
||||||
def __init__(self, radius, typ=None, pen=(200, 200, 220), parent=None, deletable=False):
|
def __init__(self, radius, typ=None, pen=(200, 200, 220), parent=None, deletable=False):
|
||||||
#print " create item with parent", parent
|
#print " create item with parent", parent
|
||||||
#self.bounds = QtCore.QRectF(-1e-10, -1e-10, 2e-10, 2e-10)
|
#self.bounds = QtCore.QRectF(-1e-10, -1e-10, 2e-10, 2e-10)
|
||||||
#self.setFlags(self.ItemIgnoresTransformations | self.ItemSendsScenePositionChanges)
|
#self.setFlags(self.ItemIgnoresTransformations | self.ItemSendsScenePositionChanges)
|
||||||
self.roi = []
|
self.rois = []
|
||||||
self.radius = radius
|
self.radius = radius
|
||||||
self.typ = typ
|
self.typ = typ
|
||||||
self.pen = fn.mkPen(pen)
|
self.pen = fn.mkPen(pen)
|
||||||
@ -961,17 +1007,19 @@ class Handle(UIGraphicsItem):
|
|||||||
#self.updateShape()
|
#self.updateShape()
|
||||||
self.setZValue(11)
|
self.setZValue(11)
|
||||||
|
|
||||||
def connectROI(self, roi, i):
|
def connectROI(self, roi):
|
||||||
### roi is the "parent" roi, i is the index of the handle in roi.handles
|
### roi is the "parent" roi, i is the index of the handle in roi.handles
|
||||||
self.roi.append((roi, i))
|
self.rois.append(roi)
|
||||||
|
|
||||||
def disconnectROI(self, roi):
|
def disconnectROI(self, roi):
|
||||||
for i, r in enumerate(self.roi):
|
self.rois.remove(roi)
|
||||||
if r[0] == roi:
|
#for i, r in enumerate(self.roi):
|
||||||
self.roi.pop(i)
|
#if r[0] == roi:
|
||||||
|
#self.roi.pop(i)
|
||||||
|
|
||||||
def close(self):
|
#def close(self):
|
||||||
self.parentItem().handleRemoved(self)
|
#for r in self.roi:
|
||||||
|
#r.removeHandle(self)
|
||||||
|
|
||||||
def setDeletable(self, b):
|
def setDeletable(self, b):
|
||||||
self.deletable = b
|
self.deletable = b
|
||||||
@ -979,8 +1027,9 @@ class Handle(UIGraphicsItem):
|
|||||||
self.setAcceptedMouseButtons(self.acceptedMouseButtons() | QtCore.Qt.RightButton)
|
self.setAcceptedMouseButtons(self.acceptedMouseButtons() | QtCore.Qt.RightButton)
|
||||||
else:
|
else:
|
||||||
self.setAcceptedMouseButtons(self.acceptedMouseButtons() & ~QtCore.Qt.RightButton)
|
self.setAcceptedMouseButtons(self.acceptedMouseButtons() & ~QtCore.Qt.RightButton)
|
||||||
#def boundingRect(self):
|
|
||||||
#return self.bounds
|
def removeClicked(self):
|
||||||
|
self.sigRemoveRequested.emit(self)
|
||||||
|
|
||||||
def hoverEvent(self, ev):
|
def hoverEvent(self, ev):
|
||||||
hover = False
|
hover = False
|
||||||
@ -1029,7 +1078,7 @@ class Handle(UIGraphicsItem):
|
|||||||
def buildMenu(self):
|
def buildMenu(self):
|
||||||
menu = QtGui.QMenu()
|
menu = QtGui.QMenu()
|
||||||
menu.setTitle("Handle")
|
menu.setTitle("Handle")
|
||||||
menu.addAction("Remove handle", self.close)
|
self.removeAction = menu.addAction("Remove handle", self.removeClicked)
|
||||||
return menu
|
return menu
|
||||||
|
|
||||||
def getMenu(self):
|
def getMenu(self):
|
||||||
@ -1041,6 +1090,10 @@ class Handle(UIGraphicsItem):
|
|||||||
|
|
||||||
def raiseContextMenu(self, ev):
|
def raiseContextMenu(self, ev):
|
||||||
menu = self.scene().addParentContextMenus(self, self.getMenu(), ev)
|
menu = self.scene().addParentContextMenus(self, self.getMenu(), ev)
|
||||||
|
|
||||||
|
## Make sure it is still ok to remove this handle
|
||||||
|
removeAllowed = all([r.checkRemoveHandle(self) for r in self.rois])
|
||||||
|
self.removeAction.setEnabled(removeAllowed)
|
||||||
pos = ev.screenPos()
|
pos = ev.screenPos()
|
||||||
menu.popup(QtCore.QPoint(pos.x(), pos.y()))
|
menu.popup(QtCore.QPoint(pos.x(), pos.y()))
|
||||||
|
|
||||||
@ -1057,12 +1110,12 @@ class Handle(UIGraphicsItem):
|
|||||||
|
|
||||||
if ev.isFinish():
|
if ev.isFinish():
|
||||||
if self.isMoving:
|
if self.isMoving:
|
||||||
for r in self.roi:
|
for r in self.rois:
|
||||||
r[0].stateChangeFinished()
|
r.stateChangeFinished()
|
||||||
self.isMoving = False
|
self.isMoving = False
|
||||||
elif ev.isStart():
|
elif ev.isStart():
|
||||||
for r in self.roi:
|
for r in self.rois:
|
||||||
r[0].handleMoveStarted()
|
r.handleMoveStarted()
|
||||||
self.isMoving = True
|
self.isMoving = True
|
||||||
self.startPos = self.scenePos()
|
self.startPos = self.scenePos()
|
||||||
self.cursorOffset = self.scenePos() - ev.buttonDownScenePos()
|
self.cursorOffset = self.scenePos() - ev.buttonDownScenePos()
|
||||||
@ -1072,13 +1125,13 @@ class Handle(UIGraphicsItem):
|
|||||||
self.movePoint(pos, ev.modifiers(), finish=False)
|
self.movePoint(pos, ev.modifiers(), finish=False)
|
||||||
|
|
||||||
def movePoint(self, pos, modifiers=QtCore.Qt.KeyboardModifier(), finish=True):
|
def movePoint(self, pos, modifiers=QtCore.Qt.KeyboardModifier(), finish=True):
|
||||||
for r in self.roi:
|
for r in self.rois:
|
||||||
if not r[0].checkPointMove(r[1], pos, modifiers):
|
if not r.checkPointMove(self, pos, modifiers):
|
||||||
return
|
return
|
||||||
#print "point moved; inform %d ROIs" % len(self.roi)
|
#print "point moved; inform %d ROIs" % len(self.roi)
|
||||||
# A handle can be used by multiple ROIs; tell each to update its handle position
|
# A handle can be used by multiple ROIs; tell each to update its handle position
|
||||||
for r in self.roi:
|
for r in self.rois:
|
||||||
r[0].movePoint(r[1], pos, modifiers, finish=finish)
|
r.movePoint(self, pos, modifiers, finish=finish)
|
||||||
|
|
||||||
def buildPath(self):
|
def buildPath(self):
|
||||||
size = self.radius
|
size = self.radius
|
||||||
@ -1269,15 +1322,12 @@ class MultiLineROI(QtGui.QGraphicsObject):
|
|||||||
if w == w0:
|
if w == w0:
|
||||||
continue
|
continue
|
||||||
l.scale([1.0, w/w0], center=[0.5,0.5])
|
l.scale([1.0, w/w0], center=[0.5,0.5])
|
||||||
#self.emit(QtCore.SIGNAL('regionChanged'), self)
|
|
||||||
self.sigRegionChanged.emit(self)
|
self.sigRegionChanged.emit(self)
|
||||||
|
|
||||||
def roiChangeStartedEvent(self):
|
def roiChangeStartedEvent(self):
|
||||||
#self.emit(QtCore.SIGNAL('regionChangeStarted'), self)
|
|
||||||
self.sigRegionChangeStarted.emit(self)
|
self.sigRegionChangeStarted.emit(self)
|
||||||
|
|
||||||
def roiChangeFinishedEvent(self):
|
def roiChangeFinishedEvent(self):
|
||||||
#self.emit(QtCore.SIGNAL('regionChangeFinished'), self)
|
|
||||||
self.sigRegionChangeFinished.emit(self)
|
self.sigRegionChangeFinished.emit(self)
|
||||||
|
|
||||||
|
|
||||||
@ -1395,99 +1445,129 @@ class PolygonROI(ROI):
|
|||||||
class PolyLineROI(ROI):
|
class PolyLineROI(ROI):
|
||||||
"""Container class for multiple connected LineSegmentROIs. Responsible for adding new
|
"""Container class for multiple connected LineSegmentROIs. Responsible for adding new
|
||||||
line segments, and for translation/(rotation?) of multiple lines together."""
|
line segments, and for translation/(rotation?) of multiple lines together."""
|
||||||
def __init__(self, positions, size=[1,1], closed=False, pos=None, **args):
|
def __init__(self, positions, closed=False, pos=None, **args):
|
||||||
|
|
||||||
if pos is None:
|
if pos is None:
|
||||||
pos = [0,0]
|
pos = [0,0]
|
||||||
pen=args.get('pen', fn.mkPen((100,100,255)))
|
#pen=args.get('pen', fn.mkPen((100,100,255)))
|
||||||
ROI.__init__(self, pos, size, **args)
|
ROI.__init__(self, pos, size=[1,1], **args)
|
||||||
|
self.closed = closed
|
||||||
self.segments = []
|
self.segments = []
|
||||||
|
|
||||||
for i, p in enumerate(positions[:-1]):
|
for p in positions:
|
||||||
h = None
|
self.addFreeHandle(p)
|
||||||
if len(self.segments) > 0:
|
|
||||||
h = self.segments[-1].handles[-1]['item']
|
start = -1 if self.closed else 0
|
||||||
self.segments.append(LineSegmentROI([p, positions[i+1]], pos=pos, handles=(h, None), pen=pen, parent=self, movable=False))
|
for i in range(start, len(self.handles)-1):
|
||||||
|
self.addSegment(self.handles[i]['item'], self.handles[i+1]['item'])
|
||||||
|
#for i in range(len(positions)-1):
|
||||||
|
#h2 = self.addFreeHandle(positions[i+1])
|
||||||
|
#segment = LineSegmentROI(handles=(h, h2), pen=pen, parent=self, movable=False)
|
||||||
|
#self.segments.append(segment)
|
||||||
|
#h = h2
|
||||||
|
|
||||||
|
|
||||||
for i, s in enumerate(self.segments):
|
#for i, s in enumerate(self.segments):
|
||||||
h = s.handles[0]
|
#h = s.handles[0]
|
||||||
self.addFreeHandle(h['pos'], item=h['item'])
|
#self.addFreeHandle(h['pos'], item=h['item'])
|
||||||
s.setZValue(self.zValue() +1)
|
#s.setZValue(self.zValue() +1)
|
||||||
|
|
||||||
h = self.segments[-1].handles[1]
|
#h = self.segments[-1].handles[1]
|
||||||
self.addFreeHandle(h['pos'], item=h['item'])
|
#self.addFreeHandle(h['pos'], item=h['item'])
|
||||||
|
|
||||||
h1 = self.segments[-1].handles[-1]['item']
|
#if closed:
|
||||||
h2 = self.segments[0].handles[0]['item']
|
#h1 = self.handles[-1]['item']
|
||||||
if closed:
|
#h2 = self.handles[0]['item']
|
||||||
self.segments.append(LineSegmentROI([positions[-1], positions[0]], pos=pos, handles=(h1, h2), pen=pen, parent=self, movable=False))
|
#self.segments.append(LineSegmentROI([positions[-1], positions[0]], pos=pos, handles=(h1, h2), pen=pen, parent=self, movable=False))
|
||||||
h2.setParentItem(self.segments[-1])
|
#h2.setParentItem(self.segments[-1])
|
||||||
for s in self.segments:
|
|
||||||
self.setSegmentSettings(s)
|
|
||||||
|
|
||||||
def movePoint(self, *args, **kargs):
|
|
||||||
pass
|
#for s in self.segments:
|
||||||
#def segmentChanged(self, obj):
|
#self.setSegmentSettings(s)
|
||||||
|
|
||||||
|
#def movePoint(self, *args, **kargs):
|
||||||
#pass
|
#pass
|
||||||
def setSegmentSettings(self, s):
|
|
||||||
s.setParentROI(self)
|
def addSegment(self, h1, h2, index=None):
|
||||||
s.sigClicked.connect(self.newHandleRequested)
|
seg = LineSegmentROI(handles=(h1, h2), pen=self.pen, parent=self, movable=False)
|
||||||
s.setAcceptedMouseButtons(QtCore.Qt.LeftButton)
|
if index is None:
|
||||||
for h in s.handles:
|
self.segments.append(seg)
|
||||||
|
else:
|
||||||
|
self.segments.insert(index, seg)
|
||||||
|
seg.sigClicked.connect(self.segmentClicked)
|
||||||
|
seg.setAcceptedMouseButtons(QtCore.Qt.LeftButton)
|
||||||
|
seg.setZValue(self.zValue()+1)
|
||||||
|
for h in seg.handles:
|
||||||
h['item'].setDeletable(True)
|
h['item'].setDeletable(True)
|
||||||
h['item'].setAcceptedMouseButtons(h['item'].acceptedMouseButtons() | QtCore.Qt.LeftButton) ## have these handles take left clicks too, so that handles cannot be added on top of other handles
|
h['item'].setAcceptedMouseButtons(h['item'].acceptedMouseButtons() | QtCore.Qt.LeftButton) ## have these handles take left clicks too, so that handles cannot be added on top of other handles
|
||||||
|
|
||||||
def setMouseHover(self, hover):
|
def setMouseHover(self, hover):
|
||||||
## Inform all the ROI's segments that the mouse is(not) hovering over it
|
## Inform all the ROI's segments that the mouse is(not) hovering over it
|
||||||
if self.mouseHovering == hover:
|
#if self.mouseHovering == hover:
|
||||||
return
|
#return
|
||||||
self.mouseHovering = hover
|
#self.mouseHovering = hover
|
||||||
|
ROI.setMouseHover(self, hover)
|
||||||
for s in self.segments:
|
for s in self.segments:
|
||||||
s.setMouseHover(hover)
|
s.setMouseHover(hover)
|
||||||
|
|
||||||
def newHandleRequested(self, segment, ev=None, pos=None): ## pos should be in this item's coordinate system
|
def addHandle(self, info, index=None):
|
||||||
|
h = ROI.addHandle(self, info, index=index)
|
||||||
|
h.sigRemoveRequested.connect(self.removeHandle)
|
||||||
|
return h
|
||||||
|
|
||||||
|
def segmentClicked(self, segment, ev=None, pos=None): ## pos should be in this item's coordinate system
|
||||||
if ev != None:
|
if ev != None:
|
||||||
pos = segment.mapToParent(ev.pos())
|
pos = segment.mapToParent(ev.pos())
|
||||||
elif pos != None:
|
elif pos != None:
|
||||||
pos = pos
|
pos = pos
|
||||||
else:
|
else:
|
||||||
raise Exception("Either an event or a position must be given.")
|
raise Exception("Either an event or a position must be given.")
|
||||||
h1 = segment.handles[0]
|
h1 = segment.handles[0]['item']
|
||||||
h2 = segment.handles[1]
|
h2 = segment.handles[1]['item']
|
||||||
|
|
||||||
for i, s in enumerate(self.segments):
|
i = self.segments.index(segment)
|
||||||
if s == segment:
|
h3 = self.addFreeHandle(pos, index=self.indexOfHandle(h2))
|
||||||
#newSegment = LineSegmentROI([pos, h2['pos']], [0,0], handles=(None, h2['item']), pen=segment.pen, movable=False, acceptsHandles=True, parent=self)
|
self.addSegment(h3, h2, index=i+1)
|
||||||
newSegment = LineSegmentROI([h1['pos'], pos], [0,0], handles=(h1['item'], None), pen=segment.pen, movable=False, acceptsHandles=True, parent=self)
|
segment.replaceHandle(h2, h3)
|
||||||
self.setSegmentSettings(newSegment)
|
|
||||||
self.segments.insert(i, newSegment)
|
|
||||||
break
|
|
||||||
|
|
||||||
segment.replaceHandle(0, newSegment.handles[1])
|
|
||||||
|
|
||||||
self.handles.insert(i+1, newSegment.handles[1])
|
|
||||||
|
#def report(self):
|
||||||
|
#for s in self.segments:
|
||||||
|
#print s
|
||||||
|
#for h in s.handles:
|
||||||
|
#print " ", h
|
||||||
|
#for h in self.handles:
|
||||||
|
#print h
|
||||||
|
|
||||||
|
def removeHandle(self, handle, updateSegments=True):
|
||||||
def handleRemoved(self, segment, handle):
|
ROI.removeHandle(self, handle)
|
||||||
#self.segments[i-1].replaceHandle(1, self.segments[ind].handles[1])
|
handle.sigRemoveRequested.disconnect(self.removeHandle)
|
||||||
for i, s in enumerate(self.segments):
|
|
||||||
if s == segment:
|
|
||||||
#s.replaceHandle(0, self.segments[i-1].handles[0])
|
|
||||||
if s != self.segments[-1]:
|
|
||||||
j = i+1
|
|
||||||
else:
|
|
||||||
j=0
|
|
||||||
self.segments[j].replaceHandle(0, segment.handles[0])
|
|
||||||
break
|
|
||||||
|
|
||||||
handle.disconnectROI(self.segments[j])
|
|
||||||
#handle.disconnectROI(self)
|
|
||||||
self.handles.pop(j)
|
|
||||||
#segment.handles[1]['item'].setParentItem(self.segments[(i+1)%len(self.segments)])
|
|
||||||
self.segments.remove(segment)
|
|
||||||
segment.close()
|
|
||||||
|
|
||||||
|
if not updateSegments:
|
||||||
|
return
|
||||||
|
segments = handle.rois[:]
|
||||||
|
|
||||||
|
if len(segments) == 1:
|
||||||
|
self.removeSegment(segments[0])
|
||||||
|
else:
|
||||||
|
handles = [h['item'] for h in segments[1].handles]
|
||||||
|
handles.remove(handle)
|
||||||
|
segments[0].replaceHandle(handle, handles[0])
|
||||||
|
self.removeSegment(segments[1])
|
||||||
|
|
||||||
|
def removeSegment(self, seg):
|
||||||
|
for handle in seg.handles[:]:
|
||||||
|
seg.removeHandle(handle['item'])
|
||||||
|
self.segments.remove(seg)
|
||||||
|
seg.sigClicked.disconnect(self.segmentClicked)
|
||||||
|
self.scene().removeItem(seg)
|
||||||
|
|
||||||
|
def checkRemoveHandle(self, h):
|
||||||
|
## called when a handle is about to display its context menu
|
||||||
|
if self.closed:
|
||||||
|
return len(self.handles) > 3
|
||||||
|
else:
|
||||||
|
return len(self.handles) > 2
|
||||||
|
|
||||||
def paint(self, p, *args):
|
def paint(self, p, *args):
|
||||||
#for s in self.segments:
|
#for s in self.segments:
|
||||||
@ -1499,10 +1579,11 @@ class PolyLineROI(ROI):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def boundingRect(self):
|
def boundingRect(self):
|
||||||
r = QtCore.QRectF()
|
return self.shape().boundingRect()
|
||||||
for h in self.handles:
|
#r = QtCore.QRectF()
|
||||||
r |= self.mapFromItem(h['item'], h['item'].boundingRect()).boundingRect() ## |= gives the union of the two QRectFs
|
#for h in self.handles:
|
||||||
return r
|
#r |= self.mapFromItem(h['item'], h['item'].boundingRect()).boundingRect() ## |= gives the union of the two QRectFs
|
||||||
|
#return r
|
||||||
|
|
||||||
def shape(self):
|
def shape(self):
|
||||||
p = QtGui.QPainterPath()
|
p = QtGui.QPainterPath()
|
||||||
@ -1512,14 +1593,16 @@ class PolyLineROI(ROI):
|
|||||||
p.lineTo(self.handles[0]['item'].pos())
|
p.lineTo(self.handles[0]['item'].pos())
|
||||||
return p
|
return p
|
||||||
|
|
||||||
|
|
||||||
class LineSegmentROI(ROI):
|
class LineSegmentROI(ROI):
|
||||||
"""
|
"""
|
||||||
ROI subclass with two freely-moving handles defining a line.
|
ROI subclass with two freely-moving handles defining a line.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, positions, pos=None, handles=(None,None), acceptsHandles=False, **args):
|
def __init__(self, positions=(None, None), pos=None, handles=(None,None), **args):
|
||||||
if pos is None:
|
if pos is None:
|
||||||
pos = [0,0]
|
pos = [0,0]
|
||||||
|
|
||||||
ROI.__init__(self, pos, [1,1], **args)
|
ROI.__init__(self, pos, [1,1], **args)
|
||||||
#ROI.__init__(self, positions[0])
|
#ROI.__init__(self, positions[0])
|
||||||
if len(positions) > 2:
|
if len(positions) > 2:
|
||||||
@ -1528,35 +1611,35 @@ class LineSegmentROI(ROI):
|
|||||||
for i, p in enumerate(positions):
|
for i, p in enumerate(positions):
|
||||||
self.addFreeHandle(p, item=handles[i])
|
self.addFreeHandle(p, item=handles[i])
|
||||||
|
|
||||||
self.setZValue(1000)
|
#self.setZValue(1000)
|
||||||
self.parentROI = None
|
#self.parentROI = None
|
||||||
self.hasParentROI = False
|
#self.hasParentROI = False
|
||||||
self.setAcceptsHandles(acceptsHandles)
|
#self.setAcceptsHandles(acceptsHandles)
|
||||||
|
|
||||||
def setParentROI(self, parent):
|
#def setParentROI(self, parent):
|
||||||
self.parentROI = parent
|
#self.parentROI = parent
|
||||||
if parent != None:
|
#if parent != None:
|
||||||
self.hasParentROI = True
|
#self.hasParentROI = True
|
||||||
else:
|
#else:
|
||||||
self.hasParentROI = False
|
#self.hasParentROI = False
|
||||||
|
|
||||||
def setAcceptsHandles(self, b):
|
#def setAcceptsHandles(self, b):
|
||||||
if b:
|
#if b:
|
||||||
self.setAcceptedMouseButtons(QtCore.Qt.LeftButton)
|
#self.setAcceptedMouseButtons(QtCore.Qt.LeftButton)
|
||||||
else:
|
#else:
|
||||||
self.setAcceptedMouseButtons(QtCore.Qt.NoButton)
|
#self.setAcceptedMouseButtons(QtCore.Qt.NoButton)
|
||||||
|
|
||||||
def close(self):
|
#def close(self):
|
||||||
#for h in self.handles:
|
##for h in self.handles:
|
||||||
#if len(h['item'].roi) == 1:
|
##if len(h['item'].roi) == 1:
|
||||||
#h['item'].scene().removeItem(h['item'])
|
##h['item'].scene().removeItem(h['item'])
|
||||||
#elif h['item'].parentItem() == self:
|
##elif h['item'].parentItem() == self:
|
||||||
#h['item'].setParentItem(self.parentItem())
|
##h['item'].setParentItem(self.parentItem())
|
||||||
|
|
||||||
self.scene().removeItem(self)
|
#self.scene().removeItem(self)
|
||||||
|
|
||||||
def handleRemoved(self, handle):
|
#def handleRemoved(self, handle):
|
||||||
self.parentROI.handleRemoved(self, handle)
|
#self.parentROI.handleRemoved(self, handle)
|
||||||
|
|
||||||
#def hoverEvent(self, ev):
|
#def hoverEvent(self, ev):
|
||||||
#if (self.translatable or self.acceptsHandles) and (not ev.isExit()) and ev.acceptDrags(QtCore.Qt.LeftButton):
|
#if (self.translatable or self.acceptsHandles) and (not ev.isExit()) and ev.acceptDrags(QtCore.Qt.LeftButton):
|
||||||
@ -1576,21 +1659,21 @@ class LineSegmentROI(ROI):
|
|||||||
#else:
|
#else:
|
||||||
#ev.ignore()
|
#ev.ignore()
|
||||||
|
|
||||||
def newHandleRequested(self, evPos):
|
#def newHandleRequested(self, evPos):
|
||||||
#print "newHandleRequested"
|
#print "newHandleRequested"
|
||||||
|
|
||||||
#if evPos - self.handles[0].pos() == Point(0.,0.) or evPos-handles[1].pos() == Point(0.,0.):
|
#if evPos - self.handles[0].pos() == Point(0.,0.) or evPos-handles[1].pos() == Point(0.,0.):
|
||||||
# return
|
# return
|
||||||
self.parentROI.newHandleRequested(self, self.mapToParent(evPos)) ## so now evPos should be passed in in the parents coordinate system
|
#self.parentROI.newHandleRequested(self, self.mapToParent(evPos)) ## so now evPos should be passed in in the parents coordinate system
|
||||||
|
|
||||||
def listPoints(self):
|
def listPoints(self):
|
||||||
return [p['item'].pos() for p in self.handles]
|
return [p['item'].pos() for p in self.handles]
|
||||||
|
|
||||||
def movePoint(self, *args, **kargs):
|
#def movePoint(self, *args, **kargs):
|
||||||
ROI.movePoint(self, *args, **kargs)
|
#ROI.movePoint(self, *args, **kargs)
|
||||||
self.prepareGeometryChange()
|
#self.prepareGeometryChange()
|
||||||
for h in self.handles:
|
#for h in self.handles:
|
||||||
h['pos'] = h['item'].pos()
|
#h['pos'] = h['item'].pos()
|
||||||
|
|
||||||
def paint(self, p, *args):
|
def paint(self, p, *args):
|
||||||
p.setRenderHint(QtGui.QPainter.Antialiasing)
|
p.setRenderHint(QtGui.QPainter.Antialiasing)
|
||||||
@ -1607,10 +1690,11 @@ class LineSegmentROI(ROI):
|
|||||||
#p.drawLine(h1, h2)
|
#p.drawLine(h1, h2)
|
||||||
|
|
||||||
def boundingRect(self):
|
def boundingRect(self):
|
||||||
r = QtCore.QRectF()
|
return self.shape().boundingRect()
|
||||||
for h in self.handles:
|
#r = QtCore.QRectF()
|
||||||
r |= self.mapFromItem(h['item'], h['item'].boundingRect()).boundingRect() ## |= gives the union of the two QRectFs
|
#for h in self.handles:
|
||||||
return r
|
#r |= self.mapFromItem(h['item'], h['item'].boundingRect()).boundingRect() ## |= gives the union of the two QRectFs
|
||||||
|
#return r
|
||||||
|
|
||||||
def shape(self):
|
def shape(self):
|
||||||
p = QtGui.QPainterPath()
|
p = QtGui.QPainterPath()
|
||||||
@ -1619,11 +1703,12 @@ class LineSegmentROI(ROI):
|
|||||||
|
|
||||||
h1 = self.handles[0]['item'].pos()
|
h1 = self.handles[0]['item'].pos()
|
||||||
h2 = self.handles[1]['item'].pos()
|
h2 = self.handles[1]['item'].pos()
|
||||||
pxv = self.pixelVectors(h2-h1)[1] * 4
|
pxv = self.pixelVectors(h2-h1)[1]
|
||||||
|
|
||||||
if pxv == (None, None):
|
if pxv is None:
|
||||||
p.addRect(self.boundingRect())
|
|
||||||
return p
|
return p
|
||||||
|
|
||||||
|
pxv *= 4
|
||||||
|
|
||||||
p.moveTo(h1+pxv)
|
p.moveTo(h1+pxv)
|
||||||
p.lineTo(h2+pxv)
|
p.lineTo(h2+pxv)
|
||||||
@ -1633,13 +1718,13 @@ class LineSegmentROI(ROI):
|
|||||||
|
|
||||||
return p
|
return p
|
||||||
|
|
||||||
def stateCopy(self):
|
#def stateCopy(self):
|
||||||
sc = {}
|
#sc = {}
|
||||||
sc['pos'] = Point(self.state['pos'])
|
#sc['pos'] = Point(self.state['pos'])
|
||||||
sc['size'] = Point(self.state['size'])
|
#sc['size'] = Point(self.state['size'])
|
||||||
sc['angle'] = self.state['angle']
|
#sc['angle'] = self.state['angle']
|
||||||
#sc['handles'] = self.handles
|
##sc['handles'] = self.handles
|
||||||
return sc
|
#return sc
|
||||||
|
|
||||||
def getArrayRegion(self, data, img, axes=(0,1)):
|
def getArrayRegion(self, data, img, axes=(0,1)):
|
||||||
"""
|
"""
|
||||||
@ -1647,15 +1732,6 @@ class LineSegmentROI(ROI):
|
|||||||
Since this pulls 1D data from a 2D coordinate system, the return value will have ndim = data.ndim-1
|
Since this pulls 1D data from a 2D coordinate system, the return value will have ndim = data.ndim-1
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
#shape = self.state['size']
|
|
||||||
|
|
||||||
#origin = self.mapToItem(img, QtCore.QPointF(0, 0))
|
|
||||||
|
|
||||||
## vx and vy point in the directions of the slice axes, but must be scaled properly
|
|
||||||
#vx = self.mapToItem(img, QtCore.QPointF(1, 0)) - origin
|
|
||||||
#vy = self.mapToItem(img, QtCore.QPointF(0, 1)) - origin
|
|
||||||
|
|
||||||
imgPts = [self.mapToItem(img, h['item'].pos()) for h in self.handles]
|
imgPts = [self.mapToItem(img, h['item'].pos()) for h in self.handles]
|
||||||
rgns = []
|
rgns = []
|
||||||
for i in range(len(imgPts)-1):
|
for i in range(len(imgPts)-1):
|
||||||
@ -1666,23 +1742,6 @@ class LineSegmentROI(ROI):
|
|||||||
|
|
||||||
return np.concatenate(rgns, axis=axes[0])
|
return np.concatenate(rgns, axis=axes[0])
|
||||||
|
|
||||||
|
|
||||||
#lvx = np.sqrt(vx.x()**2 + vx.y()**2)
|
|
||||||
#lvy = np.sqrt(vy.x()**2 + vy.y()**2)
|
|
||||||
#pxLen = img.width() / float(data.shape[axes[0]])
|
|
||||||
#sx = pxLen / lvx
|
|
||||||
#sy = pxLen / lvy
|
|
||||||
|
|
||||||
#vectors = ((vx.x()*sx, vx.y()*sx), (vy.x()*sy, vy.y()*sy))
|
|
||||||
#shape = self.state['size']
|
|
||||||
#shape = [abs(shape[0]/sx), abs(shape[1]/sy)]
|
|
||||||
|
|
||||||
#origin = (origin.x(), origin.y())
|
|
||||||
|
|
||||||
##print "shape", shape, "vectors", vectors, "origin", origin
|
|
||||||
|
|
||||||
#return fn.affineSlice(data, shape=shape, vectors=vectors, origin=origin, axes=axes, order=1)
|
|
||||||
|
|
||||||
|
|
||||||
class SpiralROI(ROI):
|
class SpiralROI(ROI):
|
||||||
def __init__(self, pos=None, size=None, **args):
|
def __init__(self, pos=None, size=None, **args):
|
||||||
|
Loading…
Reference in New Issue
Block a user