copy from acq4
This commit is contained in:
parent
008ca76d53
commit
8c13a3e7e3
@ -80,6 +80,12 @@ class Point(QtCore.QPointF):
|
||||
def __div__(self, a):
|
||||
return self._math_('__div__', a)
|
||||
|
||||
def __truediv__(self, a):
|
||||
return self._math_('__truediv__', a)
|
||||
|
||||
def __rtruediv__(self, a):
|
||||
return self._math_('__rtruediv__', a)
|
||||
|
||||
def __rpow__(self, a):
|
||||
return self._math_('__rpow__', a)
|
||||
|
||||
|
@ -130,11 +130,14 @@ class SRTTransform(QtGui.QTransform):
|
||||
self._state['angle'] = angle
|
||||
self.update()
|
||||
|
||||
def __div__(self, t):
|
||||
def __truediv__(self, t):
|
||||
"""A / B == B^-1 * A"""
|
||||
dt = t.inverted()[0] * self
|
||||
return SRTTransform(dt)
|
||||
|
||||
def __div__(self, t):
|
||||
return self.__truediv__(t)
|
||||
|
||||
def __mul__(self, t):
|
||||
return SRTTransform(QtGui.QTransform.__mul__(self, t))
|
||||
|
||||
|
@ -123,7 +123,6 @@ class SRTTransform3D(pg.Transform3D):
|
||||
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
|
||||
@ -141,18 +140,30 @@ class SRTTransform3D(pg.Transform3D):
|
||||
print("Scale: %s" % str(scale))
|
||||
print("Original matrix: %s" % str(m))
|
||||
raise
|
||||
eigIndex = np.argwhere(np.abs(evals-1) < 1e-7)
|
||||
eigIndex = np.argwhere(np.abs(evals-1) < 1e-6)
|
||||
if len(eigIndex) < 1:
|
||||
print("eigenvalues: %s" % str(evals))
|
||||
print("eigenvectors: %s" % str(evecs))
|
||||
print("index: %s, %s" % (str(eigIndex), str(evals-1)))
|
||||
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
|
||||
self._state['axis'] = axis
|
||||
|
||||
## trace(r) == 2 cos(angle) + 1, so:
|
||||
self._state['angle'] = np.arccos((r.trace()-1)*0.5) * 180 / np.pi
|
||||
cos = (r.trace()-1)*0.5 ## this only gets us abs(angle)
|
||||
|
||||
## The off-diagonal values can be used to correct the angle ambiguity,
|
||||
## but we need to figure out which element to use:
|
||||
axisInd = np.argmax(np.abs(axis))
|
||||
rInd,sign = [((1,2), -1), ((0,2), 1), ((0,1), -1)][axisInd]
|
||||
|
||||
## Then we have r-r.T = sin(angle) * 2 * sign * axis[axisInd];
|
||||
## solve for sin(angle)
|
||||
sin = (r-r.T)[rInd] / (2. * sign * axis[axisInd])
|
||||
|
||||
## finally, we get the complete angle from arctan(sin/cos)
|
||||
self._state['angle'] = np.arctan2(sin, cos) * 180 / np.pi
|
||||
if self._state['angle'] == 0:
|
||||
self._state['axis'] = (0,0,1)
|
||||
|
||||
|
@ -28,6 +28,15 @@ def ftrace(func):
|
||||
return rv
|
||||
return w
|
||||
|
||||
def warnOnException(func):
|
||||
"""Decorator which catches/ignores exceptions and prints a stack trace."""
|
||||
def w(*args, **kwds):
|
||||
try:
|
||||
func(*args, **kwds)
|
||||
except:
|
||||
printExc('Ignored exception:')
|
||||
return w
|
||||
|
||||
def getExc(indent=4, prefix='| '):
|
||||
tb = traceback.format_exc()
|
||||
lines = []
|
||||
|
@ -24,7 +24,15 @@ class BinOpNode(Node):
|
||||
})
|
||||
|
||||
def process(self, **args):
|
||||
fn = getattr(args['A'], self.fn)
|
||||
if isinstance(self.fn, tuple):
|
||||
for name in self.fn:
|
||||
try:
|
||||
fn = getattr(args['A'], name)
|
||||
break
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
fn = getattr(args['A'], self.fn)
|
||||
out = fn(args['B'])
|
||||
if out is NotImplemented:
|
||||
raise Exception("Operation %s not implemented between %s and %s" % (fn, str(type(args['A'])), str(type(args['B']))))
|
||||
@ -60,5 +68,7 @@ class DivideNode(BinOpNode):
|
||||
"""Returns A / B. Does not check input types."""
|
||||
nodeName = 'Divide'
|
||||
def __init__(self, name):
|
||||
BinOpNode.__init__(self, name, '__div__')
|
||||
# try truediv first, followed by div
|
||||
BinOpNode.__init__(self, name, ('__truediv__', '__div__'))
|
||||
|
||||
|
||||
|
@ -264,6 +264,7 @@ def mkPen(*args, **kargs):
|
||||
color = kargs.get('color', None)
|
||||
width = kargs.get('width', 1)
|
||||
style = kargs.get('style', None)
|
||||
dash = kargs.get('dash', None)
|
||||
cosmetic = kargs.get('cosmetic', True)
|
||||
hsv = kargs.get('hsv', None)
|
||||
|
||||
@ -291,6 +292,8 @@ def mkPen(*args, **kargs):
|
||||
pen.setCosmetic(cosmetic)
|
||||
if style is not None:
|
||||
pen.setStyle(style)
|
||||
if dash is not None:
|
||||
pen.setDashPattern(dash)
|
||||
return pen
|
||||
|
||||
def hsvColor(hue, sat=1.0, val=1.0, alpha=1.0):
|
||||
@ -1948,6 +1951,8 @@ def pseudoScatter(data, spacing=None, shuffle=True, bidir=False):
|
||||
s2 = spacing**2
|
||||
|
||||
yvals = np.empty(len(data))
|
||||
if len(data) == 0:
|
||||
return yvals
|
||||
yvals[0] = 0
|
||||
for i in range(1,len(data)):
|
||||
x = data[i] # current x value to be placed
|
||||
|
@ -42,12 +42,18 @@ class AxisItem(GraphicsWidget):
|
||||
self.label.rotate(-90)
|
||||
|
||||
self.style = {
|
||||
'tickTextOffset': 3, ## spacing between text and axis
|
||||
'tickTextOffset': (5, 2), ## (horizontal, vertical) spacing between text and axis
|
||||
'tickTextWidth': 30, ## space reserved for tick text
|
||||
'tickTextHeight': 18,
|
||||
'autoExpandTextSpace': True, ## automatically expand text space if needed
|
||||
'tickFont': None,
|
||||
'stopAxisAtTick': (False, False), ## whether axis is drawn to edge of box or to last tick
|
||||
'textFillLimits': [ ## how much of the axis to fill up with tick text, maximally.
|
||||
(0, 0.8), ## never fill more than 80% of the axis
|
||||
(2, 0.6), ## If we already have 2 ticks with text, fill no more than 60% of the axis
|
||||
(4, 0.4), ## If we already have 4 ticks with text, fill no more than 40% of the axis
|
||||
(6, 0.2), ## If we already have 6 ticks with text, fill no more than 20% of the axis
|
||||
]
|
||||
}
|
||||
|
||||
self.textWidth = 30 ## Keeps track of maximum width / height of tick text
|
||||
@ -209,14 +215,14 @@ class AxisItem(GraphicsWidget):
|
||||
## to accomodate.
|
||||
if self.orientation in ['left', 'right']:
|
||||
mx = max(self.textWidth, x)
|
||||
if mx > self.textWidth:
|
||||
if mx > self.textWidth or mx < self.textWidth-10:
|
||||
self.textWidth = mx
|
||||
if self.style['autoExpandTextSpace'] is True:
|
||||
self.setWidth()
|
||||
#return True ## size has changed
|
||||
else:
|
||||
mx = max(self.textHeight, x)
|
||||
if mx > self.textHeight:
|
||||
if mx > self.textHeight or mx < self.textHeight-10:
|
||||
self.textHeight = mx
|
||||
if self.style['autoExpandTextSpace'] is True:
|
||||
self.setHeight()
|
||||
@ -236,7 +242,7 @@ class AxisItem(GraphicsWidget):
|
||||
h = self.textHeight
|
||||
else:
|
||||
h = self.style['tickTextHeight']
|
||||
h += max(0, self.tickLength) + self.style['tickTextOffset']
|
||||
h += max(0, self.tickLength) + self.style['tickTextOffset'][1]
|
||||
if self.label.isVisible():
|
||||
h += self.label.boundingRect().height() * 0.8
|
||||
self.setMaximumHeight(h)
|
||||
@ -252,7 +258,7 @@ class AxisItem(GraphicsWidget):
|
||||
w = self.textWidth
|
||||
else:
|
||||
w = self.style['tickTextWidth']
|
||||
w += max(0, self.tickLength) + self.style['tickTextOffset']
|
||||
w += max(0, self.tickLength) + self.style['tickTextOffset'][0]
|
||||
if self.label.isVisible():
|
||||
w += self.label.boundingRect().height() * 0.8 ## bounding rect is usually an overestimate
|
||||
self.setMaximumWidth(w)
|
||||
@ -430,7 +436,7 @@ class AxisItem(GraphicsWidget):
|
||||
return []
|
||||
|
||||
## decide optimal minor tick spacing in pixels (this is just aesthetics)
|
||||
pixelSpacing = np.log(size+10) * 5
|
||||
pixelSpacing = size / np.log(size)
|
||||
optimalTickCount = max(2., size / pixelSpacing)
|
||||
|
||||
## optimal minor tick spacing
|
||||
@ -720,7 +726,7 @@ class AxisItem(GraphicsWidget):
|
||||
|
||||
|
||||
|
||||
textOffset = self.style['tickTextOffset'] ## spacing between axis and text
|
||||
textOffset = self.style['tickTextOffset'][axis] ## spacing between axis and text
|
||||
#if self.style['autoExpandTextSpace'] is True:
|
||||
#textWidth = self.textWidth
|
||||
#textHeight = self.textHeight
|
||||
@ -728,7 +734,7 @@ class AxisItem(GraphicsWidget):
|
||||
#textWidth = self.style['tickTextWidth'] ## space allocated for horizontal text
|
||||
#textHeight = self.style['tickTextHeight'] ## space allocated for horizontal text
|
||||
|
||||
|
||||
textSize2 = 0
|
||||
textRects = []
|
||||
textSpecs = [] ## list of draw
|
||||
for i in range(len(tickLevels)):
|
||||
@ -770,9 +776,16 @@ class AxisItem(GraphicsWidget):
|
||||
textSize = np.sum([r.width() for r in textRects])
|
||||
textSize2 = np.max([r.height() for r in textRects])
|
||||
|
||||
## If the strings are too crowded, stop drawing text now
|
||||
## If the strings are too crowded, stop drawing text now.
|
||||
## We use three different crowding limits based on the number
|
||||
## of texts drawn so far.
|
||||
textFillRatio = float(textSize) / lengthInPixels
|
||||
if textFillRatio > 0.7:
|
||||
finished = False
|
||||
for nTexts, limit in self.style['textFillLimits']:
|
||||
if len(textSpecs) >= nTexts and textFillRatio >= limit:
|
||||
finished = True
|
||||
break
|
||||
if finished:
|
||||
break
|
||||
|
||||
#spacing, values = tickLevels[best]
|
||||
|
@ -533,6 +533,7 @@ class GraphicsItem(object):
|
||||
def viewTransformChanged(self):
|
||||
"""
|
||||
Called whenever the transformation matrix of the view has changed.
|
||||
(eg, the view range has changed or the view was resized)
|
||||
"""
|
||||
pass
|
||||
|
||||
|
@ -375,6 +375,7 @@ class PlotCurveItem(GraphicsObject):
|
||||
return QtGui.QPainterPath()
|
||||
return self.path
|
||||
|
||||
@pg.debug.warnOnException ## raising an exception here causes crash
|
||||
def paint(self, p, opt, widget):
|
||||
prof = debug.Profiler('PlotCurveItem.paint '+str(id(self)), disabled=True)
|
||||
if self.xData is None:
|
||||
|
@ -84,24 +84,28 @@ class PlotDataItem(GraphicsObject):
|
||||
|
||||
**Optimization keyword arguments:**
|
||||
|
||||
============ =====================================================================
|
||||
antialias (bool) By default, antialiasing is disabled to improve performance.
|
||||
Note that in some cases (in particluar, when pxMode=True), points
|
||||
will be rendered antialiased even if this is set to False.
|
||||
decimate (int) Sub-sample data by selecting every nth sample before plotting
|
||||
onlyVisible (bool) If True, only plot data that is visible within the X range of
|
||||
the containing ViewBox. This can improve performance when plotting
|
||||
very large data sets where only a fraction of the data is visible
|
||||
at any time.
|
||||
autoResample (bool) If True, resample the data before plotting to avoid plotting
|
||||
multiple line segments per pixel. This can improve performance when
|
||||
viewing very high-density data, but increases the initial overhead
|
||||
and memory usage.
|
||||
sampleRate (float) The sample rate of the data along the X axis (for data with
|
||||
a fixed sample rate). Providing this value improves performance of
|
||||
the *onlyVisible* and *autoResample* options.
|
||||
identical *deprecated*
|
||||
============ =====================================================================
|
||||
================ =====================================================================
|
||||
antialias (bool) By default, antialiasing is disabled to improve performance.
|
||||
Note that in some cases (in particluar, when pxMode=True), points
|
||||
will be rendered antialiased even if this is set to False.
|
||||
decimate deprecated.
|
||||
downsample (int) Reduce the number of samples displayed by this value
|
||||
downsampleMethod 'subsample': Downsample by taking the first of N samples.
|
||||
This method is fastest and least accurate.
|
||||
'mean': Downsample by taking the mean of N samples.
|
||||
'peak': Downsample by drawing a saw wave that follows the min
|
||||
and max of the original data. This method produces the best
|
||||
visual representation of the data but is slower.
|
||||
autoDownsample (bool) If True, resample the data before plotting to avoid plotting
|
||||
multiple line segments per pixel. This can improve performance when
|
||||
viewing very high-density data, but increases the initial overhead
|
||||
and memory usage.
|
||||
clipToView (bool) If True, only plot data that is visible within the X range of
|
||||
the containing ViewBox. This can improve performance when plotting
|
||||
very large data sets where only a fraction of the data is visible
|
||||
at any time.
|
||||
identical *deprecated*
|
||||
================ =====================================================================
|
||||
|
||||
**Meta-info keyword arguments:**
|
||||
|
||||
@ -131,7 +135,6 @@ class PlotDataItem(GraphicsObject):
|
||||
self.opts = {
|
||||
'fftMode': False,
|
||||
'logMode': [False, False],
|
||||
'downsample': False,
|
||||
'alphaHint': 1.0,
|
||||
'alphaMode': False,
|
||||
|
||||
@ -149,6 +152,11 @@ class PlotDataItem(GraphicsObject):
|
||||
'antialias': pg.getConfigOption('antialias'),
|
||||
'pointMode': None,
|
||||
|
||||
'downsample': 1,
|
||||
'autoDownsample': False,
|
||||
'downsampleMethod': 'peak',
|
||||
'clipToView': False,
|
||||
|
||||
'data': None,
|
||||
}
|
||||
self.setData(*args, **kargs)
|
||||
@ -175,6 +183,7 @@ class PlotDataItem(GraphicsObject):
|
||||
return
|
||||
self.opts['fftMode'] = mode
|
||||
self.xDisp = self.yDisp = None
|
||||
self.xClean = self.yClean = None
|
||||
self.updateItems()
|
||||
self.informViewBoundsChanged()
|
||||
|
||||
@ -183,6 +192,7 @@ class PlotDataItem(GraphicsObject):
|
||||
return
|
||||
self.opts['logMode'] = [xMode, yMode]
|
||||
self.xDisp = self.yDisp = None
|
||||
self.xClean = self.yClean = None
|
||||
self.updateItems()
|
||||
self.informViewBoundsChanged()
|
||||
|
||||
@ -269,13 +279,51 @@ class PlotDataItem(GraphicsObject):
|
||||
#self.scatter.setSymbolSize(symbolSize)
|
||||
self.updateItems()
|
||||
|
||||
def setDownsampling(self, ds):
|
||||
if self.opts['downsample'] == ds:
|
||||
def setDownsampling(self, ds=None, auto=None, method=None):
|
||||
"""
|
||||
Set the downsampling mode of this item. Downsampling reduces the number
|
||||
of samples drawn to increase performance.
|
||||
|
||||
=========== =================================================================
|
||||
Arguments
|
||||
ds (int) Reduce visible plot samples by this factor. To disable,
|
||||
set ds=1.
|
||||
auto (bool) If True, automatically pick *ds* based on visible range
|
||||
mode 'subsample': Downsample by taking the first of N samples.
|
||||
This method is fastest and least accurate.
|
||||
'mean': Downsample by taking the mean of N samples.
|
||||
'peak': Downsample by drawing a saw wave that follows the min
|
||||
and max of the original data. This method produces the best
|
||||
visual representation of the data but is slower.
|
||||
=========== =================================================================
|
||||
"""
|
||||
changed = False
|
||||
if ds is not None:
|
||||
if self.opts['downsample'] != ds:
|
||||
changed = True
|
||||
self.opts['downsample'] = ds
|
||||
|
||||
if auto is not None and self.opts['autoDownsample'] != auto:
|
||||
self.opts['autoDownsample'] = auto
|
||||
changed = True
|
||||
|
||||
if method is not None:
|
||||
if self.opts['downsampleMethod'] != method:
|
||||
changed = True
|
||||
self.opts['downsampleMethod'] = method
|
||||
|
||||
if changed:
|
||||
self.xDisp = self.yDisp = None
|
||||
self.updateItems()
|
||||
|
||||
def setClipToView(self, clip):
|
||||
if self.opts['clipToView'] == clip:
|
||||
return
|
||||
self.opts['downsample'] = ds
|
||||
self.opts['clipToView'] = clip
|
||||
self.xDisp = self.yDisp = None
|
||||
self.updateItems()
|
||||
|
||||
|
||||
def setData(self, *args, **kargs):
|
||||
"""
|
||||
Clear any data displayed by this item and display new data.
|
||||
@ -315,7 +363,7 @@ class PlotDataItem(GraphicsObject):
|
||||
raise Exception('Invalid data type %s' % type(data))
|
||||
|
||||
elif len(args) == 2:
|
||||
seq = ('listOfValues', 'MetaArray')
|
||||
seq = ('listOfValues', 'MetaArray', 'empty')
|
||||
if dataType(args[0]) not in seq or dataType(args[1]) not in seq:
|
||||
raise Exception('When passing two unnamed arguments, both must be a list or array of values. (got %s, %s)' % (str(type(args[0])), str(type(args[1]))))
|
||||
if not isinstance(args[0], np.ndarray):
|
||||
@ -376,6 +424,7 @@ class PlotDataItem(GraphicsObject):
|
||||
|
||||
self.xData = x.view(np.ndarray) ## one last check to make sure there are no MetaArrays getting by
|
||||
self.yData = y.view(np.ndarray)
|
||||
self.xClean = self.yClean = None
|
||||
self.xDisp = None
|
||||
self.yDisp = None
|
||||
prof.mark('set data')
|
||||
@ -423,23 +472,28 @@ class PlotDataItem(GraphicsObject):
|
||||
def getData(self):
|
||||
if self.xData is None:
|
||||
return (None, None)
|
||||
if self.xDisp is None:
|
||||
|
||||
if self.xClean is None:
|
||||
nanMask = np.isnan(self.xData) | np.isnan(self.yData) | np.isinf(self.xData) | np.isinf(self.yData)
|
||||
if any(nanMask):
|
||||
self.dataMask = ~nanMask
|
||||
x = self.xData[self.dataMask]
|
||||
y = self.yData[self.dataMask]
|
||||
self.xClean = self.xData[self.dataMask]
|
||||
self.yClean = self.yData[self.dataMask]
|
||||
else:
|
||||
self.dataMask = None
|
||||
x = self.xData
|
||||
y = self.yData
|
||||
|
||||
self.xClean = self.xData
|
||||
self.yClean = self.yData
|
||||
|
||||
ds = self.opts['downsample']
|
||||
if ds > 1:
|
||||
x = x[::ds]
|
||||
#y = resample(y[:len(x)*ds], len(x)) ## scipy.signal.resample causes nasty ringing
|
||||
y = y[::ds]
|
||||
if self.xDisp is None:
|
||||
x = self.xClean
|
||||
y = self.yClean
|
||||
|
||||
|
||||
#ds = self.opts['downsample']
|
||||
#if isinstance(ds, int) and ds > 1:
|
||||
#x = x[::ds]
|
||||
##y = resample(y[:len(x)*ds], len(x)) ## scipy.signal.resample causes nasty ringing
|
||||
#y = y[::ds]
|
||||
if self.opts['fftMode']:
|
||||
f = np.fft.fft(y) / len(y)
|
||||
y = abs(f[1:len(f)/2])
|
||||
@ -457,6 +511,53 @@ class PlotDataItem(GraphicsObject):
|
||||
y = y[self.dataMask]
|
||||
else:
|
||||
self.dataMask = None
|
||||
|
||||
ds = self.opts['downsample']
|
||||
if not isinstance(ds, int):
|
||||
ds = 1
|
||||
|
||||
if self.opts['autoDownsample']:
|
||||
# this option presumes that x-values have uniform spacing
|
||||
range = self.viewRect()
|
||||
if range is not None:
|
||||
dx = float(x[-1]-x[0]) / (len(x)-1)
|
||||
x0 = (range.left()-x[0]) / dx
|
||||
x1 = (range.right()-x[0]) / dx
|
||||
width = self.getViewBox().width()
|
||||
ds = int(max(1, int(0.2 * (x1-x0) / width)))
|
||||
## downsampling is expensive; delay until after clipping.
|
||||
|
||||
if self.opts['clipToView']:
|
||||
# this option presumes that x-values have uniform spacing
|
||||
range = self.viewRect()
|
||||
if range is not None:
|
||||
dx = float(x[-1]-x[0]) / (len(x)-1)
|
||||
# clip to visible region extended by downsampling value
|
||||
x0 = np.clip(int((range.left()-x[0])/dx)-1*ds , 0, len(x)-1)
|
||||
x1 = np.clip(int((range.right()-x[0])/dx)+2*ds , 0, len(x)-1)
|
||||
x = x[x0:x1]
|
||||
y = y[x0:x1]
|
||||
|
||||
if ds > 1:
|
||||
if self.opts['downsampleMethod'] == 'subsample':
|
||||
x = x[::ds]
|
||||
y = y[::ds]
|
||||
elif self.opts['downsampleMethod'] == 'mean':
|
||||
n = len(x) / ds
|
||||
x = x[:n*ds:ds]
|
||||
y = y[:n*ds].reshape(n,ds).mean(axis=1)
|
||||
elif self.opts['downsampleMethod'] == 'peak':
|
||||
n = len(x) / ds
|
||||
x1 = np.empty((n,2))
|
||||
x1[:] = x[:n*ds:ds,np.newaxis]
|
||||
x = x1.reshape(n*2)
|
||||
y1 = np.empty((n,2))
|
||||
y2 = y[:n*ds].reshape((n, ds))
|
||||
y1[:,0] = y2.max(axis=1)
|
||||
y1[:,1] = y2.min(axis=1)
|
||||
y = y1.reshape(n*2)
|
||||
|
||||
|
||||
self.xDisp = x
|
||||
self.yDisp = y
|
||||
#print self.yDisp.shape, self.yDisp.min(), self.yDisp.max()
|
||||
@ -542,6 +643,8 @@ class PlotDataItem(GraphicsObject):
|
||||
#self.scatters = []
|
||||
self.xData = None
|
||||
self.yData = None
|
||||
self.xClean = None
|
||||
self.yClean = None
|
||||
self.xDisp = None
|
||||
self.yDisp = None
|
||||
self.curve.setData([])
|
||||
@ -557,6 +660,14 @@ class PlotDataItem(GraphicsObject):
|
||||
self.sigClicked.emit(self)
|
||||
self.sigPointsClicked.emit(self, points)
|
||||
|
||||
def viewRangeChanged(self):
|
||||
# view range has changed; re-plot if needed
|
||||
if self.opts['clipToView'] or self.opts['autoDownsample']:
|
||||
self.xDisp = self.yDisp = None
|
||||
self.updateItems()
|
||||
|
||||
|
||||
|
||||
|
||||
def dataType(obj):
|
||||
if hasattr(obj, '__len__') and len(obj) == 0:
|
||||
|
@ -256,6 +256,11 @@ class PlotItem(GraphicsWidget):
|
||||
c.logYCheck.toggled.connect(self.updateLogMode)
|
||||
|
||||
c.downsampleSpin.valueChanged.connect(self.updateDownsampling)
|
||||
c.downsampleCheck.toggled.connect(self.updateDownsampling)
|
||||
c.autoDownsampleCheck.toggled.connect(self.updateDownsampling)
|
||||
c.subsampleRadio.toggled.connect(self.updateDownsampling)
|
||||
c.meanRadio.toggled.connect(self.updateDownsampling)
|
||||
c.clipToViewCheck.toggled.connect(self.updateDownsampling)
|
||||
|
||||
self.ctrl.avgParamList.itemClicked.connect(self.avgParamListClicked)
|
||||
self.ctrl.averageGroup.toggled.connect(self.avgToggled)
|
||||
@ -526,7 +531,8 @@ class PlotItem(GraphicsWidget):
|
||||
(alpha, auto) = self.alphaState()
|
||||
item.setAlpha(alpha, auto)
|
||||
item.setFftMode(self.ctrl.fftCheck.isChecked())
|
||||
item.setDownsampling(self.downsampleMode())
|
||||
item.setDownsampling(*self.downsampleMode())
|
||||
item.setClipToView(self.clipToViewMode())
|
||||
item.setPointMode(self.pointMode())
|
||||
|
||||
## Hide older plots if needed
|
||||
@ -568,8 +574,8 @@ class PlotItem(GraphicsWidget):
|
||||
:func:`InfiniteLine.__init__() <pyqtgraph.InfiniteLine.__init__>`.
|
||||
Returns the item created.
|
||||
"""
|
||||
angle = 0 if x is None else 90
|
||||
pos = x if x is not None else y
|
||||
pos = kwds.get('pos', x if x is not None else y)
|
||||
angle = kwds.get('angle', 0 if x is None else 90)
|
||||
line = InfiniteLine(pos, angle, **kwds)
|
||||
self.addItem(line)
|
||||
if z is not None:
|
||||
@ -941,23 +947,81 @@ class PlotItem(GraphicsWidget):
|
||||
self.enableAutoRange()
|
||||
self.recomputeAverages()
|
||||
|
||||
def setDownsampling(self, ds=None, auto=None, mode=None):
|
||||
"""Change the default downsampling mode for all PlotDataItems managed by this plot.
|
||||
|
||||
=========== =================================================================
|
||||
Arguments
|
||||
ds (int) Reduce visible plot samples by this factor, or
|
||||
(bool) To enable/disable downsampling without changing the value.
|
||||
auto (bool) If True, automatically pick *ds* based on visible range
|
||||
mode 'subsample': Downsample by taking the first of N samples.
|
||||
This method is fastest and least accurate.
|
||||
'mean': Downsample by taking the mean of N samples.
|
||||
'peak': Downsample by drawing a saw wave that follows the min
|
||||
and max of the original data. This method produces the best
|
||||
visual representation of the data but is slower.
|
||||
=========== =================================================================
|
||||
"""
|
||||
if ds is not None:
|
||||
if ds is False:
|
||||
self.ctrl.downsampleCheck.setChecked(False)
|
||||
elif ds is True:
|
||||
self.ctrl.downsampleCheck.setChecked(True)
|
||||
else:
|
||||
self.ctrl.downsampleCheck.setChecked(True)
|
||||
self.ctrl.downsampleSpin.setValue(ds)
|
||||
|
||||
if auto is not None:
|
||||
if auto and ds is not False:
|
||||
self.ctrl.downsampleCheck.setChecked(True)
|
||||
self.ctrl.autoDownsampleCheck.setChecked(auto)
|
||||
|
||||
if mode is not None:
|
||||
if mode == 'subsample':
|
||||
self.ctrl.subsampleRadio.setChecked(True)
|
||||
elif mode == 'mean':
|
||||
self.ctrl.meanRadio.setChecked(True)
|
||||
elif mode == 'peak':
|
||||
self.ctrl.peakRadio.setChecked(True)
|
||||
else:
|
||||
raise ValueError("mode argument must be 'subsample', 'mean', or 'peak'.")
|
||||
|
||||
def updateDownsampling(self):
|
||||
ds = self.downsampleMode()
|
||||
ds, auto, method = self.downsampleMode()
|
||||
clip = self.ctrl.clipToViewCheck.isChecked()
|
||||
for c in self.curves:
|
||||
c.setDownsampling(ds)
|
||||
c.setDownsampling(ds, auto, method)
|
||||
c.setClipToView(clip)
|
||||
self.recomputeAverages()
|
||||
|
||||
|
||||
def downsampleMode(self):
|
||||
if self.ctrl.decimateGroup.isChecked():
|
||||
if self.ctrl.manualDecimateRadio.isChecked():
|
||||
ds = self.ctrl.downsampleSpin.value()
|
||||
else:
|
||||
ds = True
|
||||
if self.ctrl.downsampleCheck.isChecked():
|
||||
ds = self.ctrl.downsampleSpin.value()
|
||||
else:
|
||||
ds = False
|
||||
return ds
|
||||
ds = 1
|
||||
|
||||
auto = self.ctrl.downsampleCheck.isChecked() and self.ctrl.autoDownsampleCheck.isChecked()
|
||||
|
||||
if self.ctrl.subsampleRadio.isChecked():
|
||||
method = 'subsample'
|
||||
elif self.ctrl.meanRadio.isChecked():
|
||||
method = 'mean'
|
||||
elif self.ctrl.peakRadio.isChecked():
|
||||
method = 'peak'
|
||||
|
||||
return ds, auto, method
|
||||
|
||||
def setClipToView(self, clip):
|
||||
"""Set the default clip-to-view mode for all PlotDataItems managed by this plot.
|
||||
If *clip* is True, then PlotDataItems will attempt to draw only points within the visible
|
||||
range of the ViewBox."""
|
||||
self.ctrl.clipToViewCheck.setChecked(clip)
|
||||
|
||||
def clipToViewMode(self):
|
||||
return self.ctrl.clipToViewCheck.isChecked()
|
||||
|
||||
|
||||
|
||||
def updateDecimation(self):
|
||||
if self.ctrl.maxTracesCheck.isChecked():
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>258</width>
|
||||
<height>605</height>
|
||||
<width>481</width>
|
||||
<height>840</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -16,8 +16,8 @@
|
||||
<widget class="QGroupBox" name="averageGroup">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>200</y>
|
||||
<x>0</x>
|
||||
<y>640</y>
|
||||
<width>242</width>
|
||||
<height>182</height>
|
||||
</rect>
|
||||
@ -46,21 +46,15 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QGroupBox" name="decimateGroup">
|
||||
<widget class="QFrame" name="decimateGroup">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>70</y>
|
||||
<width>242</width>
|
||||
<height>160</height>
|
||||
<x>10</x>
|
||||
<y>140</y>
|
||||
<width>191</width>
|
||||
<height>171</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Downsample</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
@ -68,40 +62,17 @@
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QRadioButton" name="manualDecimateRadio">
|
||||
<item row="7" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="clipToViewCheck">
|
||||
<property name="toolTip">
|
||||
<string>Plot only the portion of each curve that is visible. This assumes X values are uniformly spaced.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Manual</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
<string>Clip to View</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="downsampleSpin">
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100000</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QRadioButton" name="autoDecimateRadio">
|
||||
<property name="text">
|
||||
<string>Auto</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="8" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="maxTracesCheck">
|
||||
<property name="toolTip">
|
||||
<string>If multiple curves are displayed in this plot, check this box to limit the number of traces that are displayed.</string>
|
||||
@ -111,14 +82,34 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<item row="0" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="downsampleCheck">
|
||||
<property name="text">
|
||||
<string>Downsample</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1" colspan="2">
|
||||
<widget class="QRadioButton" name="peakRadio">
|
||||
<property name="toolTip">
|
||||
<string>Downsample by drawing a saw wave that follows the min and max of the original data. This method produces the best visual representation of the data but is slower.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Peak</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="2">
|
||||
<widget class="QSpinBox" name="maxTracesSpin">
|
||||
<property name="toolTip">
|
||||
<string>If multiple curves are displayed in this plot, check "Max Traces" and set this value to limit the number of traces that are displayed.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<item row="9" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="forgetTracesCheck">
|
||||
<property name="toolTip">
|
||||
<string>If MaxTraces is checked, remove curves from memory after they are hidden (saves memory, but traces can not be un-hidden).</string>
|
||||
@ -128,6 +119,74 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1" colspan="2">
|
||||
<widget class="QRadioButton" name="meanRadio">
|
||||
<property name="toolTip">
|
||||
<string>Downsample by taking the mean of N samples.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Mean</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" colspan="2">
|
||||
<widget class="QRadioButton" name="subsampleRadio">
|
||||
<property name="toolTip">
|
||||
<string>Downsample by taking the first of N samples. This method is fastest and least accurate.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Subsample</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QCheckBox" name="autoDownsampleCheck">
|
||||
<property name="toolTip">
|
||||
<string>Automatically downsample data based on the visible range. This assumes X values are uniformly spaced.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Auto</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Maximum</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="downsampleSpin">
|
||||
<property name="toolTip">
|
||||
<string>Downsample data before plotting. (plot every Nth sample)</string>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string>x</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100000</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QFrame" name="transformGroup">
|
||||
|
@ -1,9 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file './graphicsItems/PlotItem/plotConfigTemplate.ui'
|
||||
# Form implementation generated from reading ui file './pyqtgraph/graphicsItems/PlotItem/plotConfigTemplate.ui'
|
||||
#
|
||||
# Created: Sun Sep 9 14:41:32 2012
|
||||
# by: PyQt4 UI code generator 4.9.1
|
||||
# Created: Mon Jul 1 23:21:08 2013
|
||||
# by: PyQt4 UI code generator 4.9.3
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
@ -17,9 +17,9 @@ except AttributeError:
|
||||
class Ui_Form(object):
|
||||
def setupUi(self, Form):
|
||||
Form.setObjectName(_fromUtf8("Form"))
|
||||
Form.resize(258, 605)
|
||||
Form.resize(481, 840)
|
||||
self.averageGroup = QtGui.QGroupBox(Form)
|
||||
self.averageGroup.setGeometry(QtCore.QRect(10, 200, 242, 182))
|
||||
self.averageGroup.setGeometry(QtCore.QRect(0, 640, 242, 182))
|
||||
self.averageGroup.setCheckable(True)
|
||||
self.averageGroup.setChecked(False)
|
||||
self.averageGroup.setObjectName(_fromUtf8("averageGroup"))
|
||||
@ -30,37 +30,50 @@ class Ui_Form(object):
|
||||
self.avgParamList = QtGui.QListWidget(self.averageGroup)
|
||||
self.avgParamList.setObjectName(_fromUtf8("avgParamList"))
|
||||
self.gridLayout_5.addWidget(self.avgParamList, 0, 0, 1, 1)
|
||||
self.decimateGroup = QtGui.QGroupBox(Form)
|
||||
self.decimateGroup.setGeometry(QtCore.QRect(0, 70, 242, 160))
|
||||
self.decimateGroup.setCheckable(True)
|
||||
self.decimateGroup = QtGui.QFrame(Form)
|
||||
self.decimateGroup.setGeometry(QtCore.QRect(10, 140, 191, 171))
|
||||
self.decimateGroup.setObjectName(_fromUtf8("decimateGroup"))
|
||||
self.gridLayout_4 = QtGui.QGridLayout(self.decimateGroup)
|
||||
self.gridLayout_4.setMargin(0)
|
||||
self.gridLayout_4.setSpacing(0)
|
||||
self.gridLayout_4.setObjectName(_fromUtf8("gridLayout_4"))
|
||||
self.manualDecimateRadio = QtGui.QRadioButton(self.decimateGroup)
|
||||
self.manualDecimateRadio.setChecked(True)
|
||||
self.manualDecimateRadio.setObjectName(_fromUtf8("manualDecimateRadio"))
|
||||
self.gridLayout_4.addWidget(self.manualDecimateRadio, 0, 0, 1, 1)
|
||||
self.clipToViewCheck = QtGui.QCheckBox(self.decimateGroup)
|
||||
self.clipToViewCheck.setObjectName(_fromUtf8("clipToViewCheck"))
|
||||
self.gridLayout_4.addWidget(self.clipToViewCheck, 7, 0, 1, 3)
|
||||
self.maxTracesCheck = QtGui.QCheckBox(self.decimateGroup)
|
||||
self.maxTracesCheck.setObjectName(_fromUtf8("maxTracesCheck"))
|
||||
self.gridLayout_4.addWidget(self.maxTracesCheck, 8, 0, 1, 2)
|
||||
self.downsampleCheck = QtGui.QCheckBox(self.decimateGroup)
|
||||
self.downsampleCheck.setObjectName(_fromUtf8("downsampleCheck"))
|
||||
self.gridLayout_4.addWidget(self.downsampleCheck, 0, 0, 1, 3)
|
||||
self.peakRadio = QtGui.QRadioButton(self.decimateGroup)
|
||||
self.peakRadio.setChecked(True)
|
||||
self.peakRadio.setObjectName(_fromUtf8("peakRadio"))
|
||||
self.gridLayout_4.addWidget(self.peakRadio, 6, 1, 1, 2)
|
||||
self.maxTracesSpin = QtGui.QSpinBox(self.decimateGroup)
|
||||
self.maxTracesSpin.setObjectName(_fromUtf8("maxTracesSpin"))
|
||||
self.gridLayout_4.addWidget(self.maxTracesSpin, 8, 2, 1, 1)
|
||||
self.forgetTracesCheck = QtGui.QCheckBox(self.decimateGroup)
|
||||
self.forgetTracesCheck.setObjectName(_fromUtf8("forgetTracesCheck"))
|
||||
self.gridLayout_4.addWidget(self.forgetTracesCheck, 9, 0, 1, 3)
|
||||
self.meanRadio = QtGui.QRadioButton(self.decimateGroup)
|
||||
self.meanRadio.setObjectName(_fromUtf8("meanRadio"))
|
||||
self.gridLayout_4.addWidget(self.meanRadio, 3, 1, 1, 2)
|
||||
self.subsampleRadio = QtGui.QRadioButton(self.decimateGroup)
|
||||
self.subsampleRadio.setObjectName(_fromUtf8("subsampleRadio"))
|
||||
self.gridLayout_4.addWidget(self.subsampleRadio, 2, 1, 1, 2)
|
||||
self.autoDownsampleCheck = QtGui.QCheckBox(self.decimateGroup)
|
||||
self.autoDownsampleCheck.setChecked(True)
|
||||
self.autoDownsampleCheck.setObjectName(_fromUtf8("autoDownsampleCheck"))
|
||||
self.gridLayout_4.addWidget(self.autoDownsampleCheck, 1, 2, 1, 1)
|
||||
spacerItem = QtGui.QSpacerItem(30, 20, QtGui.QSizePolicy.Maximum, QtGui.QSizePolicy.Minimum)
|
||||
self.gridLayout_4.addItem(spacerItem, 2, 0, 1, 1)
|
||||
self.downsampleSpin = QtGui.QSpinBox(self.decimateGroup)
|
||||
self.downsampleSpin.setMinimum(1)
|
||||
self.downsampleSpin.setMaximum(100000)
|
||||
self.downsampleSpin.setProperty("value", 1)
|
||||
self.downsampleSpin.setObjectName(_fromUtf8("downsampleSpin"))
|
||||
self.gridLayout_4.addWidget(self.downsampleSpin, 0, 1, 1, 1)
|
||||
self.autoDecimateRadio = QtGui.QRadioButton(self.decimateGroup)
|
||||
self.autoDecimateRadio.setChecked(False)
|
||||
self.autoDecimateRadio.setObjectName(_fromUtf8("autoDecimateRadio"))
|
||||
self.gridLayout_4.addWidget(self.autoDecimateRadio, 1, 0, 1, 1)
|
||||
self.maxTracesCheck = QtGui.QCheckBox(self.decimateGroup)
|
||||
self.maxTracesCheck.setObjectName(_fromUtf8("maxTracesCheck"))
|
||||
self.gridLayout_4.addWidget(self.maxTracesCheck, 2, 0, 1, 1)
|
||||
self.maxTracesSpin = QtGui.QSpinBox(self.decimateGroup)
|
||||
self.maxTracesSpin.setObjectName(_fromUtf8("maxTracesSpin"))
|
||||
self.gridLayout_4.addWidget(self.maxTracesSpin, 2, 1, 1, 1)
|
||||
self.forgetTracesCheck = QtGui.QCheckBox(self.decimateGroup)
|
||||
self.forgetTracesCheck.setObjectName(_fromUtf8("forgetTracesCheck"))
|
||||
self.gridLayout_4.addWidget(self.forgetTracesCheck, 3, 0, 1, 2)
|
||||
self.gridLayout_4.addWidget(self.downsampleSpin, 1, 1, 1, 1)
|
||||
self.transformGroup = QtGui.QFrame(Form)
|
||||
self.transformGroup.setGeometry(QtCore.QRect(0, 0, 154, 79))
|
||||
self.transformGroup.setObjectName(_fromUtf8("transformGroup"))
|
||||
@ -129,14 +142,24 @@ class Ui_Form(object):
|
||||
Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.averageGroup.setToolTip(QtGui.QApplication.translate("Form", "Display averages of the curves displayed in this plot. The parameter list allows you to choose parameters to average over (if any are available).", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.averageGroup.setTitle(QtGui.QApplication.translate("Form", "Average", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.decimateGroup.setTitle(QtGui.QApplication.translate("Form", "Downsample", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.manualDecimateRadio.setText(QtGui.QApplication.translate("Form", "Manual", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.autoDecimateRadio.setText(QtGui.QApplication.translate("Form", "Auto", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.clipToViewCheck.setToolTip(QtGui.QApplication.translate("Form", "Plot only the portion of each curve that is visible. This assumes X values are uniformly spaced.", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.clipToViewCheck.setText(QtGui.QApplication.translate("Form", "Clip to View", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.maxTracesCheck.setToolTip(QtGui.QApplication.translate("Form", "If multiple curves are displayed in this plot, check this box to limit the number of traces that are displayed.", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.maxTracesCheck.setText(QtGui.QApplication.translate("Form", "Max Traces:", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.downsampleCheck.setText(QtGui.QApplication.translate("Form", "Downsample", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.peakRadio.setToolTip(QtGui.QApplication.translate("Form", "Downsample by drawing a saw wave that follows the min and max of the original data. This method produces the best visual representation of the data but is slower.", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.peakRadio.setText(QtGui.QApplication.translate("Form", "Peak", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.maxTracesSpin.setToolTip(QtGui.QApplication.translate("Form", "If multiple curves are displayed in this plot, check \"Max Traces\" and set this value to limit the number of traces that are displayed.", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.forgetTracesCheck.setToolTip(QtGui.QApplication.translate("Form", "If MaxTraces is checked, remove curves from memory after they are hidden (saves memory, but traces can not be un-hidden).", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.forgetTracesCheck.setText(QtGui.QApplication.translate("Form", "Forget hidden traces", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.meanRadio.setToolTip(QtGui.QApplication.translate("Form", "Downsample by taking the mean of N samples.", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.meanRadio.setText(QtGui.QApplication.translate("Form", "Mean", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.subsampleRadio.setToolTip(QtGui.QApplication.translate("Form", "Downsample by taking the first of N samples. This method is fastest and least accurate.", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.subsampleRadio.setText(QtGui.QApplication.translate("Form", "Subsample", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.autoDownsampleCheck.setToolTip(QtGui.QApplication.translate("Form", "Automatically downsample data based on the visible range. This assumes X values are uniformly spaced.", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.autoDownsampleCheck.setText(QtGui.QApplication.translate("Form", "Auto", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.downsampleSpin.setToolTip(QtGui.QApplication.translate("Form", "Downsample data before plotting. (plot every Nth sample)", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.downsampleSpin.setSuffix(QtGui.QApplication.translate("Form", "x", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.fftCheck.setText(QtGui.QApplication.translate("Form", "Power Spectrum (FFT)", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.logXCheck.setText(QtGui.QApplication.translate("Form", "Log X", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.logYCheck.setText(QtGui.QApplication.translate("Form", "Log Y", None, QtGui.QApplication.UnicodeUTF8))
|
||||
|
@ -1,9 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file './graphicsItems/PlotItem/plotConfigTemplate.ui'
|
||||
# Form implementation generated from reading ui file './pyqtgraph/graphicsItems/PlotItem/plotConfigTemplate.ui'
|
||||
#
|
||||
# Created: Sun Sep 9 14:41:32 2012
|
||||
# by: pyside-uic 0.2.13 running on PySide 1.1.0
|
||||
# Created: Mon Jul 1 23:21:08 2013
|
||||
# by: pyside-uic 0.2.13 running on PySide 1.1.2
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
@ -12,9 +12,9 @@ from PySide import QtCore, QtGui
|
||||
class Ui_Form(object):
|
||||
def setupUi(self, Form):
|
||||
Form.setObjectName("Form")
|
||||
Form.resize(258, 605)
|
||||
Form.resize(481, 840)
|
||||
self.averageGroup = QtGui.QGroupBox(Form)
|
||||
self.averageGroup.setGeometry(QtCore.QRect(10, 200, 242, 182))
|
||||
self.averageGroup.setGeometry(QtCore.QRect(0, 640, 242, 182))
|
||||
self.averageGroup.setCheckable(True)
|
||||
self.averageGroup.setChecked(False)
|
||||
self.averageGroup.setObjectName("averageGroup")
|
||||
@ -25,37 +25,50 @@ class Ui_Form(object):
|
||||
self.avgParamList = QtGui.QListWidget(self.averageGroup)
|
||||
self.avgParamList.setObjectName("avgParamList")
|
||||
self.gridLayout_5.addWidget(self.avgParamList, 0, 0, 1, 1)
|
||||
self.decimateGroup = QtGui.QGroupBox(Form)
|
||||
self.decimateGroup.setGeometry(QtCore.QRect(0, 70, 242, 160))
|
||||
self.decimateGroup.setCheckable(True)
|
||||
self.decimateGroup = QtGui.QFrame(Form)
|
||||
self.decimateGroup.setGeometry(QtCore.QRect(10, 140, 191, 171))
|
||||
self.decimateGroup.setObjectName("decimateGroup")
|
||||
self.gridLayout_4 = QtGui.QGridLayout(self.decimateGroup)
|
||||
self.gridLayout_4.setContentsMargins(0, 0, 0, 0)
|
||||
self.gridLayout_4.setSpacing(0)
|
||||
self.gridLayout_4.setObjectName("gridLayout_4")
|
||||
self.manualDecimateRadio = QtGui.QRadioButton(self.decimateGroup)
|
||||
self.manualDecimateRadio.setChecked(True)
|
||||
self.manualDecimateRadio.setObjectName("manualDecimateRadio")
|
||||
self.gridLayout_4.addWidget(self.manualDecimateRadio, 0, 0, 1, 1)
|
||||
self.clipToViewCheck = QtGui.QCheckBox(self.decimateGroup)
|
||||
self.clipToViewCheck.setObjectName("clipToViewCheck")
|
||||
self.gridLayout_4.addWidget(self.clipToViewCheck, 7, 0, 1, 3)
|
||||
self.maxTracesCheck = QtGui.QCheckBox(self.decimateGroup)
|
||||
self.maxTracesCheck.setObjectName("maxTracesCheck")
|
||||
self.gridLayout_4.addWidget(self.maxTracesCheck, 8, 0, 1, 2)
|
||||
self.downsampleCheck = QtGui.QCheckBox(self.decimateGroup)
|
||||
self.downsampleCheck.setObjectName("downsampleCheck")
|
||||
self.gridLayout_4.addWidget(self.downsampleCheck, 0, 0, 1, 3)
|
||||
self.peakRadio = QtGui.QRadioButton(self.decimateGroup)
|
||||
self.peakRadio.setChecked(True)
|
||||
self.peakRadio.setObjectName("peakRadio")
|
||||
self.gridLayout_4.addWidget(self.peakRadio, 6, 1, 1, 2)
|
||||
self.maxTracesSpin = QtGui.QSpinBox(self.decimateGroup)
|
||||
self.maxTracesSpin.setObjectName("maxTracesSpin")
|
||||
self.gridLayout_4.addWidget(self.maxTracesSpin, 8, 2, 1, 1)
|
||||
self.forgetTracesCheck = QtGui.QCheckBox(self.decimateGroup)
|
||||
self.forgetTracesCheck.setObjectName("forgetTracesCheck")
|
||||
self.gridLayout_4.addWidget(self.forgetTracesCheck, 9, 0, 1, 3)
|
||||
self.meanRadio = QtGui.QRadioButton(self.decimateGroup)
|
||||
self.meanRadio.setObjectName("meanRadio")
|
||||
self.gridLayout_4.addWidget(self.meanRadio, 3, 1, 1, 2)
|
||||
self.subsampleRadio = QtGui.QRadioButton(self.decimateGroup)
|
||||
self.subsampleRadio.setObjectName("subsampleRadio")
|
||||
self.gridLayout_4.addWidget(self.subsampleRadio, 2, 1, 1, 2)
|
||||
self.autoDownsampleCheck = QtGui.QCheckBox(self.decimateGroup)
|
||||
self.autoDownsampleCheck.setChecked(True)
|
||||
self.autoDownsampleCheck.setObjectName("autoDownsampleCheck")
|
||||
self.gridLayout_4.addWidget(self.autoDownsampleCheck, 1, 2, 1, 1)
|
||||
spacerItem = QtGui.QSpacerItem(30, 20, QtGui.QSizePolicy.Maximum, QtGui.QSizePolicy.Minimum)
|
||||
self.gridLayout_4.addItem(spacerItem, 2, 0, 1, 1)
|
||||
self.downsampleSpin = QtGui.QSpinBox(self.decimateGroup)
|
||||
self.downsampleSpin.setMinimum(1)
|
||||
self.downsampleSpin.setMaximum(100000)
|
||||
self.downsampleSpin.setProperty("value", 1)
|
||||
self.downsampleSpin.setObjectName("downsampleSpin")
|
||||
self.gridLayout_4.addWidget(self.downsampleSpin, 0, 1, 1, 1)
|
||||
self.autoDecimateRadio = QtGui.QRadioButton(self.decimateGroup)
|
||||
self.autoDecimateRadio.setChecked(False)
|
||||
self.autoDecimateRadio.setObjectName("autoDecimateRadio")
|
||||
self.gridLayout_4.addWidget(self.autoDecimateRadio, 1, 0, 1, 1)
|
||||
self.maxTracesCheck = QtGui.QCheckBox(self.decimateGroup)
|
||||
self.maxTracesCheck.setObjectName("maxTracesCheck")
|
||||
self.gridLayout_4.addWidget(self.maxTracesCheck, 2, 0, 1, 1)
|
||||
self.maxTracesSpin = QtGui.QSpinBox(self.decimateGroup)
|
||||
self.maxTracesSpin.setObjectName("maxTracesSpin")
|
||||
self.gridLayout_4.addWidget(self.maxTracesSpin, 2, 1, 1, 1)
|
||||
self.forgetTracesCheck = QtGui.QCheckBox(self.decimateGroup)
|
||||
self.forgetTracesCheck.setObjectName("forgetTracesCheck")
|
||||
self.gridLayout_4.addWidget(self.forgetTracesCheck, 3, 0, 1, 2)
|
||||
self.gridLayout_4.addWidget(self.downsampleSpin, 1, 1, 1, 1)
|
||||
self.transformGroup = QtGui.QFrame(Form)
|
||||
self.transformGroup.setGeometry(QtCore.QRect(0, 0, 154, 79))
|
||||
self.transformGroup.setObjectName("transformGroup")
|
||||
@ -124,14 +137,24 @@ class Ui_Form(object):
|
||||
Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.averageGroup.setToolTip(QtGui.QApplication.translate("Form", "Display averages of the curves displayed in this plot. The parameter list allows you to choose parameters to average over (if any are available).", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.averageGroup.setTitle(QtGui.QApplication.translate("Form", "Average", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.decimateGroup.setTitle(QtGui.QApplication.translate("Form", "Downsample", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.manualDecimateRadio.setText(QtGui.QApplication.translate("Form", "Manual", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.autoDecimateRadio.setText(QtGui.QApplication.translate("Form", "Auto", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.clipToViewCheck.setToolTip(QtGui.QApplication.translate("Form", "Plot only the portion of each curve that is visible. This assumes X values are uniformly spaced.", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.clipToViewCheck.setText(QtGui.QApplication.translate("Form", "Clip to View", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.maxTracesCheck.setToolTip(QtGui.QApplication.translate("Form", "If multiple curves are displayed in this plot, check this box to limit the number of traces that are displayed.", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.maxTracesCheck.setText(QtGui.QApplication.translate("Form", "Max Traces:", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.downsampleCheck.setText(QtGui.QApplication.translate("Form", "Downsample", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.peakRadio.setToolTip(QtGui.QApplication.translate("Form", "Downsample by drawing a saw wave that follows the min and max of the original data. This method produces the best visual representation of the data but is slower.", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.peakRadio.setText(QtGui.QApplication.translate("Form", "Peak", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.maxTracesSpin.setToolTip(QtGui.QApplication.translate("Form", "If multiple curves are displayed in this plot, check \"Max Traces\" and set this value to limit the number of traces that are displayed.", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.forgetTracesCheck.setToolTip(QtGui.QApplication.translate("Form", "If MaxTraces is checked, remove curves from memory after they are hidden (saves memory, but traces can not be un-hidden).", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.forgetTracesCheck.setText(QtGui.QApplication.translate("Form", "Forget hidden traces", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.meanRadio.setToolTip(QtGui.QApplication.translate("Form", "Downsample by taking the mean of N samples.", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.meanRadio.setText(QtGui.QApplication.translate("Form", "Mean", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.subsampleRadio.setToolTip(QtGui.QApplication.translate("Form", "Downsample by taking the first of N samples. This method is fastest and least accurate.", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.subsampleRadio.setText(QtGui.QApplication.translate("Form", "Subsample", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.autoDownsampleCheck.setToolTip(QtGui.QApplication.translate("Form", "Automatically downsample data based on the visible range. This assumes X values are uniformly spaced.", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.autoDownsampleCheck.setText(QtGui.QApplication.translate("Form", "Auto", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.downsampleSpin.setToolTip(QtGui.QApplication.translate("Form", "Downsample data before plotting. (plot every Nth sample)", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.downsampleSpin.setSuffix(QtGui.QApplication.translate("Form", "x", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.fftCheck.setText(QtGui.QApplication.translate("Form", "Power Spectrum (FFT)", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.logXCheck.setText(QtGui.QApplication.translate("Form", "Log X", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.logYCheck.setText(QtGui.QApplication.translate("Form", "Log Y", None, QtGui.QApplication.UnicodeUTF8))
|
||||
|
@ -15,7 +15,7 @@ __all__ = ['ScatterPlotItem', 'SpotItem']
|
||||
|
||||
|
||||
## Build all symbol paths
|
||||
Symbols = OrderedDict([(name, QtGui.QPainterPath()) for name in ['o', 's', 't', 'd', '+']])
|
||||
Symbols = OrderedDict([(name, QtGui.QPainterPath()) for name in ['o', 's', 't', 'd', '+', 'x']])
|
||||
Symbols['o'].addEllipse(QtCore.QRectF(-0.5, -0.5, 1, 1))
|
||||
Symbols['s'].addRect(QtCore.QRectF(-0.5, -0.5, 1, 1))
|
||||
coords = {
|
||||
@ -32,6 +32,9 @@ for k, c in coords.items():
|
||||
for x,y in c[1:]:
|
||||
Symbols[k].lineTo(x, y)
|
||||
Symbols[k].closeSubpath()
|
||||
tr = QtGui.QTransform()
|
||||
tr.rotate(45)
|
||||
Symbols['x'] = tr.map(Symbols['+'])
|
||||
|
||||
|
||||
def drawSymbol(painter, symbol, size, pen, brush):
|
||||
@ -689,7 +692,8 @@ class ScatterPlotItem(GraphicsObject):
|
||||
def setExportMode(self, *args, **kwds):
|
||||
GraphicsObject.setExportMode(self, *args, **kwds)
|
||||
self.invalidate()
|
||||
|
||||
|
||||
@pg.debug.warnOnException ## raising an exception here causes crash
|
||||
def paint(self, p, *args):
|
||||
|
||||
#p.setPen(fn.mkPen('r'))
|
||||
|
@ -141,6 +141,12 @@ class ViewBox(GraphicsWidget):
|
||||
self.rbScaleBox.hide()
|
||||
self.addItem(self.rbScaleBox, ignoreBounds=True)
|
||||
|
||||
## show target rect for debugging
|
||||
self.target = QtGui.QGraphicsRectItem(0, 0, 1, 1)
|
||||
self.target.setPen(fn.mkPen('r'))
|
||||
self.target.setParentItem(self)
|
||||
self.target.hide()
|
||||
|
||||
self.axHistory = [] # maintain a history of zoom locations
|
||||
self.axHistoryPointer = -1 # pointer into the history. Allows forward/backward movement, not just "undo"
|
||||
|
||||
@ -275,6 +281,9 @@ class ViewBox(GraphicsWidget):
|
||||
"""
|
||||
if item.zValue() < self.zValue():
|
||||
item.setZValue(self.zValue()+1)
|
||||
scene = self.scene()
|
||||
if scene is not None and scene is not item.scene():
|
||||
scene.addItem(item) ## Necessary due to Qt bug: https://bugreports.qt-project.org/browse/QTBUG-18616
|
||||
item.setParentItem(self.childGroup)
|
||||
if not ignoreBounds:
|
||||
self.addedItems.append(item)
|
||||
@ -294,7 +303,7 @@ class ViewBox(GraphicsWidget):
|
||||
for i in self.addedItems[:]:
|
||||
self.removeItem(i)
|
||||
for ch in self.childGroup.childItems():
|
||||
ch.setParent(None)
|
||||
ch.setParentItem(None)
|
||||
|
||||
def resizeEvent(self, ev):
|
||||
#self.setRange(self.range, padding=0)
|
||||
@ -389,10 +398,28 @@ class ViewBox(GraphicsWidget):
|
||||
p = (mx-mn) * xpad
|
||||
mn -= p
|
||||
mx += p
|
||||
|
||||
if self.state['targetRange'][ax] != [mn, mx]:
|
||||
self.state['targetRange'][ax] = [mn, mx]
|
||||
changed[ax] = True
|
||||
|
||||
aspect = self.state['aspectLocked'] # size ratio / view ratio
|
||||
if aspect is not False and len(changes) == 1:
|
||||
## need to adjust orthogonal target range to match
|
||||
size = [self.width(), self.height()]
|
||||
tr1 = self.state['targetRange'][ax]
|
||||
tr2 = self.state['targetRange'][1-ax]
|
||||
if size[1] == 0 or aspect == 0:
|
||||
ratio = 1.0
|
||||
else:
|
||||
ratio = (size[0] / float(size[1])) / aspect
|
||||
if ax == 0:
|
||||
ratio = 1.0 / ratio
|
||||
w = (tr1[1]-tr1[0]) * ratio
|
||||
d = 0.5 * (w - (tr2[1]-tr2[0]))
|
||||
self.state['targetRange'][1-ax] = [tr2[0]-d, tr2[1]+d]
|
||||
|
||||
|
||||
|
||||
|
||||
if any(changed) and disableAutoRange:
|
||||
if all(changed):
|
||||
@ -406,6 +433,8 @@ class ViewBox(GraphicsWidget):
|
||||
|
||||
self.sigStateChanged.emit(self)
|
||||
|
||||
self.target.setRect(self.mapRectFromItem(self.childGroup, self.targetRect()))
|
||||
|
||||
if update:
|
||||
self.updateMatrix(changed)
|
||||
|
||||
@ -494,7 +523,7 @@ class ViewBox(GraphicsWidget):
|
||||
scale = Point(scale)
|
||||
|
||||
if self.state['aspectLocked'] is not False:
|
||||
scale[0] = self.state['aspectLocked'] * scale[1]
|
||||
scale[0] = scale[1]
|
||||
|
||||
vr = self.targetRect()
|
||||
if center is None:
|
||||
@ -706,6 +735,7 @@ class ViewBox(GraphicsWidget):
|
||||
else:
|
||||
if self.autoRangeEnabled()[axis] is False:
|
||||
slot()
|
||||
|
||||
|
||||
self.sigStateChanged.emit(self)
|
||||
|
||||
@ -807,13 +837,17 @@ class ViewBox(GraphicsWidget):
|
||||
"""
|
||||
If the aspect ratio is locked, view scaling must always preserve the aspect ratio.
|
||||
By default, the ratio is set to 1; x and y both have the same scaling.
|
||||
This ratio can be overridden (width/height), or use None to lock in the current ratio.
|
||||
This ratio can be overridden (xScale/yScale), or use None to lock in the current ratio.
|
||||
"""
|
||||
if not lock:
|
||||
self.state['aspectLocked'] = False
|
||||
else:
|
||||
rect = self.rect()
|
||||
vr = self.viewRect()
|
||||
currentRatio = vr.width() / vr.height()
|
||||
if rect.height() == 0 or vr.width() == 0 or vr.height() == 0:
|
||||
currentRatio = 1.0
|
||||
else:
|
||||
currentRatio = (rect.width()/float(rect.height())) / (vr.width()/vr.height())
|
||||
if ratio is None:
|
||||
ratio = currentRatio
|
||||
self.state['aspectLocked'] = ratio
|
||||
@ -1092,10 +1126,10 @@ class ViewBox(GraphicsWidget):
|
||||
xr = item.dataBounds(0, frac=frac[0], orthoRange=orthoRange[0])
|
||||
yr = item.dataBounds(1, frac=frac[1], orthoRange=orthoRange[1])
|
||||
pxPad = 0 if not hasattr(item, 'pixelPadding') else item.pixelPadding()
|
||||
if xr is None or xr == (None, None) or np.isnan(xr).any() or np.isinf(xr).any():
|
||||
if xr is None or (xr[0] is None and xr[1] is None) or np.isnan(xr).any() or np.isinf(xr).any():
|
||||
useX = False
|
||||
xr = (0,0)
|
||||
if yr is None or yr == (None, None) or np.isnan(yr).any() or np.isinf(yr).any():
|
||||
if yr is None or (yr[0] is None and yr[1] is None) or np.isnan(yr).any() or np.isinf(yr).any():
|
||||
useY = False
|
||||
yr = (0,0)
|
||||
|
||||
@ -1194,32 +1228,41 @@ class ViewBox(GraphicsWidget):
|
||||
if changed is None:
|
||||
changed = [False, False]
|
||||
changed = list(changed)
|
||||
#print "udpateMatrix:"
|
||||
#print " range:", self.range
|
||||
tr = self.targetRect()
|
||||
bounds = self.rect() #boundingRect()
|
||||
#print bounds
|
||||
bounds = self.rect()
|
||||
|
||||
## set viewRect, given targetRect and possibly aspect ratio constraint
|
||||
if self.state['aspectLocked'] is False or bounds.height() == 0:
|
||||
aspect = self.state['aspectLocked']
|
||||
if aspect is False or bounds.height() == 0:
|
||||
self.state['viewRange'] = [self.state['targetRange'][0][:], self.state['targetRange'][1][:]]
|
||||
else:
|
||||
viewRatio = bounds.width() / bounds.height()
|
||||
targetRatio = self.state['aspectLocked'] * tr.width() / tr.height()
|
||||
## aspect is (widget w/h) / (view range w/h)
|
||||
|
||||
## This is the view range aspect ratio we have requested
|
||||
targetRatio = tr.width() / tr.height()
|
||||
## This is the view range aspect ratio we need to obey aspect constraint
|
||||
viewRatio = (bounds.width() / bounds.height()) / aspect
|
||||
|
||||
if targetRatio > viewRatio:
|
||||
## target is wider than view
|
||||
dy = 0.5 * (tr.width() / (self.state['aspectLocked'] * viewRatio) - tr.height())
|
||||
## view range needs to be taller than target
|
||||
dy = 0.5 * (tr.width() / viewRatio - tr.height())
|
||||
if dy != 0:
|
||||
changed[1] = True
|
||||
self.state['viewRange'] = [self.state['targetRange'][0][:], [self.state['targetRange'][1][0] - dy, self.state['targetRange'][1][1] + dy]]
|
||||
self.state['viewRange'] = [
|
||||
self.state['targetRange'][0][:],
|
||||
[self.state['targetRange'][1][0] - dy, self.state['targetRange'][1][1] + dy]
|
||||
]
|
||||
else:
|
||||
dx = 0.5 * (tr.height() * viewRatio * self.state['aspectLocked'] - tr.width())
|
||||
## view range needs to be wider than target
|
||||
dx = 0.5 * (tr.height() * viewRatio - tr.width())
|
||||
if dx != 0:
|
||||
changed[0] = True
|
||||
self.state['viewRange'] = [[self.state['targetRange'][0][0] - dx, self.state['targetRange'][0][1] + dx], self.state['targetRange'][1][:]]
|
||||
self.state['viewRange'] = [
|
||||
[self.state['targetRange'][0][0] - dx, self.state['targetRange'][0][1] + dx],
|
||||
self.state['targetRange'][1][:]
|
||||
]
|
||||
|
||||
vr = self.viewRect()
|
||||
#print " bounds:", bounds
|
||||
if vr.height() == 0 or vr.width() == 0:
|
||||
return
|
||||
scale = Point(bounds.width()/vr.width(), bounds.height()/vr.height())
|
||||
@ -1253,6 +1296,12 @@ class ViewBox(GraphicsWidget):
|
||||
p.setPen(self.border)
|
||||
#p.fillRect(bounds, QtGui.QColor(0, 0, 0))
|
||||
p.drawPath(bounds)
|
||||
|
||||
#p.setPen(fn.mkPen('r'))
|
||||
#path = QtGui.QPainterPath()
|
||||
#path.addRect(self.targetRect())
|
||||
#tr = self.mapFromView(path)
|
||||
#p.drawPath(tr)
|
||||
|
||||
def updateBackground(self):
|
||||
bg = self.state['background']
|
||||
|
@ -328,6 +328,9 @@ class MetaArray(object):
|
||||
def __div__(self, b):
|
||||
return self._binop('__div__', b)
|
||||
|
||||
def __truediv__(self, b):
|
||||
return self._binop('__truediv__', b)
|
||||
|
||||
def _binop(self, op, b):
|
||||
if isinstance(b, MetaArray):
|
||||
b = b.asarray()
|
||||
|
@ -887,6 +887,12 @@ class ObjectProxy(object):
|
||||
def __div__(self, *args):
|
||||
return self._getSpecialAttr('__div__')(*args)
|
||||
|
||||
def __truediv__(self, *args):
|
||||
return self._getSpecialAttr('__truediv__')(*args)
|
||||
|
||||
def __floordiv__(self, *args):
|
||||
return self._getSpecialAttr('__floordiv__')(*args)
|
||||
|
||||
def __mul__(self, *args):
|
||||
return self._getSpecialAttr('__mul__')(*args)
|
||||
|
||||
@ -902,6 +908,12 @@ class ObjectProxy(object):
|
||||
def __idiv__(self, *args):
|
||||
return self._getSpecialAttr('__idiv__')(*args, _callSync='off')
|
||||
|
||||
def __itruediv__(self, *args):
|
||||
return self._getSpecialAttr('__itruediv__')(*args, _callSync='off')
|
||||
|
||||
def __ifloordiv__(self, *args):
|
||||
return self._getSpecialAttr('__ifloordiv__')(*args, _callSync='off')
|
||||
|
||||
def __imul__(self, *args):
|
||||
return self._getSpecialAttr('__imul__')(*args, _callSync='off')
|
||||
|
||||
@ -914,17 +926,11 @@ class ObjectProxy(object):
|
||||
def __lshift__(self, *args):
|
||||
return self._getSpecialAttr('__lshift__')(*args)
|
||||
|
||||
def __floordiv__(self, *args):
|
||||
return self._getSpecialAttr('__pow__')(*args)
|
||||
|
||||
def __irshift__(self, *args):
|
||||
return self._getSpecialAttr('__rshift__')(*args, _callSync='off')
|
||||
return self._getSpecialAttr('__irshift__')(*args, _callSync='off')
|
||||
|
||||
def __ilshift__(self, *args):
|
||||
return self._getSpecialAttr('__lshift__')(*args, _callSync='off')
|
||||
|
||||
def __ifloordiv__(self, *args):
|
||||
return self._getSpecialAttr('__pow__')(*args, _callSync='off')
|
||||
return self._getSpecialAttr('__ilshift__')(*args, _callSync='off')
|
||||
|
||||
def __eq__(self, *args):
|
||||
return self._getSpecialAttr('__eq__')(*args)
|
||||
@ -974,6 +980,12 @@ class ObjectProxy(object):
|
||||
def __rdiv__(self, *args):
|
||||
return self._getSpecialAttr('__rdiv__')(*args)
|
||||
|
||||
def __rfloordiv__(self, *args):
|
||||
return self._getSpecialAttr('__rfloordiv__')(*args)
|
||||
|
||||
def __rtruediv__(self, *args):
|
||||
return self._getSpecialAttr('__rtruediv__')(*args)
|
||||
|
||||
def __rmul__(self, *args):
|
||||
return self._getSpecialAttr('__rmul__')(*args)
|
||||
|
||||
@ -986,9 +998,6 @@ class ObjectProxy(object):
|
||||
def __rlshift__(self, *args):
|
||||
return self._getSpecialAttr('__rlshift__')(*args)
|
||||
|
||||
def __rfloordiv__(self, *args):
|
||||
return self._getSpecialAttr('__rpow__')(*args)
|
||||
|
||||
def __rand__(self, *args):
|
||||
return self._getSpecialAttr('__rand__')(*args)
|
||||
|
||||
|
@ -190,10 +190,15 @@ class ScatterPlotWidget(QtGui.QSplitter):
|
||||
for ax in [0,1]:
|
||||
if not enum[ax]:
|
||||
continue
|
||||
for i in range(int(xy[ax].max())+1):
|
||||
imax = int(xy[ax].max()) if len(xy[ax]) > 0 else 0
|
||||
for i in range(imax+1):
|
||||
keymask = xy[ax] == i
|
||||
scatter = pg.pseudoScatter(xy[1-ax][keymask], bidir=True)
|
||||
scatter *= 0.2 / np.abs(scatter).max()
|
||||
if len(scatter) == 0:
|
||||
continue
|
||||
smax = np.abs(scatter).max()
|
||||
if smax != 0:
|
||||
scatter *= 0.2 / smax
|
||||
xy[ax][keymask] += scatter
|
||||
|
||||
if self.scatterPlot is not None:
|
||||
|
Loading…
Reference in New Issue
Block a user