Many minor updates:
- added ability for ScatterPlotItem to use arbitrary symbol shapes - added scatter plot speed test for evaluating new methods - added butterworth notch filter to flowchart library - fixed bugs with ViewBox trying to close itself after python has started cleaning up - fixed python 2.6 compatibility bug in PlotCurveItem - fixed support for list-of-dicts and dict-of-lists input for PlotDataItem - check to ensure Qt version is >= 4.7 - workaround for numpy segmentation fault - several other minor updates and documentation changes
This commit is contained in:
parent
01deeb7579
commit
27c90c5dd5
12
Qt.py
12
Qt.py
|
@ -1,5 +1,5 @@
|
||||||
## Do all Qt imports from here to allow easier PyQt / PySide compatibility
|
## Do all Qt imports from here to allow easier PyQt / PySide compatibility
|
||||||
import sys
|
import sys, re
|
||||||
|
|
||||||
## Automatically determine whether to use PyQt or PySide.
|
## Automatically determine whether to use PyQt or PySide.
|
||||||
## This is done by first checking to see whether one of the libraries
|
## This is done by first checking to see whether one of the libraries
|
||||||
|
@ -36,3 +36,13 @@ else:
|
||||||
|
|
||||||
QtCore.Signal = QtCore.pyqtSignal
|
QtCore.Signal = QtCore.pyqtSignal
|
||||||
VERSION_INFO = 'PyQt4 ' + QtCore.PYQT_VERSION_STR + ' Qt ' + QtCore.QT_VERSION_STR
|
VERSION_INFO = 'PyQt4 ' + QtCore.PYQT_VERSION_STR + ' Qt ' + QtCore.QT_VERSION_STR
|
||||||
|
|
||||||
|
|
||||||
|
## Make sure we have Qt >= 4.7
|
||||||
|
versionReq = [4, 7]
|
||||||
|
QtVersion = PySide.QtCore.__version__ if USE_PYSIDE else QtCore.QT_VERSION_STR
|
||||||
|
m = re.match(r'(\d+)\.(\d+).*', QtVersion)
|
||||||
|
if m is not None and map(int, m.groups()) < versionReq:
|
||||||
|
print map(int, m.groups())
|
||||||
|
raise Exception('pyqtgraph requires Qt version >= %d.%d (your version is %s)' % (versionReq + (QtVersion,)))
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,11 @@ class SRTTransform(QtGui.QTransform):
|
||||||
def getScale(self):
|
def getScale(self):
|
||||||
return self._state['scale']
|
return self._state['scale']
|
||||||
|
|
||||||
def getAngle(self):
|
def getAngle(self):
|
||||||
|
## deprecated; for backward compatibility
|
||||||
|
return self.getRotation()
|
||||||
|
|
||||||
|
def getRotation(self):
|
||||||
return self._state['angle']
|
return self._state['angle']
|
||||||
|
|
||||||
def getTranslation(self):
|
def getTranslation(self):
|
||||||
|
|
|
@ -1,302 +1,302 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from .Qt import QtCore, QtGui
|
from .Qt import QtCore, QtGui
|
||||||
from .Vector import Vector
|
from .Vector import Vector
|
||||||
from .SRTTransform import SRTTransform
|
from .SRTTransform import SRTTransform
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import scipy.linalg
|
import scipy.linalg
|
||||||
|
|
||||||
class SRTTransform3D(QtGui.QMatrix4x4):
|
class SRTTransform3D(QtGui.QMatrix4x4):
|
||||||
"""4x4 Transform matrix that can always be represented as a combination of 3 matrices: scale * rotate * translate
|
"""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.
|
This transform has no shear; angles are always preserved.
|
||||||
"""
|
"""
|
||||||
def __init__(self, init=None):
|
def __init__(self, init=None):
|
||||||
QtGui.QMatrix4x4.__init__(self)
|
QtGui.QMatrix4x4.__init__(self)
|
||||||
self.reset()
|
self.reset()
|
||||||
if init is None:
|
if init is None:
|
||||||
return
|
return
|
||||||
if init.__class__ is QtGui.QTransform:
|
if init.__class__ is QtGui.QTransform:
|
||||||
init = SRTTransform(init)
|
init = SRTTransform(init)
|
||||||
|
|
||||||
if isinstance(init, dict):
|
if isinstance(init, dict):
|
||||||
self.restoreState(init)
|
self.restoreState(init)
|
||||||
elif isinstance(init, SRTTransform3D):
|
elif isinstance(init, SRTTransform3D):
|
||||||
self._state = {
|
self._state = {
|
||||||
'pos': Vector(init._state['pos']),
|
'pos': Vector(init._state['pos']),
|
||||||
'scale': Vector(init._state['scale']),
|
'scale': Vector(init._state['scale']),
|
||||||
'angle': init._state['angle'],
|
'angle': init._state['angle'],
|
||||||
'axis': Vector(init._state['axis']),
|
'axis': Vector(init._state['axis']),
|
||||||
}
|
}
|
||||||
self.update()
|
self.update()
|
||||||
elif isinstance(init, SRTTransform):
|
elif isinstance(init, SRTTransform):
|
||||||
self._state = {
|
self._state = {
|
||||||
'pos': Vector(init._state['pos']),
|
'pos': Vector(init._state['pos']),
|
||||||
'scale': Vector(init._state['scale']),
|
'scale': Vector(init._state['scale']),
|
||||||
'angle': init._state['angle'],
|
'angle': init._state['angle'],
|
||||||
'axis': Vector(0, 0, 1),
|
'axis': Vector(0, 0, 1),
|
||||||
}
|
}
|
||||||
self.update()
|
self.update()
|
||||||
elif isinstance(init, QtGui.QMatrix4x4):
|
elif isinstance(init, QtGui.QMatrix4x4):
|
||||||
self.setFromMatrix(init)
|
self.setFromMatrix(init)
|
||||||
else:
|
else:
|
||||||
raise Exception("Cannot build SRTTransform3D from argument type:", type(init))
|
raise Exception("Cannot build SRTTransform3D from argument type:", type(init))
|
||||||
|
|
||||||
|
|
||||||
def getScale(self):
|
def getScale(self):
|
||||||
return pg.Vector(self._state['scale'])
|
return pg.Vector(self._state['scale'])
|
||||||
|
|
||||||
def getRotation(self):
|
def getRotation(self):
|
||||||
"""Return (angle, axis) of rotation"""
|
"""Return (angle, axis) of rotation"""
|
||||||
return self._state['angle'], pg.Vector(self._state['axis'])
|
return self._state['angle'], pg.Vector(self._state['axis'])
|
||||||
|
|
||||||
def getTranslation(self):
|
def getTranslation(self):
|
||||||
return pg.Vector(self._state['pos'])
|
return pg.Vector(self._state['pos'])
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
self._state = {
|
self._state = {
|
||||||
'pos': Vector(0,0,0),
|
'pos': Vector(0,0,0),
|
||||||
'scale': Vector(1,1,1),
|
'scale': Vector(1,1,1),
|
||||||
'angle': 0.0, ## in degrees
|
'angle': 0.0, ## in degrees
|
||||||
'axis': (0, 0, 1)
|
'axis': (0, 0, 1)
|
||||||
}
|
}
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def translate(self, *args):
|
def translate(self, *args):
|
||||||
"""Adjust the translation of this transform"""
|
"""Adjust the translation of this transform"""
|
||||||
t = Vector(*args)
|
t = Vector(*args)
|
||||||
self.setTranslate(self._state['pos']+t)
|
self.setTranslate(self._state['pos']+t)
|
||||||
|
|
||||||
def setTranslate(self, *args):
|
def setTranslate(self, *args):
|
||||||
"""Set the translation of this transform"""
|
"""Set the translation of this transform"""
|
||||||
self._state['pos'] = Vector(*args)
|
self._state['pos'] = Vector(*args)
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def scale(self, *args):
|
def scale(self, *args):
|
||||||
"""adjust the scale of this transform"""
|
"""adjust the scale of this transform"""
|
||||||
## try to prevent accidentally setting 0 scale on z axis
|
## try to prevent accidentally setting 0 scale on z axis
|
||||||
if len(args) == 1 and hasattr(args[0], '__len__'):
|
if len(args) == 1 and hasattr(args[0], '__len__'):
|
||||||
args = args[0]
|
args = args[0]
|
||||||
if len(args) == 2:
|
if len(args) == 2:
|
||||||
args = args + (1,)
|
args = args + (1,)
|
||||||
|
|
||||||
s = Vector(*args)
|
s = Vector(*args)
|
||||||
self.setScale(self._state['scale'] * s)
|
self.setScale(self._state['scale'] * s)
|
||||||
|
|
||||||
def setScale(self, *args):
|
def setScale(self, *args):
|
||||||
"""Set the scale of this transform"""
|
"""Set the scale of this transform"""
|
||||||
if len(args) == 1 and hasattr(args[0], '__len__'):
|
if len(args) == 1 and hasattr(args[0], '__len__'):
|
||||||
args = args[0]
|
args = args[0]
|
||||||
if len(args) == 2:
|
if len(args) == 2:
|
||||||
args = args + (1,)
|
args = args + (1,)
|
||||||
self._state['scale'] = Vector(*args)
|
self._state['scale'] = Vector(*args)
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def rotate(self, angle, axis=(0,0,1)):
|
def rotate(self, angle, axis=(0,0,1)):
|
||||||
"""Adjust the rotation of this transform"""
|
"""Adjust the rotation of this transform"""
|
||||||
origAxis = self._state['axis']
|
origAxis = self._state['axis']
|
||||||
if axis[0] == origAxis[0] and axis[1] == origAxis[1] and axis[2] == origAxis[2]:
|
if axis[0] == origAxis[0] and axis[1] == origAxis[1] and axis[2] == origAxis[2]:
|
||||||
self.setRotate(self._state['angle'] + angle)
|
self.setRotate(self._state['angle'] + angle)
|
||||||
else:
|
else:
|
||||||
m = QtGui.QMatrix4x4()
|
m = QtGui.QMatrix4x4()
|
||||||
m.translate(*self._state['pos'])
|
m.translate(*self._state['pos'])
|
||||||
m.rotate(self._state['angle'], *self._state['axis'])
|
m.rotate(self._state['angle'], *self._state['axis'])
|
||||||
m.rotate(angle, *axis)
|
m.rotate(angle, *axis)
|
||||||
m.scale(*self._state['scale'])
|
m.scale(*self._state['scale'])
|
||||||
self.setFromMatrix(m)
|
self.setFromMatrix(m)
|
||||||
|
|
||||||
def setRotate(self, angle, axis=(0,0,1)):
|
def setRotate(self, angle, axis=(0,0,1)):
|
||||||
"""Set the transformation rotation to angle (in degrees)"""
|
"""Set the transformation rotation to angle (in degrees)"""
|
||||||
|
|
||||||
self._state['angle'] = angle
|
self._state['angle'] = angle
|
||||||
self._state['axis'] = Vector(axis)
|
self._state['axis'] = Vector(axis)
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def setFromMatrix(self, m):
|
def setFromMatrix(self, m):
|
||||||
"""
|
"""
|
||||||
Set this transform mased on the elements of *m*
|
Set this transform mased on the elements of *m*
|
||||||
The input matrix must be affine AND have no shear,
|
The input matrix must be affine AND have no shear,
|
||||||
otherwise the conversion will most likely fail.
|
otherwise the conversion will most likely fail.
|
||||||
"""
|
"""
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
self.setRow(i, m.row(i))
|
self.setRow(i, m.row(i))
|
||||||
m = self.matrix().reshape(4,4)
|
m = self.matrix().reshape(4,4)
|
||||||
## translation is 4th column
|
## translation is 4th column
|
||||||
self._state['pos'] = m[:3,3]
|
self._state['pos'] = m[:3,3]
|
||||||
|
|
||||||
## scale is vector-length of first three columns
|
## scale is vector-length of first three columns
|
||||||
scale = (m[:3,:3]**2).sum(axis=0)**0.5
|
scale = (m[:3,:3]**2).sum(axis=0)**0.5
|
||||||
## see whether there is an inversion
|
## see whether there is an inversion
|
||||||
z = np.cross(m[0, :3], m[1, :3])
|
z = np.cross(m[0, :3], m[1, :3])
|
||||||
if np.dot(z, m[2, :3]) < 0:
|
if np.dot(z, m[2, :3]) < 0:
|
||||||
scale[1] *= -1 ## doesn't really matter which axis we invert
|
scale[1] *= -1 ## doesn't really matter which axis we invert
|
||||||
self._state['scale'] = scale
|
self._state['scale'] = scale
|
||||||
|
|
||||||
## rotation axis is the eigenvector with eigenvalue=1
|
## rotation axis is the eigenvector with eigenvalue=1
|
||||||
r = m[:3, :3] / scale[:, np.newaxis]
|
r = m[:3, :3] / scale[:, np.newaxis]
|
||||||
try:
|
try:
|
||||||
evals, evecs = scipy.linalg.eig(r)
|
evals, evecs = scipy.linalg.eig(r)
|
||||||
except:
|
except:
|
||||||
print("Rotation matrix: %s" % str(r))
|
print("Rotation matrix: %s" % str(r))
|
||||||
print("Scale: %s" % str(scale))
|
print("Scale: %s" % str(scale))
|
||||||
print("Original matrix: %s" % str(m))
|
print("Original matrix: %s" % str(m))
|
||||||
raise
|
raise
|
||||||
eigIndex = np.argwhere(np.abs(evals-1) < 1e-7)
|
eigIndex = np.argwhere(np.abs(evals-1) < 1e-7)
|
||||||
if len(eigIndex) < 1:
|
if len(eigIndex) < 1:
|
||||||
print("eigenvalues: %s" % str(evals))
|
print("eigenvalues: %s" % str(evals))
|
||||||
print("eigenvectors: %s" % str(evecs))
|
print("eigenvectors: %s" % str(evecs))
|
||||||
print("index: %s, %s" % (str(eigIndex), str(evals-1)))
|
print("index: %s, %s" % (str(eigIndex), str(evals-1)))
|
||||||
raise Exception("Could not determine rotation axis.")
|
raise Exception("Could not determine rotation axis.")
|
||||||
axis = evecs[eigIndex[0,0]].real
|
axis = evecs[eigIndex[0,0]].real
|
||||||
axis /= ((axis**2).sum())**0.5
|
axis /= ((axis**2).sum())**0.5
|
||||||
self._state['axis'] = axis
|
self._state['axis'] = axis
|
||||||
|
|
||||||
## trace(r) == 2 cos(angle) + 1, so:
|
## trace(r) == 2 cos(angle) + 1, so:
|
||||||
self._state['angle'] = np.arccos((r.trace()-1)*0.5) * 180 / np.pi
|
self._state['angle'] = np.arccos((r.trace()-1)*0.5) * 180 / np.pi
|
||||||
if self._state['angle'] == 0:
|
if self._state['angle'] == 0:
|
||||||
self._state['axis'] = (0,0,1)
|
self._state['axis'] = (0,0,1)
|
||||||
|
|
||||||
def as2D(self):
|
def as2D(self):
|
||||||
"""Return a QTransform representing the x,y portion of this transform (if possible)"""
|
"""Return a QTransform representing the x,y portion of this transform (if possible)"""
|
||||||
return pg.SRTTransform(self)
|
return pg.SRTTransform(self)
|
||||||
|
|
||||||
#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 SRTTransform(dt)
|
#return SRTTransform(dt)
|
||||||
|
|
||||||
#def __mul__(self, t):
|
#def __mul__(self, t):
|
||||||
#return SRTTransform(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']
|
||||||
s = self._state['scale']
|
s = self._state['scale']
|
||||||
ax = self._state['axis']
|
ax = self._state['axis']
|
||||||
#if s[0] == 0:
|
#if s[0] == 0:
|
||||||
#raise Exception('Invalid scale: %s' % str(s))
|
#raise Exception('Invalid scale: %s' % str(s))
|
||||||
return {
|
return {
|
||||||
'pos': (p[0], p[1], p[2]),
|
'pos': (p[0], p[1], p[2]),
|
||||||
'scale': (s[0], s[1], s[2]),
|
'scale': (s[0], s[1], s[2]),
|
||||||
'angle': self._state['angle'],
|
'angle': self._state['angle'],
|
||||||
'axis': (ax[0], ax[1], ax[2])
|
'axis': (ax[0], ax[1], ax[2])
|
||||||
}
|
}
|
||||||
|
|
||||||
def restoreState(self, state):
|
def restoreState(self, state):
|
||||||
self._state['pos'] = Vector(state.get('pos', (0.,0.,0.)))
|
self._state['pos'] = Vector(state.get('pos', (0.,0.,0.)))
|
||||||
scale = state.get('scale', (1.,1.,1.))
|
scale = state.get('scale', (1.,1.,1.))
|
||||||
scale = scale + (1.,) * (3-len(scale))
|
scale = tuple(scale) + (1.,) * (3-len(scale))
|
||||||
self._state['scale'] = Vector(scale)
|
self._state['scale'] = Vector(scale)
|
||||||
self._state['angle'] = state.get('angle', 0.)
|
self._state['angle'] = state.get('angle', 0.)
|
||||||
self._state['axis'] = state.get('axis', (0, 0, 1))
|
self._state['axis'] = state.get('axis', (0, 0, 1))
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
QtGui.QMatrix4x4.setToIdentity(self)
|
QtGui.QMatrix4x4.setToIdentity(self)
|
||||||
## modifications to the transform are multiplied on the right, so we need to reverse order here.
|
## 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.translate(self, *self._state['pos'])
|
||||||
QtGui.QMatrix4x4.rotate(self, self._state['angle'], *self._state['axis'])
|
QtGui.QMatrix4x4.rotate(self, self._state['angle'], *self._state['axis'])
|
||||||
QtGui.QMatrix4x4.scale(self, *self._state['scale'])
|
QtGui.QMatrix4x4.scale(self, *self._state['scale'])
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return str(self.saveState())
|
return str(self.saveState())
|
||||||
|
|
||||||
def matrix(self, nd=3):
|
def matrix(self, nd=3):
|
||||||
if nd == 3:
|
if nd == 3:
|
||||||
return np.array(self.copyDataTo())
|
return np.array(self.copyDataTo())
|
||||||
elif nd == 2:
|
elif nd == 2:
|
||||||
m = np.array(self.copyDataTo())
|
m = np.array(self.copyDataTo())
|
||||||
m[2] = m[3]
|
m[2] = m[3]
|
||||||
m[:,2] = n[:,3]
|
m[:,2] = n[:,3]
|
||||||
return m[:3,:3]
|
return m[:3,:3]
|
||||||
else:
|
else:
|
||||||
raise Exception("Argument 'nd' must be 2 or 3")
|
raise Exception("Argument 'nd' must be 2 or 3")
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import widgets
|
import widgets
|
||||||
import GraphicsView
|
import GraphicsView
|
||||||
from functions import *
|
from functions import *
|
||||||
app = QtGui.QApplication([])
|
app = QtGui.QApplication([])
|
||||||
win = QtGui.QMainWindow()
|
win = QtGui.QMainWindow()
|
||||||
win.show()
|
win.show()
|
||||||
cw = GraphicsView.GraphicsView()
|
cw = GraphicsView.GraphicsView()
|
||||||
#cw.enableMouse()
|
#cw.enableMouse()
|
||||||
win.setCentralWidget(cw)
|
win.setCentralWidget(cw)
|
||||||
s = QtGui.QGraphicsScene()
|
s = QtGui.QGraphicsScene()
|
||||||
cw.setScene(s)
|
cw.setScene(s)
|
||||||
win.resize(600,600)
|
win.resize(600,600)
|
||||||
cw.enableMouse()
|
cw.enableMouse()
|
||||||
cw.setRange(QtCore.QRectF(-100., -100., 200., 200.))
|
cw.setRange(QtCore.QRectF(-100., -100., 200., 200.))
|
||||||
|
|
||||||
class Item(QtGui.QGraphicsItem):
|
class Item(QtGui.QGraphicsItem):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
QtGui.QGraphicsItem.__init__(self)
|
QtGui.QGraphicsItem.__init__(self)
|
||||||
self.b = QtGui.QGraphicsRectItem(20, 20, 20, 20, self)
|
self.b = QtGui.QGraphicsRectItem(20, 20, 20, 20, self)
|
||||||
self.b.setPen(QtGui.QPen(mkPen('y')))
|
self.b.setPen(QtGui.QPen(mkPen('y')))
|
||||||
self.t1 = QtGui.QGraphicsTextItem(self)
|
self.t1 = QtGui.QGraphicsTextItem(self)
|
||||||
self.t1.setHtml('<span style="color: #F00">R</span>')
|
self.t1.setHtml('<span style="color: #F00">R</span>')
|
||||||
self.t1.translate(20, 20)
|
self.t1.translate(20, 20)
|
||||||
self.l1 = QtGui.QGraphicsLineItem(10, 0, -10, 0, self)
|
self.l1 = QtGui.QGraphicsLineItem(10, 0, -10, 0, self)
|
||||||
self.l2 = QtGui.QGraphicsLineItem(0, 10, 0, -10, self)
|
self.l2 = QtGui.QGraphicsLineItem(0, 10, 0, -10, self)
|
||||||
self.l1.setPen(QtGui.QPen(mkPen('y')))
|
self.l1.setPen(QtGui.QPen(mkPen('y')))
|
||||||
self.l2.setPen(QtGui.QPen(mkPen('y')))
|
self.l2.setPen(QtGui.QPen(mkPen('y')))
|
||||||
def boundingRect(self):
|
def boundingRect(self):
|
||||||
return QtCore.QRectF()
|
return QtCore.QRectF()
|
||||||
def paint(self, *args):
|
def paint(self, *args):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
#s.addItem(b)
|
#s.addItem(b)
|
||||||
#s.addItem(t1)
|
#s.addItem(t1)
|
||||||
item = Item()
|
item = Item()
|
||||||
s.addItem(item)
|
s.addItem(item)
|
||||||
l1 = QtGui.QGraphicsLineItem(10, 0, -10, 0)
|
l1 = QtGui.QGraphicsLineItem(10, 0, -10, 0)
|
||||||
l2 = QtGui.QGraphicsLineItem(0, 10, 0, -10)
|
l2 = QtGui.QGraphicsLineItem(0, 10, 0, -10)
|
||||||
l1.setPen(QtGui.QPen(mkPen('r')))
|
l1.setPen(QtGui.QPen(mkPen('r')))
|
||||||
l2.setPen(QtGui.QPen(mkPen('r')))
|
l2.setPen(QtGui.QPen(mkPen('r')))
|
||||||
s.addItem(l1)
|
s.addItem(l1)
|
||||||
s.addItem(l2)
|
s.addItem(l2)
|
||||||
|
|
||||||
tr1 = SRTTransform()
|
tr1 = SRTTransform()
|
||||||
tr2 = SRTTransform()
|
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: %s" % str(SRTTransform(tr3)))
|
print("QTransform -> Transform: %s" % str(SRTTransform(tr3)))
|
||||||
|
|
||||||
print("tr1: %s" % str(tr1))
|
print("tr1: %s" % str(tr1))
|
||||||
|
|
||||||
tr2.translate(20, 0)
|
tr2.translate(20, 0)
|
||||||
tr2.rotate(45)
|
tr2.rotate(45)
|
||||||
print("tr2: %s" % str(tr2))
|
print("tr2: %s" % str(tr2))
|
||||||
|
|
||||||
dt = tr2/tr1
|
dt = tr2/tr1
|
||||||
print("tr2 / tr1 = %s" % str(dt))
|
print("tr2 / tr1 = %s" % str(dt))
|
||||||
|
|
||||||
print("tr2 * tr1 = %s" % str(tr2*tr1))
|
print("tr2 * tr1 = %s" % str(tr2*tr1))
|
||||||
|
|
||||||
tr4 = SRTTransform()
|
tr4 = SRTTransform()
|
||||||
tr4.scale(-1, 1)
|
tr4.scale(-1, 1)
|
||||||
tr4.rotate(30)
|
tr4.rotate(30)
|
||||||
print("tr1 * tr4 = %s" % str(tr1*tr4))
|
print("tr1 * tr4 = %s" % str(tr1*tr4))
|
||||||
|
|
||||||
w1 = widgets.TestROI((19,19), (22, 22), invertible=True)
|
w1 = widgets.TestROI((19,19), (22, 22), invertible=True)
|
||||||
#w2 = widgets.TestROI((0,0), (150, 150))
|
#w2 = widgets.TestROI((0,0), (150, 150))
|
||||||
w1.setZValue(10)
|
w1.setZValue(10)
|
||||||
s.addItem(w1)
|
s.addItem(w1)
|
||||||
#s.addItem(w2)
|
#s.addItem(w2)
|
||||||
w1Base = w1.getState()
|
w1Base = w1.getState()
|
||||||
#w2Base = w2.getState()
|
#w2Base = w2.getState()
|
||||||
def update():
|
def update():
|
||||||
tr1 = w1.getGlobalTransform(w1Base)
|
tr1 = w1.getGlobalTransform(w1Base)
|
||||||
#tr2 = w2.getGlobalTransform(w2Base)
|
#tr2 = w2.getGlobalTransform(w2Base)
|
||||||
item.setTransform(tr1)
|
item.setTransform(tr1)
|
||||||
|
|
||||||
#def update2():
|
#def update2():
|
||||||
#tr1 = w1.getGlobalTransform(w1Base)
|
#tr1 = w1.getGlobalTransform(w1Base)
|
||||||
#tr2 = w2.getGlobalTransform(w2Base)
|
#tr2 = w2.getGlobalTransform(w2Base)
|
||||||
#t1.setTransform(tr1)
|
#t1.setTransform(tr1)
|
||||||
#w1.setState(w1Base)
|
#w1.setState(w1Base)
|
||||||
#w1.applyGlobalTransform(tr2)
|
#w1.applyGlobalTransform(tr2)
|
||||||
|
|
||||||
w1.sigRegionChanged.connect(update)
|
w1.sigRegionChanged.connect(update)
|
||||||
#w2.sigRegionChanged.connect(update2)
|
#w2.sigRegionChanged.connect(update2)
|
||||||
|
|
||||||
|
|
19
__init__.py
19
__init__.py
|
@ -21,7 +21,9 @@ if sys.version_info[0] < 2 or (sys.version_info[0] == 2 and sys.version_info[1]
|
||||||
## helpers for 2/3 compatibility
|
## helpers for 2/3 compatibility
|
||||||
from . import python2_3
|
from . import python2_3
|
||||||
|
|
||||||
|
## install workarounds for numpy bugs
|
||||||
|
import numpy_fix
|
||||||
|
|
||||||
## in general openGL is poorly supported with Qt+GraphicsView.
|
## 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.
|
||||||
|
@ -215,6 +217,21 @@ def image(*args, **kargs):
|
||||||
w.show()
|
w.show()
|
||||||
return w
|
return w
|
||||||
show = image ## for backward compatibility
|
show = image ## for backward compatibility
|
||||||
|
|
||||||
|
def dbg():
|
||||||
|
"""
|
||||||
|
Create a console window and begin watching for exceptions.
|
||||||
|
"""
|
||||||
|
mkQApp()
|
||||||
|
import console
|
||||||
|
c = console.ConsoleWidget()
|
||||||
|
c.catchAllExceptions()
|
||||||
|
c.show()
|
||||||
|
global consoles
|
||||||
|
try:
|
||||||
|
consoles.append(c)
|
||||||
|
except NameError:
|
||||||
|
consoles = [c]
|
||||||
|
|
||||||
|
|
||||||
def mkQApp():
|
def mkQApp():
|
||||||
|
|
|
@ -184,7 +184,7 @@ class Canvas(QtGui.QWidget):
|
||||||
#if gi is None:
|
#if gi is None:
|
||||||
#return
|
#return
|
||||||
try:
|
try:
|
||||||
citem = item.canvasItem
|
citem = item.canvasItem()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return
|
return
|
||||||
if item.checkState(0) == QtCore.Qt.Checked:
|
if item.checkState(0) == QtCore.Qt.Checked:
|
||||||
|
@ -238,7 +238,7 @@ class Canvas(QtGui.QWidget):
|
||||||
"""
|
"""
|
||||||
Return list of all selected canvasItems
|
Return list of all selected canvasItems
|
||||||
"""
|
"""
|
||||||
return [item.canvasItem for item in self.itemList.selectedItems() if item.canvasItem is not None]
|
return [item.canvasItem() for item in self.itemList.selectedItems() if item.canvasItem() is not None]
|
||||||
|
|
||||||
#def selectedItem(self):
|
#def selectedItem(self):
|
||||||
#sel = self.itemList.selectedItems()
|
#sel = self.itemList.selectedItems()
|
||||||
|
@ -371,7 +371,7 @@ class Canvas(QtGui.QWidget):
|
||||||
parent = parent.listItem
|
parent = parent.listItem
|
||||||
|
|
||||||
## set Z value above all other siblings if none was specified
|
## set Z value above all other siblings if none was specified
|
||||||
siblings = [parent.child(i).canvasItem for i in range(parent.childCount())]
|
siblings = [parent.child(i).canvasItem() for i in range(parent.childCount())]
|
||||||
z = citem.zValue()
|
z = citem.zValue()
|
||||||
if z is None:
|
if z is None:
|
||||||
zvals = [i.zValue() for i in siblings]
|
zvals = [i.zValue() for i in siblings]
|
||||||
|
@ -382,7 +382,7 @@ class Canvas(QtGui.QWidget):
|
||||||
z = max(zvals)+10
|
z = max(zvals)+10
|
||||||
else:
|
else:
|
||||||
if len(zvals) == 0:
|
if len(zvals) == 0:
|
||||||
z = parent.canvasItem.zValue()
|
z = parent.canvasItem().zValue()
|
||||||
else:
|
else:
|
||||||
z = max(zvals)+1
|
z = max(zvals)+1
|
||||||
citem.setZValue(z)
|
citem.setZValue(z)
|
||||||
|
@ -390,7 +390,7 @@ class Canvas(QtGui.QWidget):
|
||||||
## determine location to insert item relative to its siblings
|
## determine location to insert item relative to its siblings
|
||||||
for i in range(parent.childCount()):
|
for i in range(parent.childCount()):
|
||||||
ch = parent.child(i)
|
ch = parent.child(i)
|
||||||
zval = ch.canvasItem.graphicsItem().zValue() ## should we use CanvasItem.zValue here?
|
zval = ch.canvasItem().graphicsItem().zValue() ## should we use CanvasItem.zValue here?
|
||||||
if zval < z:
|
if zval < z:
|
||||||
insertLocation = i
|
insertLocation = i
|
||||||
break
|
break
|
||||||
|
@ -416,7 +416,7 @@ class Canvas(QtGui.QWidget):
|
||||||
|
|
||||||
citem.name = name
|
citem.name = name
|
||||||
citem.listItem = node
|
citem.listItem = node
|
||||||
node.canvasItem = citem
|
node.canvasItem = weakref.ref(citem)
|
||||||
self.items.append(citem)
|
self.items.append(citem)
|
||||||
|
|
||||||
ctrl = citem.ctrlWidget()
|
ctrl = citem.ctrlWidget()
|
||||||
|
@ -465,10 +465,10 @@ class Canvas(QtGui.QWidget):
|
||||||
def treeItemMoved(self, item, parent, index):
|
def treeItemMoved(self, item, parent, index):
|
||||||
##Item moved in tree; update Z values
|
##Item moved in tree; update Z values
|
||||||
if parent is self.itemList.invisibleRootItem():
|
if parent is self.itemList.invisibleRootItem():
|
||||||
item.canvasItem.setParentItem(self.view.childGroup)
|
item.canvasItem().setParentItem(self.view.childGroup)
|
||||||
else:
|
else:
|
||||||
item.canvasItem.setParentItem(parent.canvasItem)
|
item.canvasItem().setParentItem(parent.canvasItem())
|
||||||
siblings = [parent.child(i).canvasItem for i in range(parent.childCount())]
|
siblings = [parent.child(i).canvasItem() for i in range(parent.childCount())]
|
||||||
|
|
||||||
zvals = [i.zValue() for i in siblings]
|
zvals = [i.zValue() for i in siblings]
|
||||||
zvals.sort(reverse=True)
|
zvals.sort(reverse=True)
|
||||||
|
|
|
@ -339,19 +339,31 @@ class ConsoleWidget(QtGui.QWidget):
|
||||||
if excType is GeneratorExit or excType is StopIteration:
|
if excType is GeneratorExit or excType is StopIteration:
|
||||||
return False
|
return False
|
||||||
if excType is KeyError:
|
if excType is KeyError:
|
||||||
if filename.endswith('python2.7/weakref.py') and function == '__contains__':
|
if filename.endswith('python2.7/weakref.py') and function in ('__contains__', 'get'):
|
||||||
return False
|
return False
|
||||||
if filename.endswith('python2.7/copy.py') and function == '_keep_alive':
|
if filename.endswith('python2.7/copy.py') and function == '_keep_alive':
|
||||||
return False
|
return False
|
||||||
if excType is AttributeError:
|
if excType is AttributeError:
|
||||||
if filename.endswith('python2.7/collections.py') and function == '__init__':
|
if filename.endswith('python2.7/collections.py') and function == '__init__':
|
||||||
return False
|
return False
|
||||||
if filename.endswith('numpy/core/fromnumeric.py') and function in ('all', '_wrapit', 'transpose'):
|
if filename.endswith('numpy/core/fromnumeric.py') and function in ('all', '_wrapit', 'transpose', 'sum'):
|
||||||
|
return False
|
||||||
|
if filename.endswith('numpy/core/arrayprint.py') and function in ('_array2string'):
|
||||||
return False
|
return False
|
||||||
if filename.endswith('MetaArray.py') and function == '__getattr__':
|
if filename.endswith('MetaArray.py') and function == '__getattr__':
|
||||||
for name in ('__array_interface__', '__array_struct__', '__array__'): ## numpy looks for these when converting objects to array
|
for name in ('__array_interface__', '__array_struct__', '__array__'): ## numpy looks for these when converting objects to array
|
||||||
if name in exc:
|
if name in exc:
|
||||||
return False
|
return False
|
||||||
|
if filename.endswith('flowchart/eq.py'):
|
||||||
|
return False
|
||||||
|
if filename.endswith('pyqtgraph/functions.py') and function == 'makeQImage':
|
||||||
|
return False
|
||||||
|
if excType is TypeError:
|
||||||
|
if filename.endswith('numpy/lib/function_base.py') and function == 'iterable':
|
||||||
|
return False
|
||||||
|
if excType is ZeroDivisionError:
|
||||||
|
if filename.endswith('python2.7/traceback.py'):
|
||||||
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
Installation
|
Installation
|
||||||
============
|
============
|
||||||
|
|
||||||
Pyqtgraph currently does not have (or really require) any installation scripts. The only requirement is that the pyqtgraph folder to be placed someplace importable. Most people will prefer to simply place pyqtgraph within a larger project folder. If you want to make pyqtgraph available system-wide, copy pyqtgraph to one of the directories listed in python's sys.path list.
|
Pyqtgraph does not really require any installation scripts. All that is needed is for the pyqtgraph folder to be placed someplace importable. Most people will prefer to simply place this folder within a larger project folder. If you want to make pyqtgraph available system-wide, use one of the methods listed below:
|
||||||
|
|
||||||
|
* **Debian, Ubuntu, and similar Linux:** Download the .deb file linked at the top of the pyqtgraph web page or install using apt by putting "deb http://luke.campagnola.me/debian dev/" in your /etc/apt/sources.list file and install the python-pyqtgraph package.
|
||||||
|
* **Arch Linux:** Looks like someone has posted unofficial packages for Arch (thanks windel). (https://aur.archlinux.org/packages.php?ID=62577)
|
||||||
|
* **Windows:** Download and run the .exe installer file linked at the top of the pyqtgraph web page.
|
||||||
|
* **Everybody (including OSX):** Download the .tar.gz source package linked at the top of the pyqtgraph web page, extract its contents, and run "python setup.py install" from within the extracted directory.
|
||||||
|
|
|
@ -26,13 +26,18 @@ win.show()
|
||||||
p = ui.plot
|
p = ui.plot
|
||||||
|
|
||||||
data = np.random.normal(size=(50,500), scale=100)
|
data = np.random.normal(size=(50,500), scale=100)
|
||||||
|
sizeArray = np.random.random(500) * 20.
|
||||||
ptr = 0
|
ptr = 0
|
||||||
lastTime = time()
|
lastTime = time()
|
||||||
fps = None
|
fps = None
|
||||||
def update():
|
def update():
|
||||||
global curve, data, ptr, p, lastTime, fps
|
global curve, data, ptr, p, lastTime, fps
|
||||||
p.clear()
|
p.clear()
|
||||||
curve = pg.ScatterPlotItem(x=data[ptr%10], y=data[(ptr+1)%10], pen='w', brush='b', size=10, pxMode=ui.pixelModeCheck.isChecked())
|
if ui.randCheck.isChecked():
|
||||||
|
size = sizeArray
|
||||||
|
else:
|
||||||
|
size = ui.sizeSpin.value()
|
||||||
|
curve = pg.ScatterPlotItem(x=data[ptr%50], y=data[(ptr+1)%50], pen='w', brush='b', size=size, pxMode=ui.pixelModeCheck.isChecked())
|
||||||
p.addItem(curve)
|
p.addItem(curve)
|
||||||
ptr += 1
|
ptr += 1
|
||||||
now = time()
|
now = time()
|
||||||
|
|
|
@ -14,23 +14,37 @@
|
||||||
<string>Form</string>
|
<string>Form</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="0" column="0" colspan="2">
|
<item row="1" column="1">
|
||||||
<widget class="PlotWidget" name="plot"/>
|
<widget class="QSpinBox" name="sizeSpin">
|
||||||
</item>
|
<property name="value">
|
||||||
<item row="1" column="0">
|
<number>10</number>
|
||||||
<widget class="QCheckBox" name="identicalCheck">
|
|
||||||
<property name="text">
|
|
||||||
<string>Identical</string>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="1" column="3">
|
||||||
<widget class="QCheckBox" name="pixelModeCheck">
|
<widget class="QCheckBox" name="pixelModeCheck">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>pixel mode</string>
|
<string>pixel mode</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Size</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" colspan="4">
|
||||||
|
<widget class="PlotWidget" name="plot"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QCheckBox" name="randCheck">
|
||||||
|
<property name="text">
|
||||||
|
<string>Randomize</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
# Form implementation generated from reading ui file './examples/ScatterPlotSpeedTestTemplate.ui'
|
# Form implementation generated from reading ui file './examples/ScatterPlotSpeedTestTemplate.ui'
|
||||||
#
|
#
|
||||||
# Created: Sun Sep 9 14:41:31 2012
|
# Created: Fri Sep 21 15:39:09 2012
|
||||||
# by: PyQt4 UI code generator 4.9.1
|
# by: PyQt4 UI code generator 4.9.1
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
@ -20,22 +20,30 @@ class Ui_Form(object):
|
||||||
Form.resize(400, 300)
|
Form.resize(400, 300)
|
||||||
self.gridLayout = QtGui.QGridLayout(Form)
|
self.gridLayout = QtGui.QGridLayout(Form)
|
||||||
self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
|
self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
|
||||||
self.plot = PlotWidget(Form)
|
self.sizeSpin = QtGui.QSpinBox(Form)
|
||||||
self.plot.setObjectName(_fromUtf8("plot"))
|
self.sizeSpin.setProperty("value", 10)
|
||||||
self.gridLayout.addWidget(self.plot, 0, 0, 1, 2)
|
self.sizeSpin.setObjectName(_fromUtf8("sizeSpin"))
|
||||||
self.identicalCheck = QtGui.QCheckBox(Form)
|
self.gridLayout.addWidget(self.sizeSpin, 1, 1, 1, 1)
|
||||||
self.identicalCheck.setObjectName(_fromUtf8("identicalCheck"))
|
|
||||||
self.gridLayout.addWidget(self.identicalCheck, 1, 0, 1, 1)
|
|
||||||
self.pixelModeCheck = QtGui.QCheckBox(Form)
|
self.pixelModeCheck = QtGui.QCheckBox(Form)
|
||||||
self.pixelModeCheck.setObjectName(_fromUtf8("pixelModeCheck"))
|
self.pixelModeCheck.setObjectName(_fromUtf8("pixelModeCheck"))
|
||||||
self.gridLayout.addWidget(self.pixelModeCheck, 1, 1, 1, 1)
|
self.gridLayout.addWidget(self.pixelModeCheck, 1, 3, 1, 1)
|
||||||
|
self.label = QtGui.QLabel(Form)
|
||||||
|
self.label.setObjectName(_fromUtf8("label"))
|
||||||
|
self.gridLayout.addWidget(self.label, 1, 0, 1, 1)
|
||||||
|
self.plot = PlotWidget(Form)
|
||||||
|
self.plot.setObjectName(_fromUtf8("plot"))
|
||||||
|
self.gridLayout.addWidget(self.plot, 0, 0, 1, 4)
|
||||||
|
self.randCheck = QtGui.QCheckBox(Form)
|
||||||
|
self.randCheck.setObjectName(_fromUtf8("randCheck"))
|
||||||
|
self.gridLayout.addWidget(self.randCheck, 1, 2, 1, 1)
|
||||||
|
|
||||||
self.retranslateUi(Form)
|
self.retranslateUi(Form)
|
||||||
QtCore.QMetaObject.connectSlotsByName(Form)
|
QtCore.QMetaObject.connectSlotsByName(Form)
|
||||||
|
|
||||||
def retranslateUi(self, Form):
|
def retranslateUi(self, Form):
|
||||||
Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8))
|
Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.identicalCheck.setText(QtGui.QApplication.translate("Form", "Identical", None, QtGui.QApplication.UnicodeUTF8))
|
|
||||||
self.pixelModeCheck.setText(QtGui.QApplication.translate("Form", "pixel mode", None, QtGui.QApplication.UnicodeUTF8))
|
self.pixelModeCheck.setText(QtGui.QApplication.translate("Form", "pixel mode", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.label.setText(QtGui.QApplication.translate("Form", "Size", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.randCheck.setText(QtGui.QApplication.translate("Form", "Randomize", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
|
||||||
from pyqtgraph import PlotWidget
|
from pyqtgraph import PlotWidget
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
# Form implementation generated from reading ui file './examples/ScatterPlotSpeedTestTemplate.ui'
|
# Form implementation generated from reading ui file './examples/ScatterPlotSpeedTestTemplate.ui'
|
||||||
#
|
#
|
||||||
# Created: Sun Sep 9 14:41:31 2012
|
# Created: Fri Sep 21 15:39:09 2012
|
||||||
# by: pyside-uic 0.2.13 running on PySide 1.1.0
|
# by: pyside-uic 0.2.13 running on PySide 1.1.0
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
@ -15,22 +15,30 @@ class Ui_Form(object):
|
||||||
Form.resize(400, 300)
|
Form.resize(400, 300)
|
||||||
self.gridLayout = QtGui.QGridLayout(Form)
|
self.gridLayout = QtGui.QGridLayout(Form)
|
||||||
self.gridLayout.setObjectName("gridLayout")
|
self.gridLayout.setObjectName("gridLayout")
|
||||||
self.plot = PlotWidget(Form)
|
self.sizeSpin = QtGui.QSpinBox(Form)
|
||||||
self.plot.setObjectName("plot")
|
self.sizeSpin.setProperty("value", 10)
|
||||||
self.gridLayout.addWidget(self.plot, 0, 0, 1, 2)
|
self.sizeSpin.setObjectName("sizeSpin")
|
||||||
self.identicalCheck = QtGui.QCheckBox(Form)
|
self.gridLayout.addWidget(self.sizeSpin, 1, 1, 1, 1)
|
||||||
self.identicalCheck.setObjectName("identicalCheck")
|
|
||||||
self.gridLayout.addWidget(self.identicalCheck, 1, 0, 1, 1)
|
|
||||||
self.pixelModeCheck = QtGui.QCheckBox(Form)
|
self.pixelModeCheck = QtGui.QCheckBox(Form)
|
||||||
self.pixelModeCheck.setObjectName("pixelModeCheck")
|
self.pixelModeCheck.setObjectName("pixelModeCheck")
|
||||||
self.gridLayout.addWidget(self.pixelModeCheck, 1, 1, 1, 1)
|
self.gridLayout.addWidget(self.pixelModeCheck, 1, 3, 1, 1)
|
||||||
|
self.label = QtGui.QLabel(Form)
|
||||||
|
self.label.setObjectName("label")
|
||||||
|
self.gridLayout.addWidget(self.label, 1, 0, 1, 1)
|
||||||
|
self.plot = PlotWidget(Form)
|
||||||
|
self.plot.setObjectName("plot")
|
||||||
|
self.gridLayout.addWidget(self.plot, 0, 0, 1, 4)
|
||||||
|
self.randCheck = QtGui.QCheckBox(Form)
|
||||||
|
self.randCheck.setObjectName("randCheck")
|
||||||
|
self.gridLayout.addWidget(self.randCheck, 1, 2, 1, 1)
|
||||||
|
|
||||||
self.retranslateUi(Form)
|
self.retranslateUi(Form)
|
||||||
QtCore.QMetaObject.connectSlotsByName(Form)
|
QtCore.QMetaObject.connectSlotsByName(Form)
|
||||||
|
|
||||||
def retranslateUi(self, Form):
|
def retranslateUi(self, Form):
|
||||||
Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8))
|
Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.identicalCheck.setText(QtGui.QApplication.translate("Form", "Identical", None, QtGui.QApplication.UnicodeUTF8))
|
|
||||||
self.pixelModeCheck.setText(QtGui.QApplication.translate("Form", "pixel mode", None, QtGui.QApplication.UnicodeUTF8))
|
self.pixelModeCheck.setText(QtGui.QApplication.translate("Form", "pixel mode", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.label.setText(QtGui.QApplication.translate("Form", "Size", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.randCheck.setText(QtGui.QApplication.translate("Form", "Randomize", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
|
||||||
from pyqtgraph import PlotWidget
|
from pyqtgraph import PlotWidget
|
||||||
|
|
|
@ -73,6 +73,29 @@ class Butterworth(CtrlNode):
|
||||||
ret = functions.butterworthFilter(data, bidir=s['bidir'], btype=mode, wPass=s['wPass'], wStop=s['wStop'], gPass=s['gPass'], gStop=s['gStop'])
|
ret = functions.butterworthFilter(data, bidir=s['bidir'], btype=mode, wPass=s['wPass'], wStop=s['wStop'], gPass=s['gPass'], gStop=s['gStop'])
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
class ButterworthNotch(CtrlNode):
|
||||||
|
"""Butterworth notch filter"""
|
||||||
|
nodeName = 'ButterworthNotchFilter'
|
||||||
|
uiTemplate = [
|
||||||
|
('low_wPass', 'spin', {'value': 1000., 'step': 1, 'dec': True, 'range': [0.0, None], 'suffix': 'Hz', 'siPrefix': True}),
|
||||||
|
('low_wStop', 'spin', {'value': 2000., 'step': 1, 'dec': True, 'range': [0.0, None], 'suffix': 'Hz', 'siPrefix': True}),
|
||||||
|
('low_gPass', 'spin', {'value': 2.0, 'step': 1, 'dec': True, 'range': [0.0, None], 'suffix': 'dB', 'siPrefix': True}),
|
||||||
|
('low_gStop', 'spin', {'value': 20.0, 'step': 1, 'dec': True, 'range': [0.0, None], 'suffix': 'dB', 'siPrefix': True}),
|
||||||
|
('high_wPass', 'spin', {'value': 3000., 'step': 1, 'dec': True, 'range': [0.0, None], 'suffix': 'Hz', 'siPrefix': True}),
|
||||||
|
('high_wStop', 'spin', {'value': 4000., 'step': 1, 'dec': True, 'range': [0.0, None], 'suffix': 'Hz', 'siPrefix': True}),
|
||||||
|
('high_gPass', 'spin', {'value': 2.0, 'step': 1, 'dec': True, 'range': [0.0, None], 'suffix': 'dB', 'siPrefix': True}),
|
||||||
|
('high_gStop', 'spin', {'value': 20.0, 'step': 1, 'dec': True, 'range': [0.0, None], 'suffix': 'dB', 'siPrefix': True}),
|
||||||
|
('bidir', 'check', {'checked': True})
|
||||||
|
]
|
||||||
|
|
||||||
|
def processData(self, data):
|
||||||
|
s = self.stateGroup.state()
|
||||||
|
|
||||||
|
low = functions.butterworthFilter(data, bidir=s['bidir'], btype='low', wPass=s['low_wPass'], wStop=s['low_wStop'], gPass=s['low_gPass'], gStop=s['low_gStop'])
|
||||||
|
high = functions.butterworthFilter(data, bidir=s['bidir'], btype='high', wPass=s['high_wPass'], wStop=s['high_wStop'], gPass=s['high_gPass'], gStop=s['high_gStop'])
|
||||||
|
return low + high
|
||||||
|
|
||||||
|
|
||||||
class Mean(CtrlNode):
|
class Mean(CtrlNode):
|
||||||
"""Filters data by taking the mean of a sliding window"""
|
"""Filters data by taking the mean of a sliding window"""
|
||||||
|
@ -204,6 +227,7 @@ class RemovePeriodic(CtrlNode):
|
||||||
#('numBins', 'intSpin', {'value': 50, 'min': 3, 'max': 1000000})
|
#('numBins', 'intSpin', {'value': 50, 'min': 3, 'max': 1000000})
|
||||||
('f0', 'spin', {'value': 60, 'suffix': 'Hz', 'siPrefix': True, 'min': 0, 'max': None}),
|
('f0', 'spin', {'value': 60, 'suffix': 'Hz', 'siPrefix': True, 'min': 0, 'max': None}),
|
||||||
('harmonics', 'intSpin', {'value': 30, 'min': 0}),
|
('harmonics', 'intSpin', {'value': 30, 'min': 0}),
|
||||||
|
('samples', 'intSpin', {'value': 1, 'min': 1}),
|
||||||
]
|
]
|
||||||
|
|
||||||
def processData(self, data):
|
def processData(self, data):
|
||||||
|
@ -224,22 +248,16 @@ class RemovePeriodic(CtrlNode):
|
||||||
|
|
||||||
## determine index range to check for this frequency
|
## determine index range to check for this frequency
|
||||||
ind1 = int(np.floor(f / df))
|
ind1 = int(np.floor(f / df))
|
||||||
ind2 = int(np.ceil(f / df))
|
ind2 = int(np.ceil(f / df)) + (self.ctrls['samples'].value()-1)
|
||||||
if ind1 > len(ft)/2.:
|
if ind1 > len(ft)/2.:
|
||||||
break
|
break
|
||||||
print "--->", f
|
|
||||||
print ind1, ind2, abs(ft[ind1-2:ind2+2])
|
|
||||||
print ft[ind1-2:ind2+2]
|
|
||||||
mag = (abs(ft[ind1-1]) + abs(ft[ind2+1])) * 0.5
|
mag = (abs(ft[ind1-1]) + abs(ft[ind2+1])) * 0.5
|
||||||
print "new mag:", mag
|
|
||||||
for j in range(ind1, ind2+1):
|
for j in range(ind1, ind2+1):
|
||||||
phase = np.angle(ft[j])
|
phase = np.angle(ft[j]) ## Must preserve the phase of each point, otherwise any transients in the trace might lead to large artifacts.
|
||||||
re = mag * np.cos(phase)
|
re = mag * np.cos(phase)
|
||||||
im = mag * np.sin(phase)
|
im = mag * np.sin(phase)
|
||||||
ft[j] = re + im*1j
|
ft[j] = re + im*1j
|
||||||
ft[len(ft)-j] = re - im*1j
|
ft[len(ft)-j] = re - im*1j
|
||||||
print abs(ft[ind1-2:ind2+2])
|
|
||||||
print ft[ind1-2:ind2+2]
|
|
||||||
|
|
||||||
data2 = np.fft.ifft(ft).real
|
data2 = np.fft.ifft(ft).real
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ import decimal, re
|
||||||
try:
|
try:
|
||||||
import scipy.weave
|
import scipy.weave
|
||||||
USE_WEAVE = True
|
USE_WEAVE = True
|
||||||
except ImportError:
|
except:
|
||||||
USE_WEAVE = False
|
USE_WEAVE = False
|
||||||
|
|
||||||
from . import debug
|
from . import debug
|
||||||
|
@ -715,7 +715,7 @@ def makeQImage(imgData, alpha):
|
||||||
imgData = imgData.transpose((1, 0, 2)) ## QImage expects the row/column order to be opposite
|
imgData = imgData.transpose((1, 0, 2)) ## QImage expects the row/column order to be opposite
|
||||||
try:
|
try:
|
||||||
buf = imgData.data
|
buf = imgData.data
|
||||||
except AttributeError:
|
except AttributeError: ## happens when image data is non-contiguous
|
||||||
imgData = np.ascontiguousarray(imgData)
|
imgData = np.ascontiguousarray(imgData)
|
||||||
buf = imgData.data
|
buf = imgData.data
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,10 @@ class GradientLegend(UIGraphicsItem):
|
||||||
points along the gradient.
|
points along the gradient.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, view, size, offset):
|
def __init__(self, size, offset):
|
||||||
self.size = size
|
self.size = size
|
||||||
self.offset = offset
|
self.offset = offset
|
||||||
UIGraphicsItem.__init__(self, view)
|
UIGraphicsItem.__init__(self)
|
||||||
self.setAcceptedMouseButtons(QtCore.Qt.NoButton)
|
self.setAcceptedMouseButtons(QtCore.Qt.NoButton)
|
||||||
self.brush = QtGui.QBrush(QtGui.QColor(200,0,0))
|
self.brush = QtGui.QBrush(QtGui.QColor(200,0,0))
|
||||||
self.pen = QtGui.QPen(QtGui.QColor(0,0,0))
|
self.pen = QtGui.QPen(QtGui.QColor(0,0,0))
|
||||||
|
|
|
@ -267,7 +267,7 @@ class PlotCurveItem(GraphicsObject):
|
||||||
## 0(i4)
|
## 0(i4)
|
||||||
##
|
##
|
||||||
## All values are big endian--pack using struct.pack('>d') or struct.pack('>i')
|
## All values are big endian--pack using struct.pack('>d') or struct.pack('>i')
|
||||||
if sys.version_info.major == 2:
|
if sys.version_info[0] == 2: ## So this is disabled for python 3... why??
|
||||||
n = x.shape[0]
|
n = x.shape[0]
|
||||||
# create empty array, pad with extra space on either end
|
# create empty array, pad with extra space on either end
|
||||||
arr = np.empty(n+2, dtype=[('x', '>f8'), ('y', '>f8'), ('c', '>i4')])
|
arr = np.empty(n+2, dtype=[('x', '>f8'), ('y', '>f8'), ('c', '>i4')])
|
||||||
|
|
|
@ -57,36 +57,36 @@ class PlotDataItem(GraphicsObject):
|
||||||
|
|
||||||
**Line style keyword arguments:**
|
**Line style keyword arguments:**
|
||||||
========== ================================================
|
========== ================================================
|
||||||
pen pen to use for drawing line between points.
|
pen Pen to use for drawing line between points.
|
||||||
Default is solid grey, 1px width. Use None to disable line drawing.
|
Default is solid grey, 1px width. Use None to disable line drawing.
|
||||||
May be any single argument accepted by :func:`mkPen() <pyqtgraph.mkPen>`
|
May be any single argument accepted by :func:`mkPen() <pyqtgraph.mkPen>`
|
||||||
shadowPen pen for secondary line to draw behind the primary line. disabled by default.
|
shadowPen Pen for secondary line to draw behind the primary line. disabled by default.
|
||||||
May be any single argument accepted by :func:`mkPen() <pyqtgraph.mkPen>`
|
May be any single argument accepted by :func:`mkPen() <pyqtgraph.mkPen>`
|
||||||
fillLevel fill the area between the curve and fillLevel
|
fillLevel Fill the area between the curve and fillLevel
|
||||||
fillBrush fill to use when fillLevel is specified
|
fillBrush Fill to use when fillLevel is specified.
|
||||||
May be any single argument accepted by :func:`mkBrush() <pyqtgraph.mkBrush>`
|
May be any single argument accepted by :func:`mkBrush() <pyqtgraph.mkBrush>`
|
||||||
========== ================================================
|
========== ================================================
|
||||||
|
|
||||||
**Point style keyword arguments:**
|
**Point style keyword arguments:** (see :func:`ScatterPlotItem.setData() <pyqtgraph.ScatterPlotItem.setData>` for more information)
|
||||||
|
|
||||||
============ ================================================
|
============ ================================================
|
||||||
symbol (str) symbol to use for drawing points OR list of symbols, one per point. Default is no symbol.
|
symbol Symbol to use for drawing points OR list of symbols, one per point. Default is no symbol.
|
||||||
options are o, s, t, d, +
|
Options are o, s, t, d, +, or any QPainterPath
|
||||||
symbolPen outline pen for drawing points OR list of pens, one per point
|
symbolPen Outline pen for drawing points OR list of pens, one per point.
|
||||||
May be any single argument accepted by :func:`mkPen() <pyqtgraph.mkPen>`
|
May be any single argument accepted by :func:`mkPen() <pyqtgraph.mkPen>`
|
||||||
symbolBrush brush for filling points OR list of brushes, one per point
|
symbolBrush Brush for filling points OR list of brushes, one per point.
|
||||||
May be any single argument accepted by :func:`mkBrush() <pyqtgraph.mkBrush>`
|
May be any single argument accepted by :func:`mkBrush() <pyqtgraph.mkBrush>`
|
||||||
symbolSize diameter of symbols OR list of diameters
|
symbolSize Diameter of symbols OR list of diameters.
|
||||||
pxMode (bool) If True, then symbolSize is specified in pixels. If False, then symbolSize is
|
pxMode (bool) If True, then symbolSize is specified in pixels. If False, then symbolSize is
|
||||||
specified in data coordinates.
|
specified in data coordinates.
|
||||||
============ ================================================
|
============ ================================================
|
||||||
|
|
||||||
**Optimization keyword arguments:**
|
**Optimization keyword arguments:**
|
||||||
|
|
||||||
========== ================================================
|
========== =====================================================================
|
||||||
identical *deprecated*
|
identical *deprecated*
|
||||||
decimate (int) decimate data
|
decimate (int) sub-sample data by selecting every nth sample before plotting
|
||||||
========== ================================================
|
========== =====================================================================
|
||||||
|
|
||||||
**Meta-info keyword arguments:**
|
**Meta-info keyword arguments:**
|
||||||
|
|
||||||
|
@ -239,7 +239,6 @@ class PlotDataItem(GraphicsObject):
|
||||||
Clear any data displayed by this item and display new data.
|
Clear any data displayed by this item and display new data.
|
||||||
See :func:`__init__() <pyqtgraph.PlotDataItem.__init__>` for details; it accepts the same arguments.
|
See :func:`__init__() <pyqtgraph.PlotDataItem.__init__>` for details; it accepts the same arguments.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#self.clear()
|
#self.clear()
|
||||||
prof = debug.Profiler('PlotDataItem.setData (0x%x)' % id(self), disabled=True)
|
prof = debug.Profiler('PlotDataItem.setData (0x%x)' % id(self), disabled=True)
|
||||||
y = None
|
y = None
|
||||||
|
@ -265,7 +264,8 @@ class PlotDataItem(GraphicsObject):
|
||||||
if 'y' in data[0]:
|
if 'y' in data[0]:
|
||||||
y = np.array([d.get('y',None) for d in data])
|
y = np.array([d.get('y',None) for d in data])
|
||||||
for k in ['data', 'symbolSize', 'symbolPen', 'symbolBrush', 'symbolShape']:
|
for k in ['data', 'symbolSize', 'symbolPen', 'symbolBrush', 'symbolShape']:
|
||||||
kargs[k] = [d.get(k, None) for d in data]
|
if k in data:
|
||||||
|
kargs[k] = [d.get(k, None) for d in data]
|
||||||
elif dt == 'MetaArray':
|
elif dt == 'MetaArray':
|
||||||
y = data.view(np.ndarray)
|
y = data.view(np.ndarray)
|
||||||
x = data.xvals(0).view(np.ndarray)
|
x = data.xvals(0).view(np.ndarray)
|
||||||
|
@ -296,6 +296,7 @@ class PlotDataItem(GraphicsObject):
|
||||||
|
|
||||||
|
|
||||||
## if symbol pen/brush are given with no symbol, then assume symbol is 'o'
|
## if symbol pen/brush are given with no symbol, then assume symbol is 'o'
|
||||||
|
|
||||||
if 'symbol' not in kargs and ('symbolPen' in kargs or 'symbolBrush' in kargs or 'symbolSize' in kargs):
|
if 'symbol' not in kargs and ('symbolPen' in kargs or 'symbolBrush' in kargs or 'symbolSize' in kargs):
|
||||||
kargs['symbol'] = 'o'
|
kargs['symbol'] = 'o'
|
||||||
|
|
||||||
|
@ -502,11 +503,11 @@ def dataType(obj):
|
||||||
else:
|
else:
|
||||||
raise Exception('array shape must be (N,) or (N,2); got %s instead' % str(obj.shape))
|
raise Exception('array shape must be (N,) or (N,2); got %s instead' % str(obj.shape))
|
||||||
elif isinstance(first, dict):
|
elif isinstance(first, dict):
|
||||||
return 'listOfDict'
|
return 'listOfDicts'
|
||||||
else:
|
else:
|
||||||
return 'listOfValues'
|
return 'listOfValues'
|
||||||
elif isinstance(obj, dict):
|
elif isinstance(obj, dict):
|
||||||
return 'dict'
|
return 'dictOfLists'
|
||||||
|
|
||||||
|
|
||||||
def isSequence(obj):
|
def isSequence(obj):
|
||||||
|
|
|
@ -44,7 +44,9 @@ def makeSymbolPixmap(size, pen, brush, symbol):
|
||||||
p.scale(size, size)
|
p.scale(size, size)
|
||||||
p.setPen(pen)
|
p.setPen(pen)
|
||||||
p.setBrush(brush)
|
p.setBrush(brush)
|
||||||
p.drawPath(Symbols[symbol])
|
if isinstance(symbol, basestring):
|
||||||
|
symbol = Symbols[symbol]
|
||||||
|
p.drawPath(symbol)
|
||||||
p.end()
|
p.end()
|
||||||
return QtGui.QPixmap(image)
|
return QtGui.QPixmap(image)
|
||||||
|
|
||||||
|
@ -112,12 +114,14 @@ class ScatterPlotItem(GraphicsObject):
|
||||||
Otherwise, size is in scene coordinates and the spots scale with the view.
|
Otherwise, size is in scene coordinates and the spots scale with the view.
|
||||||
Default is True
|
Default is True
|
||||||
*symbol* can be one (or a list) of:
|
*symbol* can be one (or a list) of:
|
||||||
|
|
||||||
* 'o' circle (default)
|
* 'o' circle (default)
|
||||||
* 's' square
|
* 's' square
|
||||||
* 't' triangle
|
* 't' triangle
|
||||||
* 'd' diamond
|
* 'd' diamond
|
||||||
* '+' plus
|
* '+' plus
|
||||||
|
* any QPainterPath to specify custom symbol shapes. To properly obey the position and size,
|
||||||
|
custom symbols should be centered at (0,0) and width and height of 1.0. Note that it is also
|
||||||
|
possible to 'install' custom shapes by setting ScatterPlotItem.Symbols[key] = shape.
|
||||||
*pen* The pen (or list of pens) to use for drawing spot outlines.
|
*pen* The pen (or list of pens) to use for drawing spot outlines.
|
||||||
*brush* The brush (or list of brushes) to use for filling spots.
|
*brush* The brush (or list of brushes) to use for filling spots.
|
||||||
*size* The size (or list of sizes) of spots. If *pxMode* is True, this value is in pixels. Otherwise,
|
*size* The size (or list of sizes) of spots. If *pxMode* is True, this value is in pixels. Otherwise,
|
||||||
|
|
|
@ -163,7 +163,8 @@ class ViewBox(GraphicsWidget):
|
||||||
if name is not None:
|
if name is not None:
|
||||||
ViewBox.NamedViews[name] = self
|
ViewBox.NamedViews[name] = self
|
||||||
ViewBox.updateAllViewLists()
|
ViewBox.updateAllViewLists()
|
||||||
self.destroyed.connect(lambda: ViewBox.forgetView(id(self), self.name))
|
sid = id(self)
|
||||||
|
self.destroyed.connect(lambda: ViewBox.forgetView(sid, name))
|
||||||
#self.destroyed.connect(self.unregister)
|
#self.destroyed.connect(self.unregister)
|
||||||
|
|
||||||
def unregister(self):
|
def unregister(self):
|
||||||
|
@ -1173,6 +1174,11 @@ class ViewBox(GraphicsWidget):
|
||||||
|
|
||||||
|
|
||||||
def updateViewLists(self):
|
def updateViewLists(self):
|
||||||
|
try:
|
||||||
|
self.window()
|
||||||
|
except RuntimeError: ## this view has already been deleted; it will probably be collected shortly.
|
||||||
|
return
|
||||||
|
|
||||||
def cmpViews(a, b):
|
def cmpViews(a, b):
|
||||||
wins = 100 * cmp(a.window() is self.window(), b.window() is self.window())
|
wins = 100 * cmp(a.window() is self.window(), b.window() is self.window())
|
||||||
alpha = cmp(a.name, b.name)
|
alpha = cmp(a.name, b.name)
|
||||||
|
|
22
numpy_fix.py
Normal file
22
numpy_fix.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
try:
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
## Wrap np.concatenate to catch and avoid a segmentation fault bug
|
||||||
|
## (numpy trac issue #2084)
|
||||||
|
if not hasattr(np, 'concatenate_orig'):
|
||||||
|
np.concatenate_orig = np.concatenate
|
||||||
|
def concatenate(vals, **kwds):
|
||||||
|
"""Wrapper around numpy.concatenate (see pyqtgraph/numpy_fix.py)"""
|
||||||
|
dtypes = [getattr(v, 'dtype', None) for v in vals]
|
||||||
|
names = [getattr(dt, 'names', None) for dt in dtypes]
|
||||||
|
if len(dtypes) < 2 or all([n is None for n in names]):
|
||||||
|
return np.concatenate_orig(vals, **kwds)
|
||||||
|
if any([dt != dtypes[0] for dt in dtypes[1:]]):
|
||||||
|
raise TypeError("Cannot concatenate structured arrays of different dtype.")
|
||||||
|
return np.concatenate_orig(vals, **kwds)
|
||||||
|
|
||||||
|
np.concatenate = concatenate
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
10
rebuildUi.py
10
rebuildUi.py
|
@ -13,9 +13,11 @@ for path, sd, files in os.walk('.'):
|
||||||
ui = os.path.join(path, f)
|
ui = os.path.join(path, f)
|
||||||
|
|
||||||
py = os.path.join(path, base + '_pyqt.py')
|
py = os.path.join(path, base + '_pyqt.py')
|
||||||
os.system('%s %s > %s' % (pyqtuic, ui, py))
|
if os.stat(ui).st_mtime > os.stat(py).st_mtime:
|
||||||
print(py)
|
os.system('%s %s > %s' % (pyqtuic, ui, py))
|
||||||
|
print(py)
|
||||||
|
|
||||||
py = os.path.join(path, base + '_pyside.py')
|
py = os.path.join(path, base + '_pyside.py')
|
||||||
os.system('%s %s > %s' % (pysideuic, ui, py))
|
if os.stat(ui).st_mtime > os.stat(py).st_mtime:
|
||||||
print(py)
|
os.system('%s %s > %s' % (pysideuic, ui, py))
|
||||||
|
print(py)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user