merge from inp, removed print statement

This commit is contained in:
Luke Campagnola 2013-02-24 11:37:17 -05:00
commit 21dff0525a
13 changed files with 133 additions and 30 deletions

View File

@ -17,7 +17,7 @@ Pyqtgraph makes it very easy to visualize data from the command line. Observe::
import pyqtgraph as pg import pyqtgraph as pg
pg.plot(data) # data can be a list of values or a numpy array pg.plot(data) # data can be a list of values or a numpy array
The example above would open a window displaying a line plot of the data given. The call to :func:`pg.plot <pyqtgraph.plot>` returns a handle to the :class:`plot widget <pyqtgraph.PlotWidget>` that is created, allowing more data to be added to the same window. The example above would open a window displaying a line plot of the data given. The call to :func:`pg.plot <pyqtgraph.plot>` returns a handle to the :class:`plot widget <pyqtgraph.PlotWidget>` that is created, allowing more data to be added to the same window. **Note:** interactive plotting from the python prompt is only available with PyQt; PySide does not run the Qt event loop while the interactive prompt is running. If you wish to use pyqtgraph interactively with PySide, see the 'console' :ref:`example <examples>`.
Further examples:: Further examples::

View File

@ -66,6 +66,12 @@ Signals, Slots, and Events
[ to be continued.. please post a request on the pyqtgraph forum if you'd like to read more ] [ to be continued.. please post a request on the pyqtgraph forum if you'd like to read more ]
Qt detects and reacts to user interaction by executing its *event loop*.
- what happens in the event loop?
- when do I need to use QApplication.exec_() ?
- what control do I have over event loop execution? (QApplication.processEvents)
GraphicsView and GraphicsItems GraphicsView and GraphicsItems
------------------------------ ------------------------------
@ -79,8 +85,8 @@ Mouse and Keyboard Input
------------------------ ------------------------
QTimer, the Event Loop, and Multi-Threading QTimer, Multi-Threading
------------------------------------------- -----------------------
Multi-threading vs Multi-processing in Qt Multi-threading vs Multi-processing in Qt

View File

@ -62,7 +62,7 @@ w.addItem(p3)
## Animated example ## Animated example
## compute surface vertex data ## compute surface vertex data
cols = 100 cols = 90
rows = 100 rows = 100
x = np.linspace(-8, 8, cols+1).reshape(cols+1,1) x = np.linspace(-8, 8, cols+1).reshape(cols+1,1)
y = np.linspace(-8, 8, rows+1).reshape(1,rows+1) y = np.linspace(-8, 8, rows+1).reshape(1,rows+1)

View File

@ -1,4 +1,15 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""
This example demonstrates the use of ImageView, which is a high-level widget for
displaying and analyzing 2D and 3D data. ImageView provides:
1. A zoomable region (ViewBox) for displaying the image
2. A combination histogram and gradient editor (HistogramLUTItem) for
controlling the visual appearance of the image
3. A timeline for selecting the currently displayed frame (for 3D data only).
4. Tools for very basic analysis of image data (see ROI and Norm buttons)
"""
## Add path to library (just for examples; you do not need this) ## Add path to library (just for examples; you do not need this)
import initExample import initExample
@ -22,9 +33,6 @@ img = img[np.newaxis,:,:]
decay = np.exp(-np.linspace(0,0.3,100))[:,np.newaxis,np.newaxis] decay = np.exp(-np.linspace(0,0.3,100))[:,np.newaxis,np.newaxis]
data = np.random.normal(size=(100, 200, 200)) data = np.random.normal(size=(100, 200, 200))
data += img * decay data += img * decay
#for i in range(data.shape[0]):
#data[i] += 10*exp(-(2.*i)/data.shape[0])
data += 2 data += 2
## Add time-varying signal ## Add time-varying signal
@ -37,7 +45,7 @@ sig = sig[:,np.newaxis,np.newaxis] * 3
data[:,50:60,50:60] += sig data[:,50:60,50:60] += sig
## Display the data ## Display the data and assign each frame a time value from 1.0 to 3.0
imv.setImage(data, xvals=np.linspace(1., 3., data.shape[0])) imv.setImage(data, xvals=np.linspace(1., 3., data.shape[0]))
## Start Qt event loop unless running in interactive mode. ## Start Qt event loop unless running in interactive mode.

View File

@ -66,6 +66,7 @@ for i in range(0, 5):
## Test large numbers ## Test large numbers
curve = pw3.plot(np.random.normal(size=100)*1e0, clickable=True) curve = pw3.plot(np.random.normal(size=100)*1e0, clickable=True)
curve.curve.setClickable(True)
curve.setPen('w') ## white pen curve.setPen('w') ## white pen
curve.setShadowPen(pg.mkPen((70,70,30), width=6, cosmetic=True)) curve.setShadowPen(pg.mkPen((70,70,30), width=6, cosmetic=True))

View File

@ -22,7 +22,7 @@ class PlotData(object):
self.maxVals = {} ## cache for max/min self.maxVals = {} ## cache for max/min
self.minVals = {} self.minVals = {}
def addFields(self, fields): def addFields(self, **fields):
for f in fields: for f in fields:
if f not in self.fields: if f not in self.fields:
self.fields[f] = None self.fields[f] = None

View File

@ -212,6 +212,19 @@ class Dock(QtGui.QWidget, DockDrop):
def __repr__(self): def __repr__(self):
return "<Dock %s %s>" % (self.name(), self.stretch()) return "<Dock %s %s>" % (self.name(), self.stretch())
## PySide bug: We need to explicitly redefine these methods
## or else drag/drop events will not be delivered.
def dragEnterEvent(self, *args):
DockDrop.dragEnterEvent(self, *args)
def dragMoveEvent(self, *args):
DockDrop.dragMoveEvent(self, *args)
def dragLeaveEvent(self, *args):
DockDrop.dragLeaveEvent(self, *args)
def dropEvent(self, *args):
DockDrop.dropEvent(self, *args)
class DockLabel(VerticalLabel): class DockLabel(VerticalLabel):

View File

@ -301,5 +301,19 @@ class DockArea(Container, QtGui.QWidget, DockDrop):
self.home.removeTempArea(self) self.home.removeTempArea(self)
#self.close() #self.close()
## PySide bug: We need to explicitly redefine these methods
## or else drag/drop events will not be delivered.
def dragEnterEvent(self, *args):
DockDrop.dragEnterEvent(self, *args)
def dragMoveEvent(self, *args):
DockDrop.dragMoveEvent(self, *args)
def dragLeaveEvent(self, *args):
DockDrop.dragLeaveEvent(self, *args)
def dropEvent(self, *args):
DockDrop.dropEvent(self, *args)

View File

@ -85,7 +85,40 @@ class ArrowItem(QtGui.QGraphicsPathItem):
p.setRenderHint(QtGui.QPainter.Antialiasing) p.setRenderHint(QtGui.QPainter.Antialiasing)
QtGui.QGraphicsPathItem.paint(self, p, *args) QtGui.QGraphicsPathItem.paint(self, p, *args)
#p.setPen(fn.mkPen('r'))
#p.setBrush(fn.mkBrush(None))
#p.drawRect(self.boundingRect())
def shape(self): def shape(self):
#if not self.opts['pxMode']: #if not self.opts['pxMode']:
#return QtGui.QGraphicsPathItem.shape(self) #return QtGui.QGraphicsPathItem.shape(self)
return self.path return self.path
## dataBounds and pixelPadding methods are provided to ensure ViewBox can
## properly auto-range
def dataBounds(self, ax, frac, orthoRange=None):
pw = 0
pen = self.pen()
if not pen.isCosmetic():
pw = pen.width() * 0.7072
if self.opts['pxMode']:
return [0,0]
else:
br = self.boundingRect()
if ax == 0:
return [br.left()-pw, br.right()+pw]
else:
return [br.top()-pw, br.bottom()+pw]
def pixelPadding(self):
pad = 0
if self.opts['pxMode']:
br = self.boundingRect()
pad += (br.width()**2 + br.height()**2) ** 0.5
pen = self.pen()
if pen.isCosmetic():
pad += max(1, pen.width()) * 0.7072
return pad

View File

@ -336,7 +336,7 @@ class ViewBox(GraphicsWidget):
print("make qrectf failed:", self.state['targetRange']) print("make qrectf failed:", self.state['targetRange'])
raise raise
def setRange(self, rect=None, xRange=None, yRange=None, padding=0.02, update=True, disableAutoRange=True): def setRange(self, rect=None, xRange=None, yRange=None, padding=None, update=True, disableAutoRange=True):
""" """
Set the visible range of the ViewBox. Set the visible range of the ViewBox.
Must specify at least one of *range*, *xRange*, or *yRange*. Must specify at least one of *range*, *xRange*, or *yRange*.
@ -347,7 +347,8 @@ class ViewBox(GraphicsWidget):
*xRange* (min,max) The range that should be visible along the x-axis. *xRange* (min,max) The range that should be visible along the x-axis.
*yRange* (min,max) The range that should be visible along the y-axis. *yRange* (min,max) The range that should be visible along the y-axis.
*padding* (float) Expand the view by a fraction of the requested range. *padding* (float) Expand the view by a fraction of the requested range.
By default, this value is 0.02 (2%) By default, this value is set between 0.02 and 0.1 depending on
the size of the ViewBox.
============= ===================================================================== ============= =====================================================================
""" """
@ -367,6 +368,10 @@ class ViewBox(GraphicsWidget):
changed = [False, False] changed = [False, False]
for ax, range in changes.items(): for ax, range in changes.items():
if padding is None:
xpad = self.suggestPadding(ax)
else:
xpad = padding
mn = min(range) mn = min(range)
mx = max(range) mx = max(range)
if mn == mx: ## If we requested 0 range, try to preserve previous scale. Otherwise just pick an arbitrary scale. if mn == mx: ## If we requested 0 range, try to preserve previous scale. Otherwise just pick an arbitrary scale.
@ -375,11 +380,11 @@ class ViewBox(GraphicsWidget):
dy = 1 dy = 1
mn -= dy*0.5 mn -= dy*0.5
mx += dy*0.5 mx += dy*0.5
padding = 0.0 xpad = 0.0
if any(np.isnan([mn, mx])) or any(np.isinf([mn, mx])): if any(np.isnan([mn, mx])) or any(np.isinf([mn, mx])):
raise Exception("Not setting range [%s, %s]" % (str(mn), str(mx))) raise Exception("Not setting range [%s, %s]" % (str(mn), str(mx)))
p = (mx-mn) * padding p = (mx-mn) * xpad
mn -= p mn -= p
mx += p mx += p
@ -412,34 +417,53 @@ class ViewBox(GraphicsWidget):
elif changed[1] and self.state['autoVisibleOnly'][0]: elif changed[1] and self.state['autoVisibleOnly'][0]:
self.updateAutoRange() self.updateAutoRange()
def setYRange(self, min, max, padding=0.02, update=True): def setYRange(self, min, max, padding=None, update=True):
""" """
Set the visible Y range of the view to [*min*, *max*]. Set the visible Y range of the view to [*min*, *max*].
The *padding* argument causes the range to be set larger by the fraction specified. The *padding* argument causes the range to be set larger by the fraction specified.
(by default, this value is between 0.02 and 0.1 depending on the size of the ViewBox)
""" """
self.setRange(yRange=[min, max], update=update, padding=padding) self.setRange(yRange=[min, max], update=update, padding=padding)
def setXRange(self, min, max, padding=0.02, update=True): def setXRange(self, min, max, padding=None, update=True):
""" """
Set the visible X range of the view to [*min*, *max*]. Set the visible X range of the view to [*min*, *max*].
The *padding* argument causes the range to be set larger by the fraction specified. The *padding* argument causes the range to be set larger by the fraction specified.
(by default, this value is between 0.02 and 0.1 depending on the size of the ViewBox)
""" """
self.setRange(xRange=[min, max], update=update, padding=padding) self.setRange(xRange=[min, max], update=update, padding=padding)
def autoRange(self, padding=0.02, item=None): def autoRange(self, padding=None, items=None, item=None):
""" """
Set the range of the view box to make all children visible. Set the range of the view box to make all children visible.
Note that this is not the same as enableAutoRange, which causes the view to Note that this is not the same as enableAutoRange, which causes the view to
automatically auto-range whenever its contents are changed. automatically auto-range whenever its contents are changed.
=========== ============================================================
Arguments
padding The fraction of the total data range to add on to the final
visible range. By default, this value is set between 0.02
and 0.1 depending on the size of the ViewBox.
items If specified, this is a list of items to consider when
determining the visible range.
=========== ============================================================
""" """
if item is None: if item is None:
bounds = self.childrenBoundingRect() bounds = self.childrenBoundingRect(items=items)
else: else:
print "Warning: ViewBox.autoRange(item=__) is deprecated. Use 'items' argument instead."
bounds = self.mapFromItemToView(item, item.boundingRect()).boundingRect() bounds = self.mapFromItemToView(item, item.boundingRect()).boundingRect()
if bounds is not None: if bounds is not None:
self.setRange(bounds, padding=padding) self.setRange(bounds, padding=padding)
def suggestPadding(self, axis):
l = self.width() if axis==0 else self.height()
if l > 0:
padding = np.clip(1./(l**0.5), 0.02, 0.1)
else:
padding = 0.02
return padding
def scaleBy(self, s, center=None): def scaleBy(self, s, center=None):
""" """
@ -577,12 +601,10 @@ class ViewBox(GraphicsWidget):
w2 = (targetRect[ax][1]-targetRect[ax][0]) / 2. w2 = (targetRect[ax][1]-targetRect[ax][0]) / 2.
childRange[ax] = [x-w2, x+w2] childRange[ax] = [x-w2, x+w2]
else: else:
l = self.width() if ax==0 else self.height() padding = self.suggestPadding(ax)
if l > 0: wp = (xr[1] - xr[0]) * padding
padding = np.clip(1./(l**0.5), 0.02, 0.1) childRange[ax][0] -= wp
wp = (xr[1] - xr[0]) * padding childRange[ax][1] += wp
childRange[ax][0] -= wp
childRange[ax][1] += wp
targetRect[ax] = childRange[ax] targetRect[ax] = childRange[ax]
args['xRange' if ax == 0 else 'yRange'] = targetRect[ax] args['xRange' if ax == 0 else 'yRange'] = targetRect[ax]
if len(args) == 0: if len(args) == 0:
@ -995,13 +1017,14 @@ class ViewBox(GraphicsWidget):
def childrenBounds(self, frac=None, orthoRange=(None,None)): def childrenBounds(self, frac=None, orthoRange=(None,None), items=None):
"""Return the bounding range of all children. """Return the bounding range of all children.
[[xmin, xmax], [ymin, ymax]] [[xmin, xmax], [ymin, ymax]]
Values may be None if there are no specific bounds for an axis. Values may be None if there are no specific bounds for an axis.
""" """
prof = debug.Profiler('updateAutoRange', disabled=True) prof = debug.Profiler('updateAutoRange', disabled=True)
items = self.addedItems if items is None:
items = self.addedItems
## measure pixel dimensions in view box ## measure pixel dimensions in view box
px, py = [v.length() if v is not None else 0 for v in self.childGroup.pixelVectors()] px, py = [v.length() if v is not None else 0 for v in self.childGroup.pixelVectors()]

View File

@ -206,6 +206,11 @@ class ImageView(QtGui.QWidget):
This is only needed to override the default guess. Format is:: This is only needed to override the default guess. Format is::
{'t':0, 'x':1, 'y':2, 'c':3}; {'t':0, 'x':1, 'y':2, 'c':3};
*pos* Change the position of the displayed image
*scale* Change the scale of the displayed image
*transform* Set the transform of the dispalyed image. This option overrides *pos*
and *scale*.
============== ======================================================================= ============== =======================================================================
""" """
prof = debug.Profiler('ImageView.setImage', disabled=True) prof = debug.Profiler('ImageView.setImage', disabled=True)

View File

@ -436,7 +436,7 @@ class MeshData(object):
elif self._faceColorsIndexedByFaces is not None: elif self._faceColorsIndexedByFaces is not None:
names.append('_faceColorsIndexedByFaces') names.append('_faceColorsIndexedByFaces')
state = {n:getattr(self, n) for n in names} state = dict([(n,getattr(self, n)) for n in names])
return pickle.dumps(state) return pickle.dumps(state)
def restore(self, state): def restore(self, state):

View File

@ -127,8 +127,8 @@ class GLSurfacePlotItem(GLMeshItem):
def generateFaces(self): def generateFaces(self):
cols = self._z.shape[0]-1 cols = self._z.shape[1]-1
rows = self._z.shape[1]-1 rows = self._z.shape[0]-1
faces = np.empty((cols*rows*2, 3), dtype=np.uint) faces = np.empty((cols*rows*2, 3), dtype=np.uint)
rowtemplate1 = np.arange(cols).reshape(cols, 1) + np.array([[0, 1, cols+1]]) rowtemplate1 = np.arange(cols).reshape(cols, 1) + np.array([[0, 1, cols+1]])
rowtemplate2 = np.arange(cols).reshape(cols, 1) + np.array([[cols+1, 1, cols+2]]) rowtemplate2 = np.arange(cols).reshape(cols, 1) + np.array([[cols+1, 1, cols+2]])