overhaul/cleanup of ROI code, particularly for PolyLineROI

(should be no major API changes here)
This commit is contained in:
Luke Campagnola 2012-05-31 16:08:33 -04:00
parent 0e1b57cf01
commit 26c73d3583

View File

@ -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):