Renamed Transform -> SRTTransform to better reflect its function.
Added SRTTransform3D
This commit is contained in:
parent
6129df2019
commit
7c87b1d04a
@ -4,7 +4,7 @@ from .Point import Point
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
|
|
||||||
class Transform(QtGui.QTransform):
|
class SRTTransform(QtGui.QTransform):
|
||||||
"""Transform that can always be represented as a combination of 3 matrices: scale * rotate * translate
|
"""Transform that can always be represented as a combination of 3 matrices: scale * rotate * translate
|
||||||
This transform has no shear; angles are always preserved.
|
This transform has no shear; angles are always preserved.
|
||||||
"""
|
"""
|
||||||
@ -16,7 +16,7 @@ class Transform(QtGui.QTransform):
|
|||||||
return
|
return
|
||||||
elif isinstance(init, dict):
|
elif isinstance(init, dict):
|
||||||
self.restoreState(init)
|
self.restoreState(init)
|
||||||
elif isinstance(init, Transform):
|
elif isinstance(init, SRTTransform):
|
||||||
self._state = {
|
self._state = {
|
||||||
'pos': Point(init._state['pos']),
|
'pos': Point(init._state['pos']),
|
||||||
'scale': Point(init._state['scale']),
|
'scale': Point(init._state['scale']),
|
||||||
@ -28,7 +28,7 @@ class Transform(QtGui.QTransform):
|
|||||||
elif isinstance(init, QtGui.QMatrix4x4):
|
elif isinstance(init, QtGui.QMatrix4x4):
|
||||||
self.setFromMatrix4x4(init)
|
self.setFromMatrix4x4(init)
|
||||||
else:
|
else:
|
||||||
raise Exception("Cannot create Transform from input type: %s" % str(type(init)))
|
raise Exception("Cannot create SRTTransform from input type: %s" % str(type(init)))
|
||||||
|
|
||||||
|
|
||||||
def getScale(self):
|
def getScale(self):
|
||||||
@ -73,9 +73,10 @@ class Transform(QtGui.QTransform):
|
|||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def setFromMatrix4x4(self, m):
|
def setFromMatrix4x4(self, m):
|
||||||
m = pg.Transform3D(m)
|
m = pg.SRTTransform3D(m)
|
||||||
angle, axis = m.getRotation()
|
angle, axis = m.getRotation()
|
||||||
if angle != 0 and (axis[0] != 0 or axis[1] != 0 or axis[2] != 1):
|
if angle != 0 and (axis[0] != 0 or axis[1] != 0 or axis[2] != 1):
|
||||||
|
print angle, axis
|
||||||
raise Exception("Can only convert 4x4 matrix to 3x3 if rotation is around Z-axis.")
|
raise Exception("Can only convert 4x4 matrix to 3x3 if rotation is around Z-axis.")
|
||||||
self._state = {
|
self._state = {
|
||||||
'pos': Point(m.getTranslation()),
|
'pos': Point(m.getTranslation()),
|
||||||
@ -128,10 +129,10 @@ class Transform(QtGui.QTransform):
|
|||||||
def __div__(self, t):
|
def __div__(self, t):
|
||||||
"""A / B == B^-1 * A"""
|
"""A / B == B^-1 * A"""
|
||||||
dt = t.inverted()[0] * self
|
dt = t.inverted()[0] * self
|
||||||
return Transform(dt)
|
return SRTTransform(dt)
|
||||||
|
|
||||||
def __mul__(self, t):
|
def __mul__(self, t):
|
||||||
return Transform(QtGui.QTransform.__mul__(self, t))
|
return SRTTransform(QtGui.QTransform.__mul__(self, t))
|
||||||
|
|
||||||
def saveState(self):
|
def saveState(self):
|
||||||
p = self._state['pos']
|
p = self._state['pos']
|
||||||
@ -203,12 +204,12 @@ if __name__ == '__main__':
|
|||||||
s.addItem(l1)
|
s.addItem(l1)
|
||||||
s.addItem(l2)
|
s.addItem(l2)
|
||||||
|
|
||||||
tr1 = Transform()
|
tr1 = SRTTransform()
|
||||||
tr2 = Transform()
|
tr2 = SRTTransform()
|
||||||
tr3 = QtGui.QTransform()
|
tr3 = QtGui.QTransform()
|
||||||
tr3.translate(20, 0)
|
tr3.translate(20, 0)
|
||||||
tr3.rotate(45)
|
tr3.rotate(45)
|
||||||
print("QTransform -> Transform:", Transform(tr3))
|
print("QTransform -> Transform:", SRTTransform(tr3))
|
||||||
|
|
||||||
print("tr1:", tr1)
|
print("tr1:", tr1)
|
||||||
|
|
||||||
@ -221,7 +222,7 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
print("tr2 * tr1 = ", tr2*tr1)
|
print("tr2 * tr1 = ", tr2*tr1)
|
||||||
|
|
||||||
tr4 = Transform()
|
tr4 = SRTTransform()
|
||||||
tr4.scale(-1, 1)
|
tr4.scale(-1, 1)
|
||||||
tr4.rotate(30)
|
tr4.rotate(30)
|
||||||
print("tr1 * tr4 = ", tr1*tr4)
|
print("tr1 * tr4 = ", tr1*tr4)
|
302
SRTTransform3D.py
Normal file
302
SRTTransform3D.py
Normal file
@ -0,0 +1,302 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from Qt import QtCore, QtGui
|
||||||
|
from Vector import Vector
|
||||||
|
from SRTTransform import SRTTransform
|
||||||
|
import pyqtgraph as pg
|
||||||
|
import numpy as np
|
||||||
|
import scipy.linalg
|
||||||
|
|
||||||
|
class SRTTransform3D(QtGui.QMatrix4x4):
|
||||||
|
"""4x4 Transform matrix that can always be represented as a combination of 3 matrices: scale * rotate * translate
|
||||||
|
This transform has no shear; angles are always preserved.
|
||||||
|
"""
|
||||||
|
def __init__(self, init=None):
|
||||||
|
QtGui.QMatrix4x4.__init__(self)
|
||||||
|
self.reset()
|
||||||
|
if init is None:
|
||||||
|
return
|
||||||
|
if init.__class__ is QtGui.QTransform:
|
||||||
|
init = SRTTransform(init)
|
||||||
|
|
||||||
|
if isinstance(init, dict):
|
||||||
|
self.restoreState(init)
|
||||||
|
elif isinstance(init, SRTTransform3D):
|
||||||
|
self._state = {
|
||||||
|
'pos': Vector(init._state['pos']),
|
||||||
|
'scale': Vector(init._state['scale']),
|
||||||
|
'angle': init._state['angle'],
|
||||||
|
'axis': Vector(init._state['axis']),
|
||||||
|
}
|
||||||
|
self.update()
|
||||||
|
elif isinstance(init, SRTTransform):
|
||||||
|
self._state = {
|
||||||
|
'pos': Vector(init._state['pos']),
|
||||||
|
'scale': Vector(init._state['scale']),
|
||||||
|
'angle': init._state['angle'],
|
||||||
|
'axis': Vector(0, 0, 1),
|
||||||
|
}
|
||||||
|
self.update()
|
||||||
|
elif isinstance(init, QtGui.QMatrix4x4):
|
||||||
|
self.setFromMatrix(init)
|
||||||
|
else:
|
||||||
|
raise Exception("Cannot build SRTTransform3D from argument type:", type(init))
|
||||||
|
|
||||||
|
|
||||||
|
def getScale(self):
|
||||||
|
return pg.Vector(self._state['scale'])
|
||||||
|
|
||||||
|
def getRotation(self):
|
||||||
|
"""Return (angle, axis) of rotation"""
|
||||||
|
return self._state['angle'], pg.Vector(self._state['axis'])
|
||||||
|
|
||||||
|
def getTranslation(self):
|
||||||
|
return pg.Vector(self._state['pos'])
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self._state = {
|
||||||
|
'pos': Vector(0,0,0),
|
||||||
|
'scale': Vector(1,1,1),
|
||||||
|
'angle': 0.0, ## in degrees
|
||||||
|
'axis': (0, 0, 1)
|
||||||
|
}
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def translate(self, *args):
|
||||||
|
"""Adjust the translation of this transform"""
|
||||||
|
t = Vector(*args)
|
||||||
|
self.setTranslate(self._state['pos']+t)
|
||||||
|
|
||||||
|
def setTranslate(self, *args):
|
||||||
|
"""Set the translation of this transform"""
|
||||||
|
self._state['pos'] = Vector(*args)
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def scale(self, *args):
|
||||||
|
"""adjust the scale of this transform"""
|
||||||
|
## try to prevent accidentally setting 0 scale on z axis
|
||||||
|
if len(args) == 1 and hasattr(args[0], '__len__'):
|
||||||
|
args = args[0]
|
||||||
|
if len(args) == 2:
|
||||||
|
args = args + (1,)
|
||||||
|
|
||||||
|
s = Vector(*args)
|
||||||
|
self.setScale(self._state['scale'] * s)
|
||||||
|
|
||||||
|
def setScale(self, *args):
|
||||||
|
"""Set the scale of this transform"""
|
||||||
|
if len(args) == 1 and hasattr(args[0], '__len__'):
|
||||||
|
args = args[0]
|
||||||
|
if len(args) == 2:
|
||||||
|
args = args + (1,)
|
||||||
|
self._state['scale'] = Vector(*args)
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def rotate(self, angle, axis=(0,0,1)):
|
||||||
|
"""Adjust the rotation of this transform"""
|
||||||
|
origAxis = self._state['axis']
|
||||||
|
if axis[0] == origAxis[0] and axis[1] == origAxis[1] and axis[2] == origAxis[2]:
|
||||||
|
self.setRotate(self._state['angle'] + angle)
|
||||||
|
else:
|
||||||
|
m = QtGui.QMatrix4x4()
|
||||||
|
m.translate(*self._state['pos'])
|
||||||
|
m.rotate(self._state['angle'], *self._state['axis'])
|
||||||
|
m.rotate(angle, *axis)
|
||||||
|
m.scale(*self._state['scale'])
|
||||||
|
self.setFromMatrix(m)
|
||||||
|
|
||||||
|
def setRotate(self, angle, axis=(0,0,1)):
|
||||||
|
"""Set the transformation rotation to angle (in degrees)"""
|
||||||
|
|
||||||
|
self._state['angle'] = angle
|
||||||
|
self._state['axis'] = Vector(axis)
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def setFromMatrix(self, m):
|
||||||
|
"""
|
||||||
|
Set this transform mased on the elements of *m*
|
||||||
|
The input matrix must be affine AND have no shear,
|
||||||
|
otherwise the conversion will most likely fail.
|
||||||
|
"""
|
||||||
|
for i in range(4):
|
||||||
|
self.setRow(i, m.row(i))
|
||||||
|
m = self.matrix().reshape(4,4)
|
||||||
|
## translation is 4th column
|
||||||
|
self._state['pos'] = m[:3,3]
|
||||||
|
|
||||||
|
## scale is vector-length of first three columns
|
||||||
|
scale = (m[:3,:3]**2).sum(axis=0)**0.5
|
||||||
|
## see whether there is an inversion
|
||||||
|
z = np.cross(m[0, :3], m[1, :3])
|
||||||
|
if np.dot(z, m[2, :3]) < 0:
|
||||||
|
scale[1] *= -1 ## doesn't really matter which axis we invert
|
||||||
|
self._state['scale'] = scale
|
||||||
|
|
||||||
|
## rotation axis is the eigenvector with eigenvalue=1
|
||||||
|
r = m[:3, :3] / scale[:, np.newaxis]
|
||||||
|
try:
|
||||||
|
evals, evecs = scipy.linalg.eig(r)
|
||||||
|
except:
|
||||||
|
print "Rotation matrix:", r
|
||||||
|
print "Scale:", scale
|
||||||
|
print "Original matrix:", m
|
||||||
|
raise
|
||||||
|
eigIndex = np.argwhere(np.abs(evals-1) < 1e-7)
|
||||||
|
if len(eigIndex) < 1:
|
||||||
|
print "eigenvalues:", evals
|
||||||
|
print "eigenvectors:", evecs
|
||||||
|
print "index:", eigIndex, evals-1
|
||||||
|
raise Exception("Could not determine rotation axis.")
|
||||||
|
axis = evecs[eigIndex[0,0]].real
|
||||||
|
axis /= ((axis**2).sum())**0.5
|
||||||
|
self._state['axis'] = axis
|
||||||
|
|
||||||
|
## trace(r) == 2 cos(angle) + 1, so:
|
||||||
|
self._state['angle'] = np.arccos((r.trace()-1)*0.5) * 180 / np.pi
|
||||||
|
if self._state['angle'] == 0:
|
||||||
|
self._state['axis'] = (0,0,1)
|
||||||
|
|
||||||
|
def as2D(self):
|
||||||
|
"""Return a QTransform representing the x,y portion of this transform (if possible)"""
|
||||||
|
return pg.SRTTransform(self)
|
||||||
|
|
||||||
|
#def __div__(self, t):
|
||||||
|
#"""A / B == B^-1 * A"""
|
||||||
|
#dt = t.inverted()[0] * self
|
||||||
|
#return SRTTransform(dt)
|
||||||
|
|
||||||
|
#def __mul__(self, t):
|
||||||
|
#return SRTTransform(QtGui.QTransform.__mul__(self, t))
|
||||||
|
|
||||||
|
def saveState(self):
|
||||||
|
p = self._state['pos']
|
||||||
|
s = self._state['scale']
|
||||||
|
ax = self._state['axis']
|
||||||
|
#if s[0] == 0:
|
||||||
|
#raise Exception('Invalid scale: %s' % str(s))
|
||||||
|
return {
|
||||||
|
'pos': (p[0], p[1], p[2]),
|
||||||
|
'scale': (s[0], s[1], s[2]),
|
||||||
|
'angle': self._state['angle'],
|
||||||
|
'axis': (ax[0], ax[1], ax[2])
|
||||||
|
}
|
||||||
|
|
||||||
|
def restoreState(self, state):
|
||||||
|
self._state['pos'] = Vector(state.get('pos', (0.,0.,0.)))
|
||||||
|
scale = state.get('scale', (1.,1.,1.))
|
||||||
|
scale = scale + (1.,) * (3-len(scale))
|
||||||
|
self._state['scale'] = Vector(scale)
|
||||||
|
self._state['angle'] = state.get('angle', 0.)
|
||||||
|
self._state['axis'] = state.get('axis', (0, 0, 1))
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
QtGui.QMatrix4x4.setToIdentity(self)
|
||||||
|
## modifications to the transform are multiplied on the right, so we need to reverse order here.
|
||||||
|
QtGui.QMatrix4x4.translate(self, *self._state['pos'])
|
||||||
|
QtGui.QMatrix4x4.rotate(self, self._state['angle'], *self._state['axis'])
|
||||||
|
QtGui.QMatrix4x4.scale(self, *self._state['scale'])
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return str(self.saveState())
|
||||||
|
|
||||||
|
def matrix(self, nd=3):
|
||||||
|
if nd == 3:
|
||||||
|
return np.array(self.copyDataTo())
|
||||||
|
elif nd == 2:
|
||||||
|
m = np.array(self.copyDataTo())
|
||||||
|
m[2] = m[3]
|
||||||
|
m[:,2] = n[:,3]
|
||||||
|
return m[:3,:3]
|
||||||
|
else:
|
||||||
|
raise Exception("Argument 'nd' must be 2 or 3")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import widgets
|
||||||
|
import GraphicsView
|
||||||
|
from functions import *
|
||||||
|
app = QtGui.QApplication([])
|
||||||
|
win = QtGui.QMainWindow()
|
||||||
|
win.show()
|
||||||
|
cw = GraphicsView.GraphicsView()
|
||||||
|
#cw.enableMouse()
|
||||||
|
win.setCentralWidget(cw)
|
||||||
|
s = QtGui.QGraphicsScene()
|
||||||
|
cw.setScene(s)
|
||||||
|
win.resize(600,600)
|
||||||
|
cw.enableMouse()
|
||||||
|
cw.setRange(QtCore.QRectF(-100., -100., 200., 200.))
|
||||||
|
|
||||||
|
class Item(QtGui.QGraphicsItem):
|
||||||
|
def __init__(self):
|
||||||
|
QtGui.QGraphicsItem.__init__(self)
|
||||||
|
self.b = QtGui.QGraphicsRectItem(20, 20, 20, 20, self)
|
||||||
|
self.b.setPen(QtGui.QPen(mkPen('y')))
|
||||||
|
self.t1 = QtGui.QGraphicsTextItem(self)
|
||||||
|
self.t1.setHtml('<span style="color: #F00">R</span>')
|
||||||
|
self.t1.translate(20, 20)
|
||||||
|
self.l1 = QtGui.QGraphicsLineItem(10, 0, -10, 0, self)
|
||||||
|
self.l2 = QtGui.QGraphicsLineItem(0, 10, 0, -10, self)
|
||||||
|
self.l1.setPen(QtGui.QPen(mkPen('y')))
|
||||||
|
self.l2.setPen(QtGui.QPen(mkPen('y')))
|
||||||
|
def boundingRect(self):
|
||||||
|
return QtCore.QRectF()
|
||||||
|
def paint(self, *args):
|
||||||
|
pass
|
||||||
|
|
||||||
|
#s.addItem(b)
|
||||||
|
#s.addItem(t1)
|
||||||
|
item = Item()
|
||||||
|
s.addItem(item)
|
||||||
|
l1 = QtGui.QGraphicsLineItem(10, 0, -10, 0)
|
||||||
|
l2 = QtGui.QGraphicsLineItem(0, 10, 0, -10)
|
||||||
|
l1.setPen(QtGui.QPen(mkPen('r')))
|
||||||
|
l2.setPen(QtGui.QPen(mkPen('r')))
|
||||||
|
s.addItem(l1)
|
||||||
|
s.addItem(l2)
|
||||||
|
|
||||||
|
tr1 = SRTTransform()
|
||||||
|
tr2 = SRTTransform()
|
||||||
|
tr3 = QtGui.QTransform()
|
||||||
|
tr3.translate(20, 0)
|
||||||
|
tr3.rotate(45)
|
||||||
|
print "QTransform -> Transform:", SRTTransform(tr3)
|
||||||
|
|
||||||
|
print "tr1:", tr1
|
||||||
|
|
||||||
|
tr2.translate(20, 0)
|
||||||
|
tr2.rotate(45)
|
||||||
|
print "tr2:", tr2
|
||||||
|
|
||||||
|
dt = tr2/tr1
|
||||||
|
print "tr2 / tr1 = ", dt
|
||||||
|
|
||||||
|
print "tr2 * tr1 = ", tr2*tr1
|
||||||
|
|
||||||
|
tr4 = SRTTransform()
|
||||||
|
tr4.scale(-1, 1)
|
||||||
|
tr4.rotate(30)
|
||||||
|
print "tr1 * tr4 = ", tr1*tr4
|
||||||
|
|
||||||
|
w1 = widgets.TestROI((19,19), (22, 22), invertible=True)
|
||||||
|
#w2 = widgets.TestROI((0,0), (150, 150))
|
||||||
|
w1.setZValue(10)
|
||||||
|
s.addItem(w1)
|
||||||
|
#s.addItem(w2)
|
||||||
|
w1Base = w1.getState()
|
||||||
|
#w2Base = w2.getState()
|
||||||
|
def update():
|
||||||
|
tr1 = w1.getGlobalTransform(w1Base)
|
||||||
|
#tr2 = w2.getGlobalTransform(w2Base)
|
||||||
|
item.setTransform(tr1)
|
||||||
|
|
||||||
|
#def update2():
|
||||||
|
#tr1 = w1.getGlobalTransform(w1Base)
|
||||||
|
#tr2 = w2.getGlobalTransform(w2Base)
|
||||||
|
#t1.setTransform(tr1)
|
||||||
|
#w1.setState(w1Base)
|
||||||
|
#w1.applyGlobalTransform(tr2)
|
||||||
|
|
||||||
|
w1.sigRegionChanged.connect(update)
|
||||||
|
#w2.sigRegionChanged.connect(update2)
|
||||||
|
|
||||||
|
|
@ -19,12 +19,12 @@ if sys.version_info[0] < 2 or (sys.version_info[0] == 2 and sys.version_info[1]
|
|||||||
from . import python2_3
|
from . import python2_3
|
||||||
|
|
||||||
|
|
||||||
## in general openGL is poorly supported in Qt.
|
## in general openGL is poorly supported with Qt+GraphicsView.
|
||||||
## we only enable it where the performance benefit is critical.
|
## we only enable it where the performance benefit is critical.
|
||||||
## Note this only applies to 2D graphics; 3D graphics always use OpenGL.
|
## Note this only applies to 2D graphics; 3D graphics always use OpenGL.
|
||||||
if 'linux' in sys.platform: ## linux has numerous bugs in opengl implementation
|
if 'linux' in sys.platform: ## linux has numerous bugs in opengl implementation
|
||||||
useOpenGL = False
|
useOpenGL = False
|
||||||
elif 'darwin' in sys.platform: ## openGL greatly speeds up display on mac
|
elif 'darwin' in sys.platform: ## openGL can have a major impact on mac, but also has serious bugs
|
||||||
useOpenGL = True
|
useOpenGL = True
|
||||||
else:
|
else:
|
||||||
useOpenGL = False ## on windows there's a more even performance / bugginess tradeoff.
|
useOpenGL = False ## on windows there's a more even performance / bugginess tradeoff.
|
||||||
@ -111,8 +111,8 @@ from .imageview import *
|
|||||||
from .WidgetGroup import *
|
from .WidgetGroup import *
|
||||||
from .Point import Point
|
from .Point import Point
|
||||||
from .Vector import Vector
|
from .Vector import Vector
|
||||||
from .Transform import Transform
|
from .SRTTransform import SRTTransform
|
||||||
from .Transform3D import Transform3D
|
from .SRTTransform3D import SRTTransform3D
|
||||||
from .functions import *
|
from .functions import *
|
||||||
from .graphicsWindows import *
|
from .graphicsWindows import *
|
||||||
from .SignalProxy import *
|
from .SignalProxy import *
|
||||||
|
@ -92,7 +92,7 @@ class CanvasItem(QtCore.QObject):
|
|||||||
if 'transform' in self.opts:
|
if 'transform' in self.opts:
|
||||||
self.baseTransform = self.opts['transform']
|
self.baseTransform = self.opts['transform']
|
||||||
else:
|
else:
|
||||||
self.baseTransform = pg.Transform()
|
self.baseTransform = pg.SRTTransform()
|
||||||
if 'pos' in self.opts and self.opts['pos'] is not None:
|
if 'pos' in self.opts and self.opts['pos'] is not None:
|
||||||
self.baseTransform.translate(self.opts['pos'])
|
self.baseTransform.translate(self.opts['pos'])
|
||||||
if 'angle' in self.opts and self.opts['angle'] is not None:
|
if 'angle' in self.opts and self.opts['angle'] is not None:
|
||||||
@ -120,8 +120,8 @@ class CanvasItem(QtCore.QObject):
|
|||||||
self.itemScale = QtGui.QGraphicsScale()
|
self.itemScale = QtGui.QGraphicsScale()
|
||||||
self._graphicsItem.setTransformations([self.itemRotation, self.itemScale])
|
self._graphicsItem.setTransformations([self.itemRotation, self.itemScale])
|
||||||
|
|
||||||
self.tempTransform = pg.Transform() ## holds the additional transform that happens during a move - gets added to the userTransform when move is done.
|
self.tempTransform = pg.SRTTransform() ## holds the additional transform that happens during a move - gets added to the userTransform when move is done.
|
||||||
self.userTransform = pg.Transform() ## stores the total transform of the object
|
self.userTransform = pg.SRTTransform() ## stores the total transform of the object
|
||||||
self.resetUserTransform()
|
self.resetUserTransform()
|
||||||
|
|
||||||
## now happens inside resetUserTransform -> selectBoxToItem
|
## now happens inside resetUserTransform -> selectBoxToItem
|
||||||
@ -196,7 +196,7 @@ class CanvasItem(QtCore.QObject):
|
|||||||
#flip = self.transformGui.mirrorImageCheck.isChecked()
|
#flip = self.transformGui.mirrorImageCheck.isChecked()
|
||||||
#tr = self.userTransform.saveState()
|
#tr = self.userTransform.saveState()
|
||||||
|
|
||||||
inv = pg.Transform()
|
inv = pg.SRTTransform()
|
||||||
inv.scale(-1, 1)
|
inv.scale(-1, 1)
|
||||||
self.userTransform = self.userTransform * inv
|
self.userTransform = self.userTransform * inv
|
||||||
self.updateTransform()
|
self.updateTransform()
|
||||||
@ -226,7 +226,7 @@ class CanvasItem(QtCore.QObject):
|
|||||||
if not self.isMovable():
|
if not self.isMovable():
|
||||||
return
|
return
|
||||||
self.rotate(180.)
|
self.rotate(180.)
|
||||||
# inv = pg.Transform()
|
# inv = pg.SRTTransform()
|
||||||
# inv.scale(-1, -1)
|
# inv.scale(-1, -1)
|
||||||
# self.userTransform = self.userTransform * inv #flip lr/ud
|
# self.userTransform = self.userTransform * inv #flip lr/ud
|
||||||
# s=self.updateTransform()
|
# s=self.updateTransform()
|
||||||
@ -311,7 +311,7 @@ class CanvasItem(QtCore.QObject):
|
|||||||
|
|
||||||
|
|
||||||
def resetTemporaryTransform(self):
|
def resetTemporaryTransform(self):
|
||||||
self.tempTransform = pg.Transform() ## don't use Transform.reset()--this transform might be used elsewhere.
|
self.tempTransform = pg.SRTTransform() ## don't use Transform.reset()--this transform might be used elsewhere.
|
||||||
self.updateTransform()
|
self.updateTransform()
|
||||||
|
|
||||||
def transform(self):
|
def transform(self):
|
||||||
@ -363,7 +363,7 @@ class CanvasItem(QtCore.QObject):
|
|||||||
try:
|
try:
|
||||||
#self.userTranslate = pg.Point(tr['trans'])
|
#self.userTranslate = pg.Point(tr['trans'])
|
||||||
#self.userRotate = tr['rot']
|
#self.userRotate = tr['rot']
|
||||||
self.userTransform = pg.Transform(tr)
|
self.userTransform = pg.SRTTransform(tr)
|
||||||
self.updateTransform()
|
self.updateTransform()
|
||||||
|
|
||||||
self.selectBoxFromUser() ## move select box to match
|
self.selectBoxFromUser() ## move select box to match
|
||||||
@ -372,7 +372,7 @@ class CanvasItem(QtCore.QObject):
|
|||||||
except:
|
except:
|
||||||
#self.userTranslate = pg.Point([0,0])
|
#self.userTranslate = pg.Point([0,0])
|
||||||
#self.userRotate = 0
|
#self.userRotate = 0
|
||||||
self.userTransform = pg.Transform()
|
self.userTransform = pg.SRTTransform()
|
||||||
debug.printExc("Failed to load transform:")
|
debug.printExc("Failed to load transform:")
|
||||||
#print "set transform", self, self.userTranslate
|
#print "set transform", self, self.userTranslate
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ from ..Node import Node
|
|||||||
from pyqtgraph.Qt import QtGui, QtCore
|
from pyqtgraph.Qt import QtGui, QtCore
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from .common import *
|
from .common import *
|
||||||
from pyqtgraph.Transform import Transform
|
from pyqtgraph.SRTTransform import SRTTransform
|
||||||
from pyqtgraph.Point import Point
|
from pyqtgraph.Point import Point
|
||||||
from pyqtgraph.widgets.TreeWidget import TreeWidget
|
from pyqtgraph.widgets.TreeWidget import TreeWidget
|
||||||
from pyqtgraph.graphicsItems.LinearRegionItem import LinearRegionItem
|
from pyqtgraph.graphicsItems.LinearRegionItem import LinearRegionItem
|
||||||
|
@ -355,7 +355,6 @@ class ImageItem(GraphicsObject):
|
|||||||
self.drawAt(ev.pos(), ev)
|
self.drawAt(ev.pos(), ev)
|
||||||
|
|
||||||
def raiseContextMenu(self, ev):
|
def raiseContextMenu(self, ev):
|
||||||
## only raise menu if this terminal is removable
|
|
||||||
menu = self.getMenu()
|
menu = self.getMenu()
|
||||||
if menu is None:
|
if menu is None:
|
||||||
return False
|
return False
|
||||||
@ -443,4 +442,8 @@ class ImageItem(GraphicsObject):
|
|||||||
self.drawMask = mask
|
self.drawMask = mask
|
||||||
|
|
||||||
def removeClicked(self):
|
def removeClicked(self):
|
||||||
self.sigRemoveRequested.emit(self)
|
## Send remove event only after we have exited the menu event handler
|
||||||
|
self.removeTimer = QtCore.QTimer()
|
||||||
|
self.removeTimer.timeout.connect(lambda: self.sigRemoveRequested.emit(self))
|
||||||
|
self.removeTimer.start(0)
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ import numpy as np
|
|||||||
from numpy.linalg import norm
|
from numpy.linalg import norm
|
||||||
import scipy.ndimage as ndimage
|
import scipy.ndimage as ndimage
|
||||||
from pyqtgraph.Point import *
|
from pyqtgraph.Point import *
|
||||||
from pyqtgraph.Transform import Transform
|
from pyqtgraph.SRTTransform import SRTTransform
|
||||||
from math import cos, sin
|
from math import cos, sin
|
||||||
import pyqtgraph.functions as fn
|
import pyqtgraph.functions as fn
|
||||||
from .GraphicsObject import GraphicsObject
|
from .GraphicsObject import GraphicsObject
|
||||||
@ -45,8 +45,9 @@ class ROI(GraphicsObject):
|
|||||||
sigRegionChanged = QtCore.Signal(object)
|
sigRegionChanged = QtCore.Signal(object)
|
||||||
sigHoverEvent = QtCore.Signal(object)
|
sigHoverEvent = QtCore.Signal(object)
|
||||||
sigClicked = QtCore.Signal(object, object)
|
sigClicked = QtCore.Signal(object, 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):
|
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)
|
#QObjectWorkaround.__init__(self)
|
||||||
GraphicsObject.__init__(self, parent)
|
GraphicsObject.__init__(self, parent)
|
||||||
self.setAcceptedMouseButtons(QtCore.Qt.NoButton)
|
self.setAcceptedMouseButtons(QtCore.Qt.NoButton)
|
||||||
@ -55,6 +56,8 @@ class ROI(GraphicsObject):
|
|||||||
self.aspectLocked = False
|
self.aspectLocked = False
|
||||||
self.translatable = movable
|
self.translatable = movable
|
||||||
self.rotateAllowed = True
|
self.rotateAllowed = True
|
||||||
|
self.removable = removable
|
||||||
|
self.menu = None
|
||||||
|
|
||||||
self.freeHandleMoved = False ## keep track of whether free handles have moved since last change signal was emitted.
|
self.freeHandleMoved = False ## keep track of whether free handles have moved since last change signal was emitted.
|
||||||
self.mouseHovering = False
|
self.mouseHovering = False
|
||||||
@ -387,13 +390,19 @@ class ROI(GraphicsObject):
|
|||||||
if not ev.isExit():
|
if not ev.isExit():
|
||||||
if self.translatable and ev.acceptDrags(QtCore.Qt.LeftButton):
|
if self.translatable and ev.acceptDrags(QtCore.Qt.LeftButton):
|
||||||
hover=True
|
hover=True
|
||||||
|
|
||||||
for btn in [QtCore.Qt.LeftButton, QtCore.Qt.RightButton, QtCore.Qt.MidButton]:
|
for btn in [QtCore.Qt.LeftButton, QtCore.Qt.RightButton, QtCore.Qt.MidButton]:
|
||||||
if int(self.acceptedMouseButtons() & btn) > 0 and ev.acceptClicks(btn):
|
if int(self.acceptedMouseButtons() & btn) > 0 and ev.acceptClicks(btn):
|
||||||
hover=True
|
hover=True
|
||||||
|
if self.contextMenuEnabled():
|
||||||
|
ev.acceptClicks(QtCore.Qt.RightButton)
|
||||||
|
|
||||||
if hover:
|
if hover:
|
||||||
self.setMouseHover(True)
|
self.setMouseHover(True)
|
||||||
self.sigHoverEvent.emit(self)
|
self.sigHoverEvent.emit(self)
|
||||||
|
ev.acceptClicks(QtCore.Qt.LeftButton) ## If the ROI is hilighted, we should accept all clicks to avoid confusion.
|
||||||
|
ev.acceptClicks(QtCore.Qt.RightButton)
|
||||||
|
ev.acceptClicks(QtCore.Qt.MidButton)
|
||||||
else:
|
else:
|
||||||
self.setMouseHover(False)
|
self.setMouseHover(False)
|
||||||
|
|
||||||
@ -408,6 +417,34 @@ class ROI(GraphicsObject):
|
|||||||
self.currentPen = self.pen
|
self.currentPen = self.pen
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
|
def contextMenuEnabled(self):
|
||||||
|
return self.removable
|
||||||
|
|
||||||
|
def raiseContextMenu(self, ev):
|
||||||
|
if not self.contextMenuEnabled():
|
||||||
|
return
|
||||||
|
menu = self.getMenu()
|
||||||
|
menu = self.scene().addParentContextMenus(self, menu, ev)
|
||||||
|
pos = ev.screenPos()
|
||||||
|
menu.popup(QtCore.QPoint(pos.x(), pos.y()))
|
||||||
|
|
||||||
|
def getMenu(self):
|
||||||
|
if self.menu is None:
|
||||||
|
self.menu = QtGui.QMenu()
|
||||||
|
self.menu.setTitle("ROI")
|
||||||
|
remAct = QtGui.QAction("Remove ROI", self.menu)
|
||||||
|
remAct.triggered.connect(self.removeClicked)
|
||||||
|
self.menu.addAction(remAct)
|
||||||
|
self.menu.remAct = remAct
|
||||||
|
return self.menu
|
||||||
|
|
||||||
|
def removeClicked(self):
|
||||||
|
## Send remove event only after we have exited the menu event handler
|
||||||
|
self.removeTimer = QtCore.QTimer()
|
||||||
|
self.removeTimer.timeout.connect(lambda: self.sigRemoveRequested.emit(self))
|
||||||
|
self.removeTimer.start(0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def mouseDragEvent(self, ev):
|
def mouseDragEvent(self, ev):
|
||||||
if ev.isStart():
|
if ev.isStart():
|
||||||
@ -442,6 +479,9 @@ class ROI(GraphicsObject):
|
|||||||
if ev.button() == QtCore.Qt.RightButton and self.isMoving:
|
if ev.button() == QtCore.Qt.RightButton and self.isMoving:
|
||||||
ev.accept()
|
ev.accept()
|
||||||
self.cancelMove()
|
self.cancelMove()
|
||||||
|
if ev.button() == QtCore.Qt.RightButton and self.contextMenuEnabled():
|
||||||
|
self.raiseContextMenu(ev)
|
||||||
|
ev.accept()
|
||||||
elif int(ev.button() & self.acceptedMouseButtons()) > 0:
|
elif int(ev.button() & self.acceptedMouseButtons()) > 0:
|
||||||
ev.accept()
|
ev.accept()
|
||||||
self.sigClicked.emit(self, ev)
|
self.sigClicked.emit(self, ev)
|
||||||
@ -933,8 +973,8 @@ class ROI(GraphicsObject):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
t1 = Transform(relativeTo)
|
t1 = SRTTransform(relativeTo)
|
||||||
t2 = Transform(st)
|
t2 = SRTTransform(st)
|
||||||
return t2/t1
|
return t2/t1
|
||||||
|
|
||||||
|
|
||||||
@ -962,7 +1002,7 @@ class ROI(GraphicsObject):
|
|||||||
st = self.getState()
|
st = self.getState()
|
||||||
|
|
||||||
st['scale'] = st['size']
|
st['scale'] = st['size']
|
||||||
st = Transform(st)
|
st = SRTTransform(st)
|
||||||
st = (st * tr).saveState()
|
st = (st * tr).saveState()
|
||||||
st['size'] = st['scale']
|
st['size'] = st['scale']
|
||||||
self.setState(st)
|
self.setState(st)
|
||||||
|
Loading…
Reference in New Issue
Block a user