diff --git a/CHANGELOG b/CHANGELOG index 4888d8d4..dd076d9d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -24,6 +24,8 @@ pyqtgraph-0.9.9 [unreleased] - Added Flowchart.sigChartChanged - ImageItem.getHistogram is more clever about constructing histograms - Added FillBetweenItem.setCurves() + - MultiPlotWidget now has setMinimumPlotHeight method and displays scroll bar + when plots do not fit inside the widget. Bugfixes: - PlotCurveItem now has correct clicking behavior--clicks within a few px @@ -44,6 +46,7 @@ pyqtgraph-0.9.9 [unreleased] - Major speedup when using ScatterPlotItem in pxMode - 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 pyqtgraph-0.9.8 2013-11-24 diff --git a/examples/MultiPlotWidget.py b/examples/MultiPlotWidget.py index 28492f64..5ab4b21d 100644 --- a/examples/MultiPlotWidget.py +++ b/examples/MultiPlotWidget.py @@ -10,11 +10,11 @@ from pyqtgraph.Qt import QtGui, QtCore import pyqtgraph as pg from pyqtgraph import MultiPlotWidget try: - from metaarray import * + from pyqtgraph.metaarray import * except: print("MultiPlot is only used with MetaArray for now (and you do not have the metaarray package)") exit() - + app = QtGui.QApplication([]) mw = QtGui.QMainWindow() mw.resize(800,800) @@ -22,7 +22,15 @@ pw = MultiPlotWidget() mw.setCentralWidget(pw) mw.show() -ma = MetaArray(random.random((3, 1000)), info=[{'name': 'Signal', 'cols': [{'name': 'Col1'}, {'name': 'Col2'}, {'name': 'Col3'}]}, {'name': 'Time', 'vals': linspace(0., 1., 1000)}]) +data = random.normal(size=(3, 1000)) * np.array([[0.1], [1e-5], [1]]) +ma = MetaArray(data, info=[ + {'name': 'Signal', 'cols': [ + {'name': 'Col1', 'units': 'V'}, + {'name': 'Col2', 'units': 'A'}, + {'name': 'Col3'}, + ]}, + {'name': 'Time', 'values': linspace(0., 1., 1000), 'units': 's'} + ]) pw.plot(ma) ## Start Qt event loop unless running in interactive mode. diff --git a/pyqtgraph/graphicsItems/MultiPlotItem.py b/pyqtgraph/graphicsItems/MultiPlotItem.py index d20467a9..be775d4a 100644 --- a/pyqtgraph/graphicsItems/MultiPlotItem.py +++ b/pyqtgraph/graphicsItems/MultiPlotItem.py @@ -7,26 +7,23 @@ Distributed under MIT/X11 license. See license.txt for more infomation. from numpy import ndarray from . import GraphicsLayout +from ..metaarray import * -try: - from metaarray import * - HAVE_METAARRAY = True -except: - #raise - HAVE_METAARRAY = False - __all__ = ['MultiPlotItem'] class MultiPlotItem(GraphicsLayout.GraphicsLayout): """ - Automaticaly generates a grid of plots from a multi-dimensional array + Automatically generates a grid of plots from a multi-dimensional array """ - + def __init__(self, *args, **kwds): + GraphicsLayout.GraphicsLayout.__init__(self, *args, **kwds) + self.plots = [] + + def plot(self, data): #self.layout.clear() - self.plots = [] - - if HAVE_METAARRAY and (hasattr(data, 'implements') and data.implements('MetaArray')): + + if hasattr(data, 'implements') and data.implements('MetaArray'): if data.ndim != 2: raise Exception("MultiPlot currently only accepts 2D MetaArray.") ic = data.infoCopy() @@ -44,21 +41,17 @@ class MultiPlotItem(GraphicsLayout.GraphicsLayout): pi.plot(data[tuple(sl)]) #self.layout.addItem(pi, i, 0) self.plots.append((pi, i, 0)) - title = None - units = None info = ic[ax]['cols'][i] - if 'title' in info: - title = info['title'] - elif 'name' in info: - title = info['name'] - if 'units' in info: - units = info['units'] - + title = info.get('title', info.get('name', None)) + units = info.get('units', None) pi.setLabel('left', text=title, units=units) - + info = ic[1-ax] + title = info.get('title', info.get('name', None)) + units = info.get('units', None) + pi.setLabel('bottom', text=title, units=units) else: raise Exception("Data type %s not (yet?) supported for MultiPlot." % type(data)) - + def close(self): for p in self.plots: p[0].close() diff --git a/pyqtgraph/widgets/MultiPlotWidget.py b/pyqtgraph/widgets/MultiPlotWidget.py index 58b71296..abad55ef 100644 --- a/pyqtgraph/widgets/MultiPlotWidget.py +++ b/pyqtgraph/widgets/MultiPlotWidget.py @@ -4,28 +4,41 @@ MultiPlotWidget.py - Convenience class--GraphicsView widget displaying a MultiP Copyright 2010 Luke Campagnola Distributed under MIT/X11 license. See license.txt for more infomation. """ - +from ..Qt import QtCore from .GraphicsView import GraphicsView from ..graphicsItems import MultiPlotItem as MultiPlotItem __all__ = ['MultiPlotWidget'] class MultiPlotWidget(GraphicsView): - """Widget implementing a graphicsView with a single PlotItem inside.""" + """Widget implementing a graphicsView with a single MultiPlotItem inside.""" def __init__(self, parent=None): + self.minPlotHeight = 50 + self.mPlotItem = MultiPlotItem.MultiPlotItem() GraphicsView.__init__(self, parent) self.enableMouse(False) - self.mPlotItem = MultiPlotItem.MultiPlotItem() self.setCentralItem(self.mPlotItem) ## Explicitly wrap methods from mPlotItem #for m in ['setData']: #setattr(self, m, getattr(self.mPlotItem, m)) + self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) def __getattr__(self, attr): ## implicitly wrap methods from plotItem if hasattr(self.mPlotItem, attr): m = getattr(self.mPlotItem, attr) if hasattr(m, '__call__'): return m - raise NameError(attr) + raise AttributeError(attr) + + def setMinimumPlotHeight(self, min): + """Set the minimum height for each sub-plot displayed. + + If the total height of all plots is greater than the height of the + widget, then a scroll bar will appear to provide access to the entire + set of plots. + """ + self.minPlotHeight = min + self.resizeEvent(None) def widgetGroupInterface(self): return (None, MultiPlotWidget.saveState, MultiPlotWidget.restoreState) @@ -43,3 +56,21 @@ class MultiPlotWidget(GraphicsView): self.mPlotItem = None self.setParent(None) GraphicsView.close(self) + + def setRange(self, *args, **kwds): + GraphicsView.setRange(self, *args, **kwds) + if self.centralWidget is not None: + r = self.range + minHeight = len(self.mPlotItem.plots) * self.minPlotHeight + if r.height() < minHeight: + r.setHeight(minHeight) + r.setWidth(r.width() - self.verticalScrollBar().width()) + self.centralWidget.setGeometry(r) + + def resizeEvent(self, ev): + if self.closed: + return + if self.autoPixelRange: + self.range = QtCore.QRectF(0, 0, self.size().width(), self.size().height()) + MultiPlotWidget.setRange(self, self.range, padding=0, disableAutoPixel=False) ## we do this because some subclasses like to redefine setRange in an incompatible way. + self.updateMatrix()