Merge pull request #596 from campagnola/fix-polyline-click

Fix polyline click
This commit is contained in:
Luke Campagnola 2017-10-17 21:30:59 -07:00 committed by GitHub
commit 22b0ddaa21
3 changed files with 24 additions and 143 deletions

View File

@ -113,7 +113,6 @@ class ROI(GraphicsObject):
sigRemoveRequested = QtCore.Signal(object) sigRemoveRequested = QtCore.Signal(object)
def __init__(self, pos, size=Point(1, 1), angle=0.0, invertible=False, maxBounds=None, snapSize=1.0, scaleSnap=False, translateSnap=False, rotateSnap=False, parent=None, pen=None, movable=True, removable=False): def __init__(self, pos, size=Point(1, 1), angle=0.0, invertible=False, maxBounds=None, snapSize=1.0, scaleSnap=False, translateSnap=False, rotateSnap=False, parent=None, pen=None, movable=True, removable=False):
#QObjectWorkaround.__init__(self)
GraphicsObject.__init__(self, parent) GraphicsObject.__init__(self, parent)
self.setAcceptedMouseButtons(QtCore.Qt.NoButton) self.setAcceptedMouseButtons(QtCore.Qt.NoButton)
pos = Point(pos) pos = Point(pos)
@ -148,7 +147,6 @@ class ROI(GraphicsObject):
self.translateSnap = translateSnap self.translateSnap = translateSnap
self.rotateSnap = rotateSnap self.rotateSnap = rotateSnap
self.scaleSnap = scaleSnap self.scaleSnap = scaleSnap
#self.setFlag(self.ItemIsSelectable, True)
def getState(self): def getState(self):
return self.stateCopy() return self.stateCopy()
@ -268,7 +266,6 @@ class ROI(GraphicsObject):
raise TypeError("update argument must be bool") raise TypeError("update argument must be bool")
self.state['angle'] = angle self.state['angle'] = angle
tr = QtGui.QTransform() tr = QtGui.QTransform()
#tr.rotate(-angle * 180 / np.pi)
tr.rotate(angle) tr.rotate(angle)
self.setTransform(tr) self.setTransform(tr)
if update: if update:
@ -316,20 +313,14 @@ class ROI(GraphicsObject):
newState = self.stateCopy() newState = self.stateCopy()
newState['pos'] = newState['pos'] + pt newState['pos'] = newState['pos'] + pt
## snap position
#snap = kargs.get('snap', None)
#if (snap is not False) and not (snap is None and self.translateSnap is False):
snap = kargs.get('snap', None) snap = kargs.get('snap', None)
if snap is None: if snap is None:
snap = self.translateSnap snap = self.translateSnap
if snap is not False: if snap is not False:
newState['pos'] = self.getSnapPosition(newState['pos'], snap=snap) newState['pos'] = self.getSnapPosition(newState['pos'], snap=snap)
#d = ev.scenePos() - self.mapToScene(self.pressPos)
if self.maxBounds is not None: if self.maxBounds is not None:
r = self.stateRect(newState) r = self.stateRect(newState)
#r0 = self.sceneTransform().mapRect(self.boundingRect())
d = Point(0,0) d = Point(0,0)
if self.maxBounds.left() > r.left(): if self.maxBounds.left() > r.left():
d[0] = self.maxBounds.left() - r.left() d[0] = self.maxBounds.left() - r.left()
@ -341,12 +332,9 @@ class ROI(GraphicsObject):
d[1] = self.maxBounds.bottom() - r.bottom() d[1] = self.maxBounds.bottom() - r.bottom()
newState['pos'] += d newState['pos'] += d
#self.state['pos'] = newState['pos']
update = kargs.get('update', True) update = kargs.get('update', True)
finish = kargs.get('finish', True) finish = kargs.get('finish', True)
self.setPos(newState['pos'], update=update, finish=finish) self.setPos(newState['pos'], update=update, finish=finish)
#if 'update' not in kargs or kargs['update'] is True:
#self.stateChanged()
def rotate(self, angle, update=True, finish=True): def rotate(self, angle, update=True, finish=True):
""" """
@ -583,7 +571,6 @@ class ROI(GraphicsObject):
## Note: by default, handles are not user-removable even if this method returns True. ## Note: by default, handles are not user-removable even if this method returns True.
return True return True
def getLocalHandlePositions(self, index=None): def getLocalHandlePositions(self, index=None):
"""Returns the position of handles in the ROI's coordinate system. """Returns the position of handles in the ROI's coordinate system.
@ -629,7 +616,6 @@ class ROI(GraphicsObject):
for h in self.handles: for h in self.handles:
h['item'].hide() h['item'].hide()
def hoverEvent(self, ev): def hoverEvent(self, ev):
hover = False hover = False
if not ev.isExit(): if not ev.isExit():
@ -870,10 +856,8 @@ class ROI(GraphicsObject):
r = self.stateRect(newState) r = self.stateRect(newState)
if not self.maxBounds.contains(r): if not self.maxBounds.contains(r):
return return
#self.setTransform(tr)
self.setPos(newState['pos'], update=False) self.setPos(newState['pos'], update=False)
self.setAngle(ang, update=False) self.setAngle(ang, update=False)
#self.state = newState
## If this is a free-rotate handle, its distance from the center may change. ## If this is a free-rotate handle, its distance from the center may change.
@ -898,7 +882,6 @@ class ROI(GraphicsObject):
if ang is None: if ang is None:
return return
if self.rotateSnap or (modifiers & QtCore.Qt.ControlModifier): if self.rotateSnap or (modifiers & QtCore.Qt.ControlModifier):
#ang = round(ang / (np.pi/12.)) * (np.pi/12.)
ang = round(ang / 15.) * 15. ang = round(ang / 15.) * 15.
hs = abs(h['pos'][scaleAxis] - c[scaleAxis]) hs = abs(h['pos'][scaleAxis] - c[scaleAxis])
@ -949,9 +932,6 @@ class ROI(GraphicsObject):
if h['item'] in self.childItems(): if h['item'] in self.childItems():
p = h['pos'] p = h['pos']
h['item'].setPos(h['pos'] * self.state['size']) h['item'].setPos(h['pos'] * self.state['size'])
#else:
# trans = self.state['pos']-self.lastState['pos']
# h['item'].setPos(h['pos'] + h['item'].parentItem().mapFromParent(trans))
self.update() self.update()
self.sigRegionChanged.emit(self) self.sigRegionChanged.emit(self)
@ -971,12 +951,10 @@ class ROI(GraphicsObject):
def stateRect(self, state): def stateRect(self, state):
r = QtCore.QRectF(0, 0, state['size'][0], state['size'][1]) r = QtCore.QRectF(0, 0, state['size'][0], state['size'][1])
tr = QtGui.QTransform() tr = QtGui.QTransform()
#tr.rotate(-state['angle'] * 180 / np.pi)
tr.rotate(-state['angle']) tr.rotate(-state['angle'])
r = tr.mapRect(r) r = tr.mapRect(r)
return r.adjusted(state['pos'][0], state['pos'][1], state['pos'][0], state['pos'][1]) return r.adjusted(state['pos'][0], state['pos'][1], state['pos'][0], state['pos'][1])
def getSnapPosition(self, pos, snap=None): def getSnapPosition(self, pos, snap=None):
## Given that pos has been requested, return the nearest snap-to position ## Given that pos has been requested, return the nearest snap-to position
## optionally, snap may be passed in to specify a rectangular snap grid. ## optionally, snap may be passed in to specify a rectangular snap grid.
@ -996,7 +974,6 @@ class ROI(GraphicsObject):
return QtCore.QRectF(0, 0, self.state['size'][0], self.state['size'][1]).normalized() return QtCore.QRectF(0, 0, self.state['size'][0], self.state['size'][1]).normalized()
def paint(self, p, opt, widget): def paint(self, p, opt, widget):
# p.save()
# Note: don't use self.boundingRect here, because subclasses may need to redefine it. # Note: don't use self.boundingRect here, because subclasses may need to redefine it.
r = QtCore.QRectF(0, 0, self.state['size'][0], self.state['size'][1]).normalized() r = QtCore.QRectF(0, 0, self.state['size'][0], self.state['size'][1]).normalized()
@ -1005,7 +982,6 @@ class ROI(GraphicsObject):
p.translate(r.left(), r.top()) p.translate(r.left(), r.top())
p.scale(r.width(), r.height()) p.scale(r.width(), r.height())
p.drawRect(0, 0, 1, 1) p.drawRect(0, 0, 1, 1)
# p.restore()
def getArraySlice(self, data, img, axes=(0,1), returnSlice=True): def getArraySlice(self, data, img, axes=(0,1), returnSlice=True):
"""Return a tuple of slice objects that can be used to slice the region """Return a tuple of slice objects that can be used to slice the region
@ -1133,11 +1109,8 @@ class ROI(GraphicsObject):
lvx = np.sqrt(vx.x()**2 + vx.y()**2) lvx = np.sqrt(vx.x()**2 + vx.y()**2)
lvy = np.sqrt(vy.x()**2 + vy.y()**2) lvy = np.sqrt(vy.x()**2 + vy.y()**2)
#pxLen = img.width() / float(data.shape[axes[0]])
##img.width is number of pixels, not width of item. ##img.width is number of pixels, not width of item.
##need pxWidth and pxHeight instead of pxLen ? ##need pxWidth and pxHeight instead of pxLen ?
#sx = pxLen / lvx
#sy = pxLen / lvy
sx = 1.0 / lvx sx = 1.0 / lvx
sy = 1.0 / lvy sy = 1.0 / lvy
@ -1167,7 +1140,6 @@ class ROI(GraphicsObject):
if width == 0 or height == 0: if width == 0 or height == 0:
return np.empty((width, height), dtype=float) return np.empty((width, height), dtype=float)
# QImage(width, height, format)
im = QtGui.QImage(width, height, QtGui.QImage.Format_ARGB32) im = QtGui.QImage(width, height, QtGui.QImage.Format_ARGB32)
im.fill(0x0) im.fill(0x0)
p = QtGui.QPainter(im) p = QtGui.QPainter(im)
@ -1198,27 +1170,6 @@ class ROI(GraphicsObject):
t2 = SRTTransform(st) t2 = SRTTransform(st)
return t2/t1 return t2/t1
#st = self.getState()
### rotation
#ang = (st['angle']-relativeTo['angle']) * 180. / 3.14159265358
#rot = QtGui.QTransform()
#rot.rotate(-ang)
### We need to come up with a universal transformation--one that can be applied to other objects
### such that all maintain alignment.
### More specifically, we need to turn the ROI's position and angle into
### a rotation _around the origin_ and a translation.
#p0 = Point(relativeTo['pos'])
### base position, rotated
#p1 = rot.map(p0)
#trans = Point(st['pos']) - p1
#return trans, ang
def applyGlobalTransform(self, tr): def applyGlobalTransform(self, tr):
st = self.getState() st = self.getState()
@ -1239,8 +1190,6 @@ class Handle(UIGraphicsItem):
Handles may be dragged to change the position, size, orientation, or other Handles may be dragged to change the position, size, orientation, or other
properties of the ROI they are attached to. properties of the ROI they are attached to.
""" """
types = { ## defines number of sides, start angle for each handle type types = { ## defines number of sides, start angle for each handle type
't': (4, np.pi/4), 't': (4, np.pi/4),
@ -1255,9 +1204,6 @@ class Handle(UIGraphicsItem):
sigRemoveRequested = QtCore.Signal(object) # self 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
#self.bounds = QtCore.QRectF(-1e-10, -1e-10, 2e-10, 2e-10)
#self.setFlags(self.ItemIgnoresTransformations | self.ItemSendsScenePositionChanges)
self.rois = [] self.rois = []
self.radius = radius self.radius = radius
self.typ = typ self.typ = typ
@ -1276,7 +1222,6 @@ class Handle(UIGraphicsItem):
self.deletable = deletable self.deletable = deletable
if deletable: if deletable:
self.setAcceptedMouseButtons(QtCore.Qt.RightButton) self.setAcceptedMouseButtons(QtCore.Qt.RightButton)
#self.updateShape()
self.setZValue(11) self.setZValue(11)
def connectROI(self, roi): def connectROI(self, roi):
@ -1285,13 +1230,6 @@ class Handle(UIGraphicsItem):
def disconnectROI(self, roi): def disconnectROI(self, roi):
self.rois.remove(roi) self.rois.remove(roi)
#for i, r in enumerate(self.roi):
#if r[0] == roi:
#self.roi.pop(i)
#def close(self):
#for r in self.roi:
#r.removeHandle(self)
def setDeletable(self, b): def setDeletable(self, b):
self.deletable = b self.deletable = b
@ -1317,21 +1255,12 @@ class Handle(UIGraphicsItem):
else: else:
self.currentPen = self.pen self.currentPen = self.pen
self.update() self.update()
#if (not ev.isExit()) and ev.acceptDrags(QtCore.Qt.LeftButton):
#self.currentPen = fn.mkPen(255, 255,0)
#else:
#self.currentPen = self.pen
#self.update()
def mouseClickEvent(self, ev): def mouseClickEvent(self, ev):
## right-click cancels drag ## right-click cancels drag
if ev.button() == QtCore.Qt.RightButton and self.isMoving: if ev.button() == QtCore.Qt.RightButton and self.isMoving:
self.isMoving = False ## prevents any further motion self.isMoving = False ## prevents any further motion
self.movePoint(self.startPos, finish=True) self.movePoint(self.startPos, finish=True)
#for r in self.roi:
#r[0].cancelMove()
ev.accept() ev.accept()
elif int(ev.button() & self.acceptedMouseButtons()) > 0: elif int(ev.button() & self.acceptedMouseButtons()) > 0:
ev.accept() ev.accept()
@ -1341,12 +1270,6 @@ class Handle(UIGraphicsItem):
else: else:
ev.ignore() ev.ignore()
#elif self.deletable:
#ev.accept()
#self.raiseContextMenu(ev)
#else:
#ev.ignore()
def buildMenu(self): def buildMenu(self):
menu = QtGui.QMenu() menu = QtGui.QMenu()
menu.setTitle("Handle") menu.setTitle("Handle")
@ -1416,36 +1339,10 @@ class Handle(UIGraphicsItem):
self.path.lineTo(x, y) self.path.lineTo(x, y)
def paint(self, p, opt, widget): def paint(self, p, opt, widget):
### determine rotation of transform
#m = self.sceneTransform()
##mi = m.inverted()[0]
#v = m.map(QtCore.QPointF(1, 0)) - m.map(QtCore.QPointF(0, 0))
#va = np.arctan2(v.y(), v.x())
### Determine length of unit vector in painter's coords
##size = mi.map(Point(self.radius, self.radius)) - mi.map(Point(0, 0))
##size = (size.x()*size.x() + size.y() * size.y()) ** 0.5
#size = self.radius
#bounds = QtCore.QRectF(-size, -size, size*2, size*2)
#if bounds != self.bounds:
#self.bounds = bounds
#self.prepareGeometryChange()
p.setRenderHints(p.Antialiasing, True) p.setRenderHints(p.Antialiasing, True)
p.setPen(self.currentPen) p.setPen(self.currentPen)
#p.rotate(va * 180. / 3.1415926)
#p.drawPath(self.path)
p.drawPath(self.shape()) p.drawPath(self.shape())
#ang = self.startAng + va
#dt = 2*np.pi / self.sides
#for i in range(0, self.sides):
#x1 = size * cos(ang)
#y1 = size * sin(ang)
#x2 = size * cos(ang+dt)
#y2 = size * sin(ang+dt)
#ang += dt
#p.drawLine(Point(x1, y1), Point(x2, y2))
def shape(self): def shape(self):
if self._shape is None: if self._shape is None:
@ -1457,18 +1354,10 @@ class Handle(UIGraphicsItem):
return self._shape return self._shape
def boundingRect(self): def boundingRect(self):
#print 'roi:', self.roi
s1 = self.shape() s1 = self.shape()
#print " s1:", s1
#s2 = self.shape()
#print " s2:", s2
return self.shape().boundingRect() return self.shape().boundingRect()
def generateShape(self): def generateShape(self):
## determine rotation of transform
#m = self.sceneTransform() ## Qt bug: do not access sceneTransform() until we know this object has a scene.
#mi = m.inverted()[0]
dt = self.deviceTransform() dt = self.deviceTransform()
if dt is None: if dt is None:
@ -1486,22 +1375,15 @@ class Handle(UIGraphicsItem):
return dti.map(tr.map(self.path)) return dti.map(tr.map(self.path))
def viewTransformChanged(self): def viewTransformChanged(self):
GraphicsObject.viewTransformChanged(self) GraphicsObject.viewTransformChanged(self)
self._shape = None ## invalidate shape, recompute later if requested. self._shape = None ## invalidate shape, recompute later if requested.
self.update() self.update()
#def itemChange(self, change, value):
#if change == self.ItemScenePositionHasChanged:
#self.updateShape()
class TestROI(ROI): class TestROI(ROI):
def __init__(self, pos, size, **args): def __init__(self, pos, size, **args):
#QtGui.QGraphicsRectItem.__init__(self, pos[0], pos[1], size[0], size[1])
ROI.__init__(self, pos, size, **args) ROI.__init__(self, pos, size, **args)
#self.addTranslateHandle([0, 0])
self.addTranslateHandle([0.5, 0.5]) self.addTranslateHandle([0.5, 0.5])
self.addScaleHandle([1, 1], [0, 0]) self.addScaleHandle([1, 1], [0, 0])
self.addScaleHandle([0, 0], [1, 1]) self.addScaleHandle([0, 0], [1, 1])
@ -1511,7 +1393,6 @@ class TestROI(ROI):
self.addRotateHandle([0, 1], [1, 1]) self.addRotateHandle([0, 1], [1, 1])
class RectROI(ROI): class RectROI(ROI):
""" """
Rectangular ROI subclass with a single scale handle at the top-right corner. Rectangular ROI subclass with a single scale handle at the top-right corner.
@ -1530,14 +1411,12 @@ class RectROI(ROI):
""" """
def __init__(self, pos, size, centered=False, sideScalers=False, **args): def __init__(self, pos, size, centered=False, sideScalers=False, **args):
#QtGui.QGraphicsRectItem.__init__(self, 0, 0, size[0], size[1])
ROI.__init__(self, pos, size, **args) ROI.__init__(self, pos, size, **args)
if centered: if centered:
center = [0.5, 0.5] center = [0.5, 0.5]
else: else:
center = [0, 0] center = [0, 0]
#self.addTranslateHandle(center)
self.addScaleHandle([1, 1], center) self.addScaleHandle([1, 1], center)
if sideScalers: if sideScalers:
self.addScaleHandle([1, 0.5], [center[0], 0.5]) self.addScaleHandle([1, 0.5], [center[0], 0.5])
@ -1646,7 +1525,6 @@ class MultiRectROI(QtGui.QGraphicsObject):
rgn = l.getArrayRegion(arr, img, axes=axes, **kwds) rgn = l.getArrayRegion(arr, img, axes=axes, **kwds)
if rgn is None: if rgn is None:
continue continue
#return None
rgns.append(rgn) rgns.append(rgn)
#print l.state['size'] #print l.state['size']
@ -1731,7 +1609,6 @@ class EllipseROI(ROI):
""" """
def __init__(self, pos, size, **args): def __init__(self, pos, size, **args):
#QtGui.QGraphicsRectItem.__init__(self, 0, 0, size[0], size[1])
self.path = None self.path = None
ROI.__init__(self, pos, size, **args) ROI.__init__(self, pos, size, **args)
self.sigRegionChanged.connect(self._clearPath) self.sigRegionChanged.connect(self._clearPath)
@ -1835,22 +1712,14 @@ class PolygonROI(ROI):
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])
for p in positions: for p in positions:
self.addFreeHandle(p) self.addFreeHandle(p)
self.setZValue(1000) self.setZValue(1000)
print("Warning: PolygonROI is deprecated. Use PolyLineROI instead.") print("Warning: PolygonROI is deprecated. Use PolyLineROI instead.")
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):
#ROI.movePoint(self, *args, **kargs)
#self.prepareGeometryChange()
#for h in self.handles:
#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)
p.setPen(self.currentPen) p.setPen(self.currentPen)
@ -1877,7 +1746,6 @@ class PolygonROI(ROI):
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
return sc return sc
@ -2097,13 +1965,16 @@ class LineSegmentROI(ROI):
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])
if len(positions) > 2: if len(positions) > 2:
raise Exception("LineSegmentROI must be defined by exactly 2 positions. For more points, use PolyLineROI.") raise Exception("LineSegmentROI must be defined by exactly 2 positions. For more points, use PolyLineROI.")
self.endpoints = []
for i, p in enumerate(positions): for i, p in enumerate(positions):
self.endpoints.append(self.addFreeHandle(p, item=handles[i])) self.addFreeHandle(p, item=handles[i])
@property
def endpoints(self):
# must not be cached because self.handles may change.
return [h['item'] for h in self.handles]
def listPoints(self): def listPoints(self):
return [p['item'].pos() for p in self.handles] return [p['item'].pos() for p in self.handles]
@ -2150,7 +2021,6 @@ class LineSegmentROI(ROI):
See ROI.getArrayRegion() for a description of the arguments. See ROI.getArrayRegion() for a description of the arguments.
""" """
imgPts = [self.mapToItem(img, h.pos()) for h in self.endpoints] imgPts = [self.mapToItem(img, h.pos()) for h in self.endpoints]
rgns = [] rgns = []
coords = [] coords = []
@ -2193,7 +2063,6 @@ class CrosshairROI(ROI):
def __init__(self, pos=None, size=None, **kargs): def __init__(self, pos=None, size=None, **kargs):
if size == None: if size == None:
#size = [100e-6,100e-6]
size=[1,1] size=[1,1]
if pos == None: if pos == None:
pos = [0,0] pos = [0,0]

View File

@ -209,14 +209,22 @@ def test_PolyLineROI():
mouseClick(plt, pt, QtCore.Qt.LeftButton) mouseClick(plt, pt, QtCore.Qt.LeftButton)
assertImageApproved(plt, 'roi/polylineroi/'+name+'_click_segment', 'Click mouse over segment.') assertImageApproved(plt, 'roi/polylineroi/'+name+'_click_segment', 'Click mouse over segment.')
# drag new handle
mouseMove(plt, pt+pg.Point(10, -10)) # pg bug: have to move the mouse off/on again to register hover
mouseDrag(plt, pt, pt + pg.Point(10, -10), QtCore.Qt.LeftButton)
assertImageApproved(plt, 'roi/polylineroi/'+name+'_drag_new_handle', 'Drag mouse over created handle.')
# clear all points
r.clearPoints() r.clearPoints()
assertImageApproved(plt, 'roi/polylineroi/'+name+'_clear', 'All points cleared.') assertImageApproved(plt, 'roi/polylineroi/'+name+'_clear', 'All points cleared.')
assert len(r.getState()['points']) == 0 assert len(r.getState()['points']) == 0
# call setPoints
r.setPoints(initState['points']) r.setPoints(initState['points'])
assertImageApproved(plt, 'roi/polylineroi/'+name+'_setpoints', 'Reset points to initial state.') assertImageApproved(plt, 'roi/polylineroi/'+name+'_setpoints', 'Reset points to initial state.')
assert len(r.getState()['points']) == 3 assert len(r.getState()['points']) == 3
# call setState
r.setState(initState) r.setState(initState)
assertImageApproved(plt, 'roi/polylineroi/'+name+'_setstate', 'Reset ROI to initial state.') assertImageApproved(plt, 'roi/polylineroi/'+name+'_setstate', 'Reset ROI to initial state.')
assert len(r.getState()['points']) == 3 assert len(r.getState()['points']) == 3

View File

@ -10,11 +10,13 @@ Procedure for unit-testing with images:
$ PYQTGRAPH_AUDIT=1 python pyqtgraph/graphicsItems/tests/test_PlotCurveItem.py $ PYQTGRAPH_AUDIT=1 python pyqtgraph/graphicsItems/tests/test_PlotCurveItem.py
Any failing tests will Any failing tests will display the test results, standard image, and the
display the test results, standard image, and the differences between the differences between the two. If the test result is bad, then press (f)ail.
two. If the test result is bad, then press (f)ail. If the test result is If the test result is good, then press (p)ass and the new image will be
good, then press (p)ass and the new image will be saved to the test-data saved to the test-data directory.
directory.
To check all test results regardless of whether the test failed, set the
environment variable PYQTGRAPH_AUDIT_ALL=1.
3. After adding or changing test images, create a new commit: 3. After adding or changing test images, create a new commit:
@ -42,7 +44,7 @@ Procedure for unit-testing with images:
# pyqtgraph should be tested against. When adding or changing test images, # pyqtgraph should be tested against. When adding or changing test images,
# create and push a new tag and update this variable. To test locally, begin # create and push a new tag and update this variable. To test locally, begin
# by creating the tag in your ~/.pyqtgraph/test-data repository. # by creating the tag in your ~/.pyqtgraph/test-data repository.
testDataTag = 'test-data-6' testDataTag = 'test-data-7'
import time import time
@ -162,6 +164,8 @@ def assertImageApproved(image, standardFile, message=None, **kwargs):
# If the test image does not match, then we go to audit if requested. # If the test image does not match, then we go to audit if requested.
try: try:
if stdImage is None:
raise Exception("No reference image saved for this test.")
if image.shape[2] != stdImage.shape[2]: if image.shape[2] != stdImage.shape[2]:
raise Exception("Test result has different channel count than standard image" raise Exception("Test result has different channel count than standard image"
"(%d vs %d)" % (image.shape[2], stdImage.shape[2])) "(%d vs %d)" % (image.shape[2], stdImage.shape[2]))