From 412e1d2ec8c907430a1e2e21d4ceec1c26997213 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Wed, 20 Feb 2013 11:13:50 -0500 Subject: [PATCH] doc updates ViewBox: made padding more consistent for all auto-ranging methods, deprecated autoRange(item=) in favor of autoRange(items=) --- doc/source/how_to_use.rst | 2 +- doc/source/qtcrashcourse.rst | 10 +++- examples/ImageView.py | 16 +++++-- examples/PlotWidget.py | 1 + pyqtgraph/PlotData.py | 2 +- pyqtgraph/graphicsItems/ViewBox/ViewBox.py | 55 +++++++++++++++------- 6 files changed, 62 insertions(+), 24 deletions(-) diff --git a/doc/source/how_to_use.rst b/doc/source/how_to_use.rst index 53a3d2b0..0e00af59 100644 --- a/doc/source/how_to_use.rst +++ b/doc/source/how_to_use.rst @@ -17,7 +17,7 @@ Pyqtgraph makes it very easy to visualize data from the command line. Observe:: import pyqtgraph as pg 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 ` returns a handle to the :class:`plot widget ` 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 ` returns a handle to the :class:`plot widget ` 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 `. Further examples:: diff --git a/doc/source/qtcrashcourse.rst b/doc/source/qtcrashcourse.rst index ca2da797..23a561b9 100644 --- a/doc/source/qtcrashcourse.rst +++ b/doc/source/qtcrashcourse.rst @@ -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 ] +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 ------------------------------ @@ -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 diff --git a/examples/ImageView.py b/examples/ImageView.py index 5edae00b..f11ce0f7 100644 --- a/examples/ImageView.py +++ b/examples/ImageView.py @@ -1,4 +1,15 @@ # -*- 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) import initExample @@ -22,9 +33,6 @@ img = img[np.newaxis,:,:] decay = np.exp(-np.linspace(0,0.3,100))[:,np.newaxis,np.newaxis] data = np.random.normal(size=(100, 200, 200)) data += img * decay - -#for i in range(data.shape[0]): - #data[i] += 10*exp(-(2.*i)/data.shape[0]) data += 2 ## Add time-varying signal @@ -37,7 +45,7 @@ sig = sig[:,np.newaxis,np.newaxis] * 3 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])) ## Start Qt event loop unless running in interactive mode. diff --git a/examples/PlotWidget.py b/examples/PlotWidget.py index 3cca8f7a..2aa118f2 100644 --- a/examples/PlotWidget.py +++ b/examples/PlotWidget.py @@ -66,6 +66,7 @@ for i in range(0, 5): ## Test large numbers curve = pw3.plot(np.random.normal(size=100)*1e0, clickable=True) +curve.curve.setClickable(True) curve.setPen('w') ## white pen curve.setShadowPen(pg.mkPen((70,70,30), width=6, cosmetic=True)) diff --git a/pyqtgraph/PlotData.py b/pyqtgraph/PlotData.py index 18531c14..0bf13ca8 100644 --- a/pyqtgraph/PlotData.py +++ b/pyqtgraph/PlotData.py @@ -22,7 +22,7 @@ class PlotData(object): self.maxVals = {} ## cache for max/min self.minVals = {} - def addFields(self, fields): + def addFields(self, **fields): for f in fields: if f not in self.fields: self.fields[f] = None diff --git a/pyqtgraph/graphicsItems/ViewBox/ViewBox.py b/pyqtgraph/graphicsItems/ViewBox/ViewBox.py index ce1d61f9..b562132c 100644 --- a/pyqtgraph/graphicsItems/ViewBox/ViewBox.py +++ b/pyqtgraph/graphicsItems/ViewBox/ViewBox.py @@ -336,7 +336,7 @@ class ViewBox(GraphicsWidget): print("make qrectf failed:", self.state['targetRange']) 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. 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. *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. - 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] for ax, range in changes.items(): + if padding is None: + xpad = self.suggestPadding(ax) + else: + xpad = padding mn = min(range) mx = max(range) 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 mn -= 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])): raise Exception("Not setting range [%s, %s]" % (str(mn), str(mx))) - p = (mx-mn) * padding + p = (mx-mn) * xpad mn -= p mx += p @@ -412,34 +417,53 @@ class ViewBox(GraphicsWidget): elif changed[1] and self.state['autoVisibleOnly'][0]: 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*]. 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) - 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*]. 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) - 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. Note that this is not the same as enableAutoRange, which causes the view to 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: - bounds = self.childrenBoundingRect() + bounds = self.childrenBoundingRect(items=items) else: + print "Warning: ViewBox.autoRange(item=__) is deprecated. Use 'items' argument instead." bounds = self.mapFromItemToView(item, item.boundingRect()).boundingRect() if bounds is not None: 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): """ @@ -577,12 +601,10 @@ class ViewBox(GraphicsWidget): w2 = (targetRect[ax][1]-targetRect[ax][0]) / 2. childRange[ax] = [x-w2, x+w2] else: - l = self.width() if ax==0 else self.height() - if l > 0: - padding = np.clip(1./(l**0.5), 0.02, 0.1) - wp = (xr[1] - xr[0]) * padding - childRange[ax][0] -= wp - childRange[ax][1] += wp + padding = self.suggestPadding(ax) + wp = (xr[1] - xr[0]) * padding + childRange[ax][0] -= wp + childRange[ax][1] += wp targetRect[ax] = childRange[ax] args['xRange' if ax == 0 else 'yRange'] = targetRect[ax] 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. [[xmin, xmax], [ymin, ymax]] Values may be None if there are no specific bounds for an axis. """ prof = debug.Profiler('updateAutoRange', disabled=True) - items = self.addedItems + if items is None: + items = self.addedItems ## measure pixel dimensions in view box px, py = [v.length() if v is not None else 0 for v in self.childGroup.pixelVectors()]