From 797a8c0f08362d9d0c18a9d72a9ae3503bebacb3 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Sun, 8 Dec 2013 09:38:43 -0500 Subject: [PATCH 1/5] Implementing user-defined limits for ViewBox --- pyqtgraph/graphicsItems/ViewBox/ViewBox.py | 82 +++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/pyqtgraph/graphicsItems/ViewBox/ViewBox.py b/pyqtgraph/graphicsItems/ViewBox/ViewBox.py index 70012ec4..845efe43 100644 --- a/pyqtgraph/graphicsItems/ViewBox/ViewBox.py +++ b/pyqtgraph/graphicsItems/ViewBox/ViewBox.py @@ -118,6 +118,15 @@ class ViewBox(GraphicsWidget): 'wheelScaleFactor': -1.0 / 8.0, 'background': None, + + # Limits + 'limits': { + 'xRange': [None, None], # Maximum and minimum visible X values + 'yRange': [None, None], # Maximum and minimum visible Y values + 'minRange': [None, None], # Minimum allowed range for both axes + 'maxRange': [None, None], # Maximum allowed range for both axes + } + } self._updatingRange = False ## Used to break recursive loops. See updateAutoRange. self._itemBoundsCache = weakref.WeakKeyDictionary() @@ -571,6 +580,40 @@ class ViewBox(GraphicsWidget): else: padding = 0.02 return padding + + def setLimits(self, **kwds): + """ + Set limits that constrain the possible view ranges. + + =========== ============================================================ + Arguments + xRange (min, max) limits for x-axis range + yRange (min, max) limits for y-axis range + minRange (x, y) minimum allowed span + maxRange (x, y) maximum allowed span + xMin Minimum allowed x-axis range + xMax Maximum allowed x-axis range + yMin Minimum allowed y-axis range + yMax Maximum allowed y-axis range + =========== ============================================================ + """ + for kwd in ['xRange', 'yRange', 'minRange', 'maxRange']: + if kwd in kwds and self.state['limits'][kwd] != kwds[kwd]: + self.state['limits'][kwd] = kwds[kwd] + update = True + for axis in [0,1]: + for mnmx in [0,1]: + kwd = [['xMin', 'xMax'], ['yMin', 'yMax']][axis][mnmx] + lname = ['xRange', 'yRange'][axis] + if kwd in kwds and self.state['limits'][lname][mnmx] != kwds[kwd]: + self.state['limits'][lname][mnmx] = kwds[kwd] + update = True + + if update: + self.updateViewRange() + + + def scaleBy(self, s=None, center=None, x=None, y=None): """ @@ -1351,7 +1394,6 @@ class ViewBox(GraphicsWidget): # then make the entire target range visible ax = 0 if targetRatio > viewRatio else 1 - #### these should affect viewRange, not targetRange! if ax == 0: ## view range needs to be taller than target dy = 0.5 * (tr.width() / viewRatio - tr.height()) @@ -1364,6 +1406,44 @@ class ViewBox(GraphicsWidget): if dx != 0: changed[0] = True viewRange[0] = [self.state['targetRange'][0][0] - dx, self.state['targetRange'][0][1] + dx] + + # check for any requested limits + rng = (self.state['limits']['xRange'], self.state['limits']['yRange']) + minRng = self.state['limits']['minRange'] + maxRng = self.state['limits']['maxRange'] + + for axis in [0, 1]: + # max range cannot be larger than bounds, if they are given + if rng[axis][0] is not None and rng[axis][1] is not None: + maxRng[axis] = min(maxRng[axis], rng[axis][1]-rng[axis][0]) + + diff = viewRange[axis][1] - viewRange[axis][0] + print axis, diff, maxRng[axis] + if maxRng[axis] is not None and diff > maxRng[axis]: + delta = maxRng[axis] - diff + changed[axis] = True + elif minRng[axis] is not None and diff < minRng[axis]: + delta = minRng[axis] - diff + changed[axis] = True + else: + delta = 0 + + viewRange[axis][0] -= diff/2. + viewRange[axis][1] += diff/2. + print viewRange + + mn, mx = rng[axis] + if mn is not None and viewRange[axis][0] < mn: + delta = mn - viewRange[axis][0] + viewRange[axis][0] += delta + viewRange[axis][1] += delta + elif mx is not None and viewRange[axis][1] > mx: + delta = mx - viewRange[axis][1] + viewRange[axis][0] += delta + viewRange[axis][1] += delta + + + changed = [(viewRange[i][0] != self.state['viewRange'][i][0]) and (viewRange[i][1] != self.state['viewRange'][i][1]) for i in (0,1)] self.state['viewRange'] = viewRange From b0cafce3b4129a72125be13d12fef5387d2d3949 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Thu, 30 Jan 2014 10:50:07 -0500 Subject: [PATCH 2/5] Basic view limits appear to be working. --- examples/SimplePlot.py | 6 +++-- examples/ViewLimits.py | 15 ++++++++++++ pyqtgraph/graphicsItems/ViewBox/ViewBox.py | 28 +++++++++++++++------- 3 files changed, 38 insertions(+), 11 deletions(-) create mode 100644 examples/ViewLimits.py diff --git a/examples/SimplePlot.py b/examples/SimplePlot.py index f572743a..cacdbbdc 100644 --- a/examples/SimplePlot.py +++ b/examples/SimplePlot.py @@ -2,13 +2,15 @@ import initExample ## Add path to library (just for examples; you do not need th from pyqtgraph.Qt import QtGui, QtCore import pyqtgraph as pg +import pyqtgraph.exporters import numpy as np plt = pg.plot(np.random.normal(size=100), title="Simplest possible plotting example") plt.getAxis('bottom').setTicks([[(x*20, str(x*20)) for x in range(6)]]) -## Start Qt event loop unless running in interactive mode or using pyside. -ex = pg.exporters.SVGExporter.SVGExporter(plt.plotItem.scene()) + +ex = pg.exporters.SVGExporter(plt.plotItem.scene()) ex.export('/home/luke/tmp/test.svg') +## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': import sys if sys.flags.interactive != 1 or not hasattr(QtCore, 'PYQT_VERSION'): diff --git a/examples/ViewLimits.py b/examples/ViewLimits.py new file mode 100644 index 00000000..c08bf77c --- /dev/null +++ b/examples/ViewLimits.py @@ -0,0 +1,15 @@ +import initExample ## Add path to library (just for examples; you do not need this) + +from pyqtgraph.Qt import QtGui, QtCore +import pyqtgraph as pg +import numpy as np + +plt = pg.plot(np.random.normal(size=100), title="View limit example") +plt.centralWidget.vb.setLimits(xRange=[-100, 100], minRange=[0.1, None], maxRange=[50, None]) + + +## Start Qt event loop unless running in interactive mode or using pyside. +if __name__ == '__main__': + import sys + if sys.flags.interactive != 1 or not hasattr(QtCore, 'PYQT_VERSION'): + pg.QtGui.QApplication.exec_() diff --git a/pyqtgraph/graphicsItems/ViewBox/ViewBox.py b/pyqtgraph/graphicsItems/ViewBox/ViewBox.py index 845efe43..d4467286 100644 --- a/pyqtgraph/graphicsItems/ViewBox/ViewBox.py +++ b/pyqtgraph/graphicsItems/ViewBox/ViewBox.py @@ -597,6 +597,8 @@ class ViewBox(GraphicsWidget): yMax Maximum allowed y-axis range =========== ============================================================ """ + update = False + for kwd in ['xRange', 'yRange', 'minRange', 'maxRange']: if kwd in kwds and self.state['limits'][kwd] != kwds[kwd]: self.state['limits'][kwd] = kwds[kwd] @@ -1409,16 +1411,24 @@ class ViewBox(GraphicsWidget): # check for any requested limits rng = (self.state['limits']['xRange'], self.state['limits']['yRange']) - minRng = self.state['limits']['minRange'] - maxRng = self.state['limits']['maxRange'] + minRng = self.state['limits']['minRange'][:] + maxRng = self.state['limits']['maxRange'][:] for axis in [0, 1]: + if rng[axis][0] is None and rng[axis][1] is None and minRng[axis] is None and maxRng[axis] is None: + continue + # max range cannot be larger than bounds, if they are given if rng[axis][0] is not None and rng[axis][1] is not None: - maxRng[axis] = min(maxRng[axis], rng[axis][1]-rng[axis][0]) + if maxRng[axis] is not None: + maxRng[axis] = min(maxRng[axis], rng[axis][1]-rng[axis][0]) + else: + maxRng[axis] = rng[axis][1]-rng[axis][0] + + #print "\nLimits for axis %d: range=%s min=%s max=%s" % (axis, rng[axis], minRng[axis], maxRng[axis]) + #print "Starting range:", viewRange[axis] diff = viewRange[axis][1] - viewRange[axis][0] - print axis, diff, maxRng[axis] if maxRng[axis] is not None and diff > maxRng[axis]: delta = maxRng[axis] - diff changed[axis] = True @@ -1428,9 +1438,10 @@ class ViewBox(GraphicsWidget): else: delta = 0 - viewRange[axis][0] -= diff/2. - viewRange[axis][1] += diff/2. - print viewRange + viewRange[axis][0] -= delta/2. + viewRange[axis][1] += delta/2. + + #print "after applying min/max:", viewRange[axis] mn, mx = rng[axis] if mn is not None and viewRange[axis][0] < mn: @@ -1442,8 +1453,7 @@ class ViewBox(GraphicsWidget): viewRange[axis][0] += delta viewRange[axis][1] += delta - - + #print "after applying edge limits:", viewRange[axis] changed = [(viewRange[i][0] != self.state['viewRange'][i][0]) and (viewRange[i][1] != self.state['viewRange'][i][1]) for i in (0,1)] self.state['viewRange'] = viewRange From d0ed3ba2457d5cb10cc80befbf1cd0734b89c441 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Fri, 31 Jan 2014 13:04:47 -0500 Subject: [PATCH 3/5] Removed duplicate limit-setting arguments Renamed args for clarity, improved documentation Fixed interaction bugs - zooming works correctly when view is against limit - no more phantom target range; target is reset during mouse interaction. --- examples/ViewLimits.py | 2 +- pyqtgraph/graphicsItems/GraphicsLayout.py | 9 +++ pyqtgraph/graphicsItems/ViewBox/ViewBox.py | 86 ++++++++++++++-------- 3 files changed, 66 insertions(+), 31 deletions(-) diff --git a/examples/ViewLimits.py b/examples/ViewLimits.py index c08bf77c..c8f0dd21 100644 --- a/examples/ViewLimits.py +++ b/examples/ViewLimits.py @@ -5,7 +5,7 @@ import pyqtgraph as pg import numpy as np plt = pg.plot(np.random.normal(size=100), title="View limit example") -plt.centralWidget.vb.setLimits(xRange=[-100, 100], minRange=[0.1, None], maxRange=[50, None]) +plt.centralWidget.vb.setLimits(xMin=-20, xMax=120, minXRange=5, maxXRange=100) ## Start Qt event loop unless running in interactive mode or using pyside. diff --git a/pyqtgraph/graphicsItems/GraphicsLayout.py b/pyqtgraph/graphicsItems/GraphicsLayout.py index a4016522..b8325736 100644 --- a/pyqtgraph/graphicsItems/GraphicsLayout.py +++ b/pyqtgraph/graphicsItems/GraphicsLayout.py @@ -31,6 +31,15 @@ class GraphicsLayout(GraphicsWidget): #ret = GraphicsWidget.resizeEvent(self, ev) #print self.pos(), self.mapToDevice(self.rect().topLeft()) #return ret + + def setBorder(self, *args, **kwds): + """ + Set the pen used to draw border between cells. + + See :func:`mkPen ` for arguments. + """ + self.border = fn.mkPen(*args, **kwds) + self.update() def nextRow(self): """Advance to next row for automatic item placement""" diff --git a/pyqtgraph/graphicsItems/ViewBox/ViewBox.py b/pyqtgraph/graphicsItems/ViewBox/ViewBox.py index d4467286..0fe6cd53 100644 --- a/pyqtgraph/graphicsItems/ViewBox/ViewBox.py +++ b/pyqtgraph/graphicsItems/ViewBox/ViewBox.py @@ -121,10 +121,10 @@ class ViewBox(GraphicsWidget): # Limits 'limits': { - 'xRange': [None, None], # Maximum and minimum visible X values - 'yRange': [None, None], # Maximum and minimum visible Y values - 'minRange': [None, None], # Minimum allowed range for both axes - 'maxRange': [None, None], # Maximum allowed range for both axes + 'xLimits': [None, None], # Maximum and minimum visible X values + 'yLimits': [None, None], # Maximum and minimum visible Y values + 'xRange': [None, None], # Maximum and minimum X range + 'yRange': [None, None], # Maximum and minimum Y range } } @@ -407,6 +407,13 @@ class ViewBox(GraphicsWidget): print("make qrectf failed:", self.state['targetRange']) raise + def _resetTarget(self): + # Reset target range to exactly match current view range. + # This is used during mouse interaction to prevent unpredictable + # behavior (because the user is unaware of targetRange). + if self.state['aspectLocked'] is False: # (interferes with aspect locking) + self.state['targetRange'] = [self.state['viewRange'][0][:], self.state['viewRange'][1][:]] + def setRange(self, rect=None, xRange=None, yRange=None, padding=None, update=True, disableAutoRange=True): """ Set the visible range of the ViewBox. @@ -585,27 +592,38 @@ class ViewBox(GraphicsWidget): """ Set limits that constrain the possible view ranges. + **Panning limits**. The following arguments define the region within the + viewbox coordinate system that may be accessed by panning the view. =========== ============================================================ - Arguments - xRange (min, max) limits for x-axis range - yRange (min, max) limits for y-axis range - minRange (x, y) minimum allowed span - maxRange (x, y) maximum allowed span - xMin Minimum allowed x-axis range - xMax Maximum allowed x-axis range - yMin Minimum allowed y-axis range - yMax Maximum allowed y-axis range + xMin Minimum allowed x-axis value + xMax Maximum allowed x-axis value + yMin Minimum allowed y-axis value + yMax Maximum allowed y-axis value + =========== ============================================================ + + **Scaling limits**. These arguments prevent the view being zoomed in or + out too far. =========== ============================================================ + minXRange Minimum allowed left-to-right span across the view. + maxXRange Maximum allowed left-to-right span across the view. + minYRange Minimum allowed top-to-bottom span across the view. + maxYRange Maximum allowed top-to-bottom span across the view. + =========== ============================================================ """ update = False - for kwd in ['xRange', 'yRange', 'minRange', 'maxRange']: - if kwd in kwds and self.state['limits'][kwd] != kwds[kwd]: - self.state['limits'][kwd] = kwds[kwd] - update = True + #for kwd in ['xLimits', 'yLimits', 'minRange', 'maxRange']: + #if kwd in kwds and self.state['limits'][kwd] != kwds[kwd]: + #self.state['limits'][kwd] = kwds[kwd] + #update = True for axis in [0,1]: for mnmx in [0,1]: kwd = [['xMin', 'xMax'], ['yMin', 'yMax']][axis][mnmx] + lname = ['xLimits', 'yLimits'][axis] + if kwd in kwds and self.state['limits'][lname][mnmx] != kwds[kwd]: + self.state['limits'][lname][mnmx] = kwds[kwd] + update = True + kwd = [['minXRange', 'maxXRange'], ['minYRange', 'maxYRange']][axis][mnmx] lname = ['xRange', 'yRange'][axis] if kwd in kwds and self.state['limits'][lname][mnmx] != kwds[kwd]: self.state['limits'][lname][mnmx] = kwds[kwd] @@ -1101,6 +1119,7 @@ class ViewBox(GraphicsWidget): center = Point(fn.invertQTransform(self.childGroup.transform()).map(ev.pos())) #center = ev.pos() + self._resetTarget() self.scaleBy(s, center) self.sigRangeChangedManually.emit(self.state['mouseEnabled']) ev.accept() @@ -1158,6 +1177,7 @@ class ViewBox(GraphicsWidget): x = tr.x() if mask[0] == 1 else None y = tr.y() if mask[1] == 1 else None + self._resetTarget() self.translateBy(x=x, y=y) self.sigRangeChangedManually.emit(self.state['mouseEnabled']) elif ev.button() & QtCore.Qt.RightButton: @@ -1177,6 +1197,7 @@ class ViewBox(GraphicsWidget): y = s[1] if mouseEnabled[1] == 1 else None center = Point(tr.map(ev.buttonDownPos(QtCore.Qt.RightButton))) + self._resetTarget() self.scaleBy(x=x, y=y, center=center) self.sigRangeChangedManually.emit(self.state['mouseEnabled']) @@ -1372,9 +1393,9 @@ class ViewBox(GraphicsWidget): viewRange = [self.state['targetRange'][0][:], self.state['targetRange'][1][:]] changed = [False, False] - # Make correction for aspect ratio constraint + #-------- Make correction for aspect ratio constraint ---------- - ## aspect is (widget w/h) / (view range w/h) + # aspect is (widget w/h) / (view range w/h) aspect = self.state['aspectLocked'] # size ratio / view ratio tr = self.targetRect() bounds = self.rect() @@ -1409,25 +1430,27 @@ class ViewBox(GraphicsWidget): changed[0] = True viewRange[0] = [self.state['targetRange'][0][0] - dx, self.state['targetRange'][0][1] + dx] - # check for any requested limits - rng = (self.state['limits']['xRange'], self.state['limits']['yRange']) - minRng = self.state['limits']['minRange'][:] - maxRng = self.state['limits']['maxRange'][:] + # ----------- Make corrections for view limits ----------- + + limits = (self.state['limits']['xLimits'], self.state['limits']['yLimits']) + minRng = [self.state['limits']['xRange'][0], self.state['limits']['yRange'][0]] + maxRng = [self.state['limits']['xRange'][1], self.state['limits']['yRange'][1]] for axis in [0, 1]: - if rng[axis][0] is None and rng[axis][1] is None and minRng[axis] is None and maxRng[axis] is None: + if limits[axis][0] is None and limits[axis][1] is None and minRng[axis] is None and maxRng[axis] is None: continue # max range cannot be larger than bounds, if they are given - if rng[axis][0] is not None and rng[axis][1] is not None: + if limits[axis][0] is not None and limits[axis][1] is not None: if maxRng[axis] is not None: - maxRng[axis] = min(maxRng[axis], rng[axis][1]-rng[axis][0]) + maxRng[axis] = min(maxRng[axis], limits[axis][1]-limits[axis][0]) else: - maxRng[axis] = rng[axis][1]-rng[axis][0] + maxRng[axis] = limits[axis][1]-limits[axis][0] - #print "\nLimits for axis %d: range=%s min=%s max=%s" % (axis, rng[axis], minRng[axis], maxRng[axis]) + #print "\nLimits for axis %d: range=%s min=%s max=%s" % (axis, limits[axis], minRng[axis], maxRng[axis]) #print "Starting range:", viewRange[axis] + # Apply xRange, yRange diff = viewRange[axis][1] - viewRange[axis][0] if maxRng[axis] is not None and diff > maxRng[axis]: delta = maxRng[axis] - diff @@ -1443,19 +1466,22 @@ class ViewBox(GraphicsWidget): #print "after applying min/max:", viewRange[axis] - mn, mx = rng[axis] + # Apply xLimits, yLimits + mn, mx = limits[axis] if mn is not None and viewRange[axis][0] < mn: delta = mn - viewRange[axis][0] viewRange[axis][0] += delta viewRange[axis][1] += delta + changed[axis] = True elif mx is not None and viewRange[axis][1] > mx: delta = mx - viewRange[axis][1] viewRange[axis][0] += delta viewRange[axis][1] += delta + changed[axis] = True #print "after applying edge limits:", viewRange[axis] - changed = [(viewRange[i][0] != self.state['viewRange'][i][0]) and (viewRange[i][1] != self.state['viewRange'][i][1]) for i in (0,1)] + changed = [(viewRange[i][0] != self.state['viewRange'][i][0]) or (viewRange[i][1] != self.state['viewRange'][i][1]) for i in (0,1)] self.state['viewRange'] = viewRange # emit range change signals From bccbc2994061dc377f33cf419464f28b990450d4 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Mon, 3 Feb 2014 22:24:45 -0500 Subject: [PATCH 4/5] Bugfixes: - isosurface works for arrays with shapes > 255 - Fixed ImageItem exception building histogram when image has only one value - Fixed MeshData exception caused when vertexes have no matching faces - Fixed GLViewWidget exception handler --- CHANGELOG | 5 +++++ pyqtgraph/functions.py | 5 +---- pyqtgraph/graphicsItems/ImageItem.py | 2 ++ pyqtgraph/opengl/GLViewWidget.py | 2 +- pyqtgraph/opengl/MeshData.py | 10 ++++++---- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index dcc21eb5..15ce1536 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -48,6 +48,11 @@ pyqtgraph-0.9.9 [unreleased] - PlotCurveItem ignores clip-to-view when auto range is enabled - FillBetweenItem now forces PlotCurveItem to generate path - Fixed import errors and py3 issues in MultiPlotWidget + - Isosurface works for arrays with shapes > 255 + - Fixed ImageItem exception building histogram when image has only one value + - Fixed MeshData exception caused when vertexes have no matching faces + - Fixed GLViewWidget exception handler + pyqtgraph-0.9.8 2013-11-24 diff --git a/pyqtgraph/functions.py b/pyqtgraph/functions.py index db01b6b9..99c45606 100644 --- a/pyqtgraph/functions.py +++ b/pyqtgraph/functions.py @@ -1790,7 +1790,7 @@ def isosurface(data, level): [1, 1, 0, 2], [0, 1, 0, 2], #[9, 9, 9, 9] ## fake - ], dtype=np.ubyte) + ], dtype=np.uint16) # don't use ubyte here! This value gets added to cell index later; will need the extra precision. nTableFaces = np.array([len(f)/3 for f in triTable], dtype=np.ubyte) faceShiftTables = [None] for i in range(1,6): @@ -1889,7 +1889,6 @@ def isosurface(data, level): #profiler() if cells.shape[0] == 0: continue - #cellInds = index[(cells*ins[np.newaxis,:]).sum(axis=1)] cellInds = index[cells[:,0], cells[:,1], cells[:,2]] ## index values of cells to process for this round #profiler() @@ -1901,9 +1900,7 @@ def isosurface(data, level): #profiler() ### expensive: - #print verts.shape verts = (verts * cs[np.newaxis, np.newaxis, :]).sum(axis=2) - #vertInds = cutEdges[verts[...,0], verts[...,1], verts[...,2], verts[...,3]] ## and these are the vertex indexes we want. vertInds = cutEdges[verts] #profiler() nv = vertInds.shape[0] diff --git a/pyqtgraph/graphicsItems/ImageItem.py b/pyqtgraph/graphicsItems/ImageItem.py index 7c80859d..6da8aedc 100644 --- a/pyqtgraph/graphicsItems/ImageItem.py +++ b/pyqtgraph/graphicsItems/ImageItem.py @@ -322,6 +322,8 @@ class ImageItem(GraphicsObject): mx = stepData.max() step = np.ceil((mx-mn) / 500.) bins = np.arange(mn, mx+1.01*step, step, dtype=np.int) + if len(bins) == 0: + bins = [mn, mx] else: bins = 500 diff --git a/pyqtgraph/opengl/GLViewWidget.py b/pyqtgraph/opengl/GLViewWidget.py index d74a13ce..0516bf08 100644 --- a/pyqtgraph/opengl/GLViewWidget.py +++ b/pyqtgraph/opengl/GLViewWidget.py @@ -180,7 +180,7 @@ class GLViewWidget(QtOpenGL.QGLWidget): i.paint() except: from .. import debug - pyqtgraph.debug.printExc() + debug.printExc() msg = "Error while drawing item %s." % str(item) ver = glGetString(GL_VERSION) if ver is not None: diff --git a/pyqtgraph/opengl/MeshData.py b/pyqtgraph/opengl/MeshData.py index 3046459d..e6888c16 100644 --- a/pyqtgraph/opengl/MeshData.py +++ b/pyqtgraph/opengl/MeshData.py @@ -266,7 +266,11 @@ class MeshData(object): vertFaces = self.vertexFaces() self._vertexNormals = np.empty(self._vertexes.shape, dtype=float) for vindex in xrange(self._vertexes.shape[0]): - norms = faceNorms[vertFaces[vindex]] ## get all face normals + faces = vertFaces[vindex] + if len(faces) == 0: + self._vertexNormals[vindex] = (0,0,0) + continue + norms = faceNorms[faces] ## get all face normals norm = norms.sum(axis=0) ## sum normals norm /= (norm**2).sum()**0.5 ## and re-normalize self._vertexNormals[vindex] = norm @@ -403,12 +407,10 @@ class MeshData(object): Return list mapping each vertex index to a list of face indexes that use the vertex. """ if self._vertexFaces is None: - self._vertexFaces = [None] * len(self.vertexes()) + self._vertexFaces = [[] for i in xrange(len(self.vertexes()))] for i in xrange(self._faces.shape[0]): face = self._faces[i] for ind in face: - if self._vertexFaces[ind] is None: - self._vertexFaces[ind] = [] ## need a unique/empty list to fill self._vertexFaces[ind].append(i) return self._vertexFaces From 9093282a2a9ae0e9019dbdb46a1fccd8a5db7d06 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Tue, 4 Feb 2014 20:28:23 -0500 Subject: [PATCH 5/5] Wrap setLimits in PlotItem and PlotWidget --- pyqtgraph/graphicsItems/PlotItem/PlotItem.py | 3 ++- pyqtgraph/widgets/PlotWidget.py | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pyqtgraph/graphicsItems/PlotItem/PlotItem.py b/pyqtgraph/graphicsItems/PlotItem/PlotItem.py index 575a1599..77413fc2 100644 --- a/pyqtgraph/graphicsItems/PlotItem/PlotItem.py +++ b/pyqtgraph/graphicsItems/PlotItem/PlotItem.py @@ -69,6 +69,7 @@ class PlotItem(GraphicsWidget): :func:`setYLink `, :func:`setAutoPan `, :func:`setAutoVisible `, + :func:`setLimits `, :func:`viewRect `, :func:`viewRange `, :func:`setMouseEnabled `, @@ -195,7 +196,7 @@ class PlotItem(GraphicsWidget): ## Wrap a few methods from viewBox for m in [ 'setXRange', 'setYRange', 'setXLink', 'setYLink', 'setAutoPan', 'setAutoVisible', - 'setRange', 'autoRange', 'viewRect', 'viewRange', 'setMouseEnabled', + 'setRange', 'autoRange', 'viewRect', 'viewRange', 'setMouseEnabled', 'setLimits', 'enableAutoRange', 'disableAutoRange', 'setAspectLocked', 'invertY', 'register', 'unregister']: ## NOTE: If you update this list, please update the class docstring as well. setattr(self, m, getattr(self.vb, m)) diff --git a/pyqtgraph/widgets/PlotWidget.py b/pyqtgraph/widgets/PlotWidget.py index f9b544f5..12176c74 100644 --- a/pyqtgraph/widgets/PlotWidget.py +++ b/pyqtgraph/widgets/PlotWidget.py @@ -33,6 +33,7 @@ class PlotWidget(GraphicsView): :func:`enableAutoRange `, :func:`disableAutoRange `, :func:`setAspectLocked `, + :func:`setLimits `, :func:`register `, :func:`unregister ` @@ -52,7 +53,10 @@ class PlotWidget(GraphicsView): self.setCentralItem(self.plotItem) ## Explicitly wrap methods from plotItem ## NOTE: If you change this list, update the documentation above as well. - for m in ['addItem', 'removeItem', 'autoRange', 'clear', 'setXRange', 'setYRange', 'setRange', 'setAspectLocked', 'setMouseEnabled', 'setXLink', 'setYLink', 'enableAutoRange', 'disableAutoRange', 'register', 'unregister', 'viewRect']: + for m in ['addItem', 'removeItem', 'autoRange', 'clear', 'setXRange', + 'setYRange', 'setRange', 'setAspectLocked', 'setMouseEnabled', + 'setXLink', 'setYLink', 'enableAutoRange', 'disableAutoRange', + 'setLimits', 'register', 'unregister', 'viewRect']: setattr(self, m, getattr(self.plotItem, m)) #QtCore.QObject.connect(self.plotItem, QtCore.SIGNAL('viewChanged'), self.viewChanged) self.plotItem.sigRangeChanged.connect(self.viewRangeChanged)