Merge pull request #596 from campagnola/fix-polyline-click
Fix polyline click
This commit is contained in:
commit
22b0ddaa21
@ -113,7 +113,6 @@ class ROI(GraphicsObject):
|
||||
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):
|
||||
#QObjectWorkaround.__init__(self)
|
||||
GraphicsObject.__init__(self, parent)
|
||||
self.setAcceptedMouseButtons(QtCore.Qt.NoButton)
|
||||
pos = Point(pos)
|
||||
@ -148,7 +147,6 @@ class ROI(GraphicsObject):
|
||||
self.translateSnap = translateSnap
|
||||
self.rotateSnap = rotateSnap
|
||||
self.scaleSnap = scaleSnap
|
||||
#self.setFlag(self.ItemIsSelectable, True)
|
||||
|
||||
def getState(self):
|
||||
return self.stateCopy()
|
||||
@ -268,7 +266,6 @@ class ROI(GraphicsObject):
|
||||
raise TypeError("update argument must be bool")
|
||||
self.state['angle'] = angle
|
||||
tr = QtGui.QTransform()
|
||||
#tr.rotate(-angle * 180 / np.pi)
|
||||
tr.rotate(angle)
|
||||
self.setTransform(tr)
|
||||
if update:
|
||||
@ -316,20 +313,14 @@ class ROI(GraphicsObject):
|
||||
newState = self.stateCopy()
|
||||
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)
|
||||
if snap is None:
|
||||
snap = self.translateSnap
|
||||
if snap is not False:
|
||||
newState['pos'] = self.getSnapPosition(newState['pos'], snap=snap)
|
||||
|
||||
#d = ev.scenePos() - self.mapToScene(self.pressPos)
|
||||
if self.maxBounds is not None:
|
||||
r = self.stateRect(newState)
|
||||
#r0 = self.sceneTransform().mapRect(self.boundingRect())
|
||||
d = Point(0,0)
|
||||
if 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()
|
||||
newState['pos'] += d
|
||||
|
||||
#self.state['pos'] = newState['pos']
|
||||
update = kargs.get('update', True)
|
||||
finish = kargs.get('finish', True)
|
||||
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):
|
||||
"""
|
||||
@ -583,7 +571,6 @@ class ROI(GraphicsObject):
|
||||
## Note: by default, handles are not user-removable even if this method returns True.
|
||||
return True
|
||||
|
||||
|
||||
def getLocalHandlePositions(self, index=None):
|
||||
"""Returns the position of handles in the ROI's coordinate system.
|
||||
|
||||
@ -629,7 +616,6 @@ class ROI(GraphicsObject):
|
||||
for h in self.handles:
|
||||
h['item'].hide()
|
||||
|
||||
|
||||
def hoverEvent(self, ev):
|
||||
hover = False
|
||||
if not ev.isExit():
|
||||
@ -870,10 +856,8 @@ class ROI(GraphicsObject):
|
||||
r = self.stateRect(newState)
|
||||
if not self.maxBounds.contains(r):
|
||||
return
|
||||
#self.setTransform(tr)
|
||||
self.setPos(newState['pos'], update=False)
|
||||
self.setAngle(ang, update=False)
|
||||
#self.state = newState
|
||||
|
||||
## 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:
|
||||
return
|
||||
if self.rotateSnap or (modifiers & QtCore.Qt.ControlModifier):
|
||||
#ang = round(ang / (np.pi/12.)) * (np.pi/12.)
|
||||
ang = round(ang / 15.) * 15.
|
||||
|
||||
hs = abs(h['pos'][scaleAxis] - c[scaleAxis])
|
||||
@ -949,9 +932,6 @@ class ROI(GraphicsObject):
|
||||
if h['item'] in self.childItems():
|
||||
p = h['pos']
|
||||
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.sigRegionChanged.emit(self)
|
||||
@ -971,12 +951,10 @@ class ROI(GraphicsObject):
|
||||
def stateRect(self, state):
|
||||
r = QtCore.QRectF(0, 0, state['size'][0], state['size'][1])
|
||||
tr = QtGui.QTransform()
|
||||
#tr.rotate(-state['angle'] * 180 / np.pi)
|
||||
tr.rotate(-state['angle'])
|
||||
r = tr.mapRect(r)
|
||||
return r.adjusted(state['pos'][0], state['pos'][1], state['pos'][0], state['pos'][1])
|
||||
|
||||
|
||||
def getSnapPosition(self, pos, snap=None):
|
||||
## Given that pos has been requested, return the nearest snap-to position
|
||||
## 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()
|
||||
|
||||
def paint(self, p, opt, widget):
|
||||
# p.save()
|
||||
# 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()
|
||||
|
||||
@ -1005,7 +982,6 @@ class ROI(GraphicsObject):
|
||||
p.translate(r.left(), r.top())
|
||||
p.scale(r.width(), r.height())
|
||||
p.drawRect(0, 0, 1, 1)
|
||||
# p.restore()
|
||||
|
||||
def getArraySlice(self, data, img, axes=(0,1), returnSlice=True):
|
||||
"""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)
|
||||
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.
|
||||
##need pxWidth and pxHeight instead of pxLen ?
|
||||
#sx = pxLen / lvx
|
||||
#sy = pxLen / lvy
|
||||
sx = 1.0 / lvx
|
||||
sy = 1.0 / lvy
|
||||
|
||||
@ -1167,7 +1140,6 @@ class ROI(GraphicsObject):
|
||||
if width == 0 or height == 0:
|
||||
return np.empty((width, height), dtype=float)
|
||||
|
||||
# QImage(width, height, format)
|
||||
im = QtGui.QImage(width, height, QtGui.QImage.Format_ARGB32)
|
||||
im.fill(0x0)
|
||||
p = QtGui.QPainter(im)
|
||||
@ -1198,27 +1170,6 @@ class ROI(GraphicsObject):
|
||||
t2 = SRTTransform(st)
|
||||
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):
|
||||
st = self.getState()
|
||||
|
||||
@ -1239,8 +1190,6 @@ class Handle(UIGraphicsItem):
|
||||
|
||||
Handles may be dragged to change the position, size, orientation, or other
|
||||
properties of the ROI they are attached to.
|
||||
|
||||
|
||||
"""
|
||||
types = { ## defines number of sides, start angle for each handle type
|
||||
't': (4, np.pi/4),
|
||||
@ -1255,9 +1204,6 @@ class Handle(UIGraphicsItem):
|
||||
sigRemoveRequested = QtCore.Signal(object) # self
|
||||
|
||||
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.radius = radius
|
||||
self.typ = typ
|
||||
@ -1276,7 +1222,6 @@ class Handle(UIGraphicsItem):
|
||||
self.deletable = deletable
|
||||
if deletable:
|
||||
self.setAcceptedMouseButtons(QtCore.Qt.RightButton)
|
||||
#self.updateShape()
|
||||
self.setZValue(11)
|
||||
|
||||
def connectROI(self, roi):
|
||||
@ -1285,13 +1230,6 @@ class Handle(UIGraphicsItem):
|
||||
|
||||
def disconnectROI(self, 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):
|
||||
self.deletable = b
|
||||
@ -1317,21 +1255,12 @@ class Handle(UIGraphicsItem):
|
||||
else:
|
||||
self.currentPen = self.pen
|
||||
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):
|
||||
## right-click cancels drag
|
||||
if ev.button() == QtCore.Qt.RightButton and self.isMoving:
|
||||
self.isMoving = False ## prevents any further motion
|
||||
self.movePoint(self.startPos, finish=True)
|
||||
#for r in self.roi:
|
||||
#r[0].cancelMove()
|
||||
ev.accept()
|
||||
elif int(ev.button() & self.acceptedMouseButtons()) > 0:
|
||||
ev.accept()
|
||||
@ -1341,12 +1270,6 @@ class Handle(UIGraphicsItem):
|
||||
else:
|
||||
ev.ignore()
|
||||
|
||||
#elif self.deletable:
|
||||
#ev.accept()
|
||||
#self.raiseContextMenu(ev)
|
||||
#else:
|
||||
#ev.ignore()
|
||||
|
||||
def buildMenu(self):
|
||||
menu = QtGui.QMenu()
|
||||
menu.setTitle("Handle")
|
||||
@ -1416,36 +1339,10 @@ class Handle(UIGraphicsItem):
|
||||
self.path.lineTo(x, y)
|
||||
|
||||
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.setPen(self.currentPen)
|
||||
|
||||
#p.rotate(va * 180. / 3.1415926)
|
||||
#p.drawPath(self.path)
|
||||
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):
|
||||
if self._shape is None:
|
||||
@ -1457,18 +1354,10 @@ class Handle(UIGraphicsItem):
|
||||
return self._shape
|
||||
|
||||
def boundingRect(self):
|
||||
#print 'roi:', self.roi
|
||||
s1 = self.shape()
|
||||
#print " s1:", s1
|
||||
#s2 = self.shape()
|
||||
#print " s2:", s2
|
||||
|
||||
return self.shape().boundingRect()
|
||||
|
||||
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()
|
||||
|
||||
if dt is None:
|
||||
@ -1486,22 +1375,15 @@ class Handle(UIGraphicsItem):
|
||||
|
||||
return dti.map(tr.map(self.path))
|
||||
|
||||
|
||||
def viewTransformChanged(self):
|
||||
GraphicsObject.viewTransformChanged(self)
|
||||
self._shape = None ## invalidate shape, recompute later if requested.
|
||||
self.update()
|
||||
|
||||
#def itemChange(self, change, value):
|
||||
#if change == self.ItemScenePositionHasChanged:
|
||||
#self.updateShape()
|
||||
|
||||
|
||||
class TestROI(ROI):
|
||||
def __init__(self, pos, size, **args):
|
||||
#QtGui.QGraphicsRectItem.__init__(self, pos[0], pos[1], size[0], size[1])
|
||||
ROI.__init__(self, pos, size, **args)
|
||||
#self.addTranslateHandle([0, 0])
|
||||
self.addTranslateHandle([0.5, 0.5])
|
||||
self.addScaleHandle([1, 1], [0, 0])
|
||||
self.addScaleHandle([0, 0], [1, 1])
|
||||
@ -1511,7 +1393,6 @@ class TestROI(ROI):
|
||||
self.addRotateHandle([0, 1], [1, 1])
|
||||
|
||||
|
||||
|
||||
class RectROI(ROI):
|
||||
"""
|
||||
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):
|
||||
#QtGui.QGraphicsRectItem.__init__(self, 0, 0, size[0], size[1])
|
||||
ROI.__init__(self, pos, size, **args)
|
||||
if centered:
|
||||
center = [0.5, 0.5]
|
||||
else:
|
||||
center = [0, 0]
|
||||
|
||||
#self.addTranslateHandle(center)
|
||||
self.addScaleHandle([1, 1], center)
|
||||
if sideScalers:
|
||||
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)
|
||||
if rgn is None:
|
||||
continue
|
||||
#return None
|
||||
rgns.append(rgn)
|
||||
#print l.state['size']
|
||||
|
||||
@ -1731,7 +1609,6 @@ class EllipseROI(ROI):
|
||||
|
||||
"""
|
||||
def __init__(self, pos, size, **args):
|
||||
#QtGui.QGraphicsRectItem.__init__(self, 0, 0, size[0], size[1])
|
||||
self.path = None
|
||||
ROI.__init__(self, pos, size, **args)
|
||||
self.sigRegionChanged.connect(self._clearPath)
|
||||
@ -1835,22 +1712,14 @@ class PolygonROI(ROI):
|
||||
if pos is None:
|
||||
pos = [0,0]
|
||||
ROI.__init__(self, pos, [1,1], **args)
|
||||
#ROI.__init__(self, positions[0])
|
||||
for p in positions:
|
||||
self.addFreeHandle(p)
|
||||
self.setZValue(1000)
|
||||
print("Warning: PolygonROI is deprecated. Use PolyLineROI instead.")
|
||||
|
||||
|
||||
def listPoints(self):
|
||||
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):
|
||||
p.setRenderHint(QtGui.QPainter.Antialiasing)
|
||||
p.setPen(self.currentPen)
|
||||
@ -1877,7 +1746,6 @@ class PolygonROI(ROI):
|
||||
sc['pos'] = Point(self.state['pos'])
|
||||
sc['size'] = Point(self.state['size'])
|
||||
sc['angle'] = self.state['angle']
|
||||
#sc['handles'] = self.handles
|
||||
return sc
|
||||
|
||||
|
||||
@ -2097,13 +1965,16 @@ class LineSegmentROI(ROI):
|
||||
pos = [0,0]
|
||||
|
||||
ROI.__init__(self, pos, [1,1], **args)
|
||||
#ROI.__init__(self, positions[0])
|
||||
if len(positions) > 2:
|
||||
raise Exception("LineSegmentROI must be defined by exactly 2 positions. For more points, use PolyLineROI.")
|
||||
|
||||
self.endpoints = []
|
||||
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):
|
||||
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.
|
||||
"""
|
||||
|
||||
imgPts = [self.mapToItem(img, h.pos()) for h in self.endpoints]
|
||||
rgns = []
|
||||
coords = []
|
||||
@ -2193,7 +2063,6 @@ class CrosshairROI(ROI):
|
||||
|
||||
def __init__(self, pos=None, size=None, **kargs):
|
||||
if size == None:
|
||||
#size = [100e-6,100e-6]
|
||||
size=[1,1]
|
||||
if pos == None:
|
||||
pos = [0,0]
|
||||
|
@ -209,14 +209,22 @@ def test_PolyLineROI():
|
||||
mouseClick(plt, pt, QtCore.Qt.LeftButton)
|
||||
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()
|
||||
assertImageApproved(plt, 'roi/polylineroi/'+name+'_clear', 'All points cleared.')
|
||||
assert len(r.getState()['points']) == 0
|
||||
|
||||
# call setPoints
|
||||
r.setPoints(initState['points'])
|
||||
assertImageApproved(plt, 'roi/polylineroi/'+name+'_setpoints', 'Reset points to initial state.')
|
||||
assert len(r.getState()['points']) == 3
|
||||
|
||||
# call setState
|
||||
r.setState(initState)
|
||||
assertImageApproved(plt, 'roi/polylineroi/'+name+'_setstate', 'Reset ROI to initial state.')
|
||||
assert len(r.getState()['points']) == 3
|
||||
|
@ -10,11 +10,13 @@ Procedure for unit-testing with images:
|
||||
|
||||
$ PYQTGRAPH_AUDIT=1 python pyqtgraph/graphicsItems/tests/test_PlotCurveItem.py
|
||||
|
||||
Any failing tests will
|
||||
display the test results, standard image, and the differences between the
|
||||
two. If the test result is bad, then press (f)ail. If the test result is
|
||||
good, then press (p)ass and the new image will be saved to the test-data
|
||||
directory.
|
||||
Any failing tests will display the test results, standard image, and the
|
||||
differences between the two. If the test result is bad, then press (f)ail.
|
||||
If the test result is good, then press (p)ass and the new image will be
|
||||
saved to the test-data 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:
|
||||
|
||||
@ -42,7 +44,7 @@ Procedure for unit-testing with 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
|
||||
# by creating the tag in your ~/.pyqtgraph/test-data repository.
|
||||
testDataTag = 'test-data-6'
|
||||
testDataTag = 'test-data-7'
|
||||
|
||||
|
||||
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.
|
||||
try:
|
||||
if stdImage is None:
|
||||
raise Exception("No reference image saved for this test.")
|
||||
if image.shape[2] != stdImage.shape[2]:
|
||||
raise Exception("Test result has different channel count than standard image"
|
||||
"(%d vs %d)" % (image.shape[2], stdImage.shape[2]))
|
||||
|
Loading…
Reference in New Issue
Block a user