copy from acq4

This commit is contained in:
Luke Campagnola 2013-07-03 11:20:49 -04:00
parent 008ca76d53
commit 8c13a3e7e3
19 changed files with 613 additions and 204 deletions

View File

@ -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)

View File

@ -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))

View File

@ -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)

View File

@ -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 = []

View File

@ -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__'))

View File

@ -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

View File

@ -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]

View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -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():

View File

@ -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 &quot;Max Traces&quot; 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">

View File

@ -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))

View File

@ -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))

View File

@ -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'))

View File

@ -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']

View File

@ -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()

View File

@ -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)

View File

@ -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: