From 7be7c3f459775dd4aea6a869d9967015ef86e596 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Fri, 8 Jun 2018 08:43:46 -0700 Subject: [PATCH] More PlotItem cleanup --- pyqtgraph/graphicsItems/PlotItem/PlotItem.py | 123 +++++-------------- 1 file changed, 32 insertions(+), 91 deletions(-) diff --git a/pyqtgraph/graphicsItems/PlotItem/PlotItem.py b/pyqtgraph/graphicsItems/PlotItem/PlotItem.py index 3e5624b9..b8face5e 100644 --- a/pyqtgraph/graphicsItems/PlotItem/PlotItem.py +++ b/pyqtgraph/graphicsItems/PlotItem/PlotItem.py @@ -1,21 +1,4 @@ # -*- coding: utf-8 -*- -""" -PlotItem.py - Graphics item implementing a scalable ViewBox with plotting powers. -Copyright 2010 Luke Campagnola -Distributed under MIT/X11 license. See license.txt for more infomation. - -This class is one of the workhorses of pyqtgraph. It implements a graphics item with -plots, labels, and scales which can be viewed inside a QGraphicsScene. If you want -a widget that can be added to your GUI, see PlotWidget instead. - -This class is very heavily featured: - - Automatically creates and manages PlotCurveItems - - Fast display and update of plots - - Manages zoom/pan ViewBox, scale, and label elements - - Automatic scaling when data changes - - Control panel with a huge feature set including averaging, decimation, - display, power spectrum, svg/png export, plot linking, and more. -""" import sys import weakref import numpy as np @@ -53,17 +36,24 @@ except: HAVE_METAARRAY = False - - class PlotItem(GraphicsWidget): - - """ + """GraphicsWidget implementing a standard 2D plotting area with axes. + **Bases:** :class:`GraphicsWidget ` - Plot graphics item that can be added to any graphics scene. Implements axes, titles, and interactive viewbox. - PlotItem also provides some basic analysis functionality that may be accessed from the context menu. - Use :func:`plot() ` to create a new PlotDataItem and add it to the view. - Use :func:`addItem() ` to add any QGraphicsItem to the view. + This class provides the ViewBox-plus-axes that appear when using + :func:`pg.plot() `, :class:`PlotWidget `, + and :func:`GraphicsLayoutWidget.addPlot() `. + + It's main functionality is: + + - Manage placement of ViewBox, AxisItems, and LabelItems + - Create and manage a list of PlotDataItems displayed inside the ViewBox + - Implement a context menu with commonly used display and analysis options + + Use :func:`plot() ` to create a new PlotDataItem and + add it to the view. Use :func:`addItem() ` to + add any QGraphicsItem to the view. This class wraps several methods from its internal ViewBox: :func:`setXRange `, @@ -99,8 +89,7 @@ class PlotItem(GraphicsWidget): sigRangeChanged = QtCore.Signal(object, object) ## Emitted when the ViewBox range has changed sigYRangeChanged = QtCore.Signal(object, object) ## Emitted when the ViewBox Y range has changed sigXRangeChanged = QtCore.Signal(object, object) ## Emitted when the ViewBox X range has changed - - + lastFileDir = None def __init__(self, parent=None, name=None, labels=None, title=None, viewBox=None, axisItems=None, enableMenu=True, **kargs): @@ -133,12 +122,9 @@ class PlotItem(GraphicsWidget): ## Set up control buttons path = os.path.dirname(__file__) - #self.autoImageFile = os.path.join(path, 'auto.png') - #self.lockImageFile = os.path.join(path, 'lock.png') self.autoBtn = ButtonItem(pixmaps.getPixmap('auto'), 14, self) self.autoBtn.mode = 'auto' self.autoBtn.clicked.connect(self.autoBtnClicked) - #self.autoBtn.hide() self.buttonsHidden = False ## whether the user has requested buttons to be hidden self.mouseHovering = False @@ -186,7 +172,6 @@ class PlotItem(GraphicsWidget): self.layout.addItem(self.titleLabel, 0, 1) self.setTitle(None) ## hide - for i in range(4): self.layout.setRowPreferredHeight(i, 0) self.layout.setRowMinimumHeight(i, 0) @@ -289,8 +274,7 @@ class PlotItem(GraphicsWidget): self.setTitle(title) if len(kargs) > 0: - self.plot(**kargs) - + self.plot(**kargs) def implements(self, interface=None): return interface in ['ViewBoxWrapper'] @@ -298,12 +282,10 @@ class PlotItem(GraphicsWidget): def getViewBox(self): """Return the :class:`ViewBox ` contained within.""" return self.vb - ## Wrap a few methods from viewBox. #Important: don't use a settattr(m, getattr(self.vb, m)) as we'd be leaving the viebox alive #because we had a reference to an instance method (creating wrapper methods at runtime instead). - for m in ['setXRange', 'setYRange', 'setXLink', 'setYLink', 'setAutoPan', # NOTE: 'setAutoVisible', 'setRange', 'autoRange', 'viewRect', 'viewRange', # If you update this list, please 'setMouseEnabled', 'setLimits', 'enableAutoRange', 'disableAutoRange', # update the class docstring @@ -318,8 +300,7 @@ class PlotItem(GraphicsWidget): locals()[m] = _create_method(m) del _create_method - - + def setLogMode(self, x=None, y=None): """ Set log scaling for x and/or y axes. @@ -359,7 +340,6 @@ class PlotItem(GraphicsWidget): self.ctrl.gridAlphaSlider.setValue(v) def close(self): - #print "delete", self ## Most of this crap is needed to avoid PySide trouble. ## The problem seems to be whenever scene.clear() leads to deletion of widgets (either through proxies or qgraphicswidgets) ## the solution is to manually remove all widgets before scene.clear() is called @@ -400,7 +380,6 @@ class PlotItem(GraphicsWidget): wr.adjust(pos.x(), pos.y(), pos.x(), pos.y()) return wr - def avgToggled(self, b): if b: self.recomputeAverages() @@ -541,8 +520,7 @@ class PlotItem(GraphicsWidget): #self.plotChanged() #name = kargs.get('name', getattr(item, 'opts', {}).get('name', None)) if name is not None and hasattr(self, 'legend') and self.legend is not None: - self.legend.addItem(item, name=name) - + self.legend.addItem(item, name=name) def addDataItem(self, item, *args): print("PlotItem.addDataItem is deprecated. Use addItem instead.") @@ -573,9 +551,7 @@ class PlotItem(GraphicsWidget): self.addItem(line) if z is not None: line.setZValue(z) - return line - - + return line def removeItem(self, item): """ @@ -593,8 +569,6 @@ class PlotItem(GraphicsWidget): self.curves.remove(item) self.updateDecimation() self.updateParamList() - #item.connect(item, QtCore.SIGNAL('plotChanged'), self.plotChanged) - #item.sigPlotChanged.connect(self.plotChanged) if self.legend is not None: self.legend.removeItem(item) @@ -610,8 +584,7 @@ class PlotItem(GraphicsWidget): def clearPlots(self): for i in self.curves[:]: self.removeItem(i) - self.avgCurves = {} - + self.avgCurves = {} def plot(self, *args, **kargs): """ @@ -622,8 +595,6 @@ class PlotItem(GraphicsWidget): clear - clear all plots before displaying new data params - meta-parameters to associate with this data """ - - clear = kargs.get('clear', False) params = kargs.get('params', None) @@ -692,11 +663,9 @@ class PlotItem(GraphicsWidget): self.paramList[p] = (i.checkState() == QtCore.Qt.Checked) - - ## Qt's SVG-writing capabilities are pretty terrible. def writeSvgCurves(self, fileName=None): if fileName is None: - self._choose_filename_dialog(handler=self.writeSvg) + self._chooseFilenameDialog(handler=self.writeSvg) return if isinstance(fileName, tuple): @@ -722,12 +691,10 @@ class PlotItem(GraphicsWidget): sy *= 1000 sy *= -1 - #fh.write('\n' % (rect.left()*sx, rect.top()*sx, rect.width()*sy, rect.height()*sy)) fh.write('\n') fh.write('\n' % (rect.left()*sx, rect.right()*sx)) fh.write('\n' % (rect.top()*sy, rect.bottom()*sy)) - for item in self.curves: if isinstance(item, PlotCurveItem): color = fn.colorStr(item.pen.color()) @@ -744,13 +711,12 @@ class PlotItem(GraphicsWidget): x *= sx y *= sy - #fh.write('\n' % color) fh.write('') - #fh.write("") + for item in self.dataItems: if isinstance(item, ScatterPlotItem): @@ -770,12 +736,10 @@ class PlotItem(GraphicsWidget): fh.write('\n' % (x, y, color, opacity)) fh.write("\n") - - def writeSvg(self, fileName=None): if fileName is None: - self._choose_filename_dialog(handler=self.writeSvg) + self._chooseFilenameDialog(handler=self.writeSvg) return fileName = str(fileName) @@ -787,23 +751,16 @@ class PlotItem(GraphicsWidget): def writeImage(self, fileName=None): if fileName is None: - self._choose_filename_dialog(handler=self.writeImage) + self._chooseFilenameDialog(handler=self.writeImage) return - if isinstance(fileName, tuple): - raise Exception("Not implemented yet..") - fileName = str(fileName) - PlotItem.lastFileDir = os.path.dirname(fileName) - self.png = QtGui.QImage(int(self.size().width()), int(self.size().height()), QtGui.QImage.Format_ARGB32) - painter = QtGui.QPainter(self.png) - painter.setRenderHints(painter.Antialiasing | painter.TextAntialiasing) - self.scene().render(painter, QtCore.QRectF(), self.mapRectToScene(self.boundingRect())) - painter.end() - self.png.save(fileName) + from ...exporters import ImageExporter + ex = ImageExporter(self) + ex.export(fileName) def writeCsv(self, fileName=None): if fileName is None: - self._choose_filename_dialog(handler=self.writeCsv) + self._chooseFilenameDialog(handler=self.writeCsv) return fileName = str(fileName) @@ -826,7 +783,6 @@ class PlotItem(GraphicsWidget): i += 1 fd.close() - def saveState(self): state = self.stateGroup.state() state['paramList'] = self.paramList.copy() @@ -861,7 +817,6 @@ class PlotItem(GraphicsWidget): 'viewRange': r, } self.vb.setState(state['view']) - def widgetGroupInterface(self): return (None, PlotItem.saveState, PlotItem.restoreState) @@ -960,9 +915,7 @@ class PlotItem(GraphicsWidget): def clipToViewMode(self): return self.ctrl.clipToViewCheck.isChecked() - - - + def updateDecimation(self): if self.ctrl.maxTracesCheck.isChecked(): numCurves = self.ctrl.maxTracesSpin.value() @@ -979,8 +932,7 @@ class PlotItem(GraphicsWidget): curves[i].clear() self.removeItem(curves[i]) else: - curves[i].hide() - + curves[i].hide() def updateAlpha(self, *args): (alpha, auto) = self.alphaState() @@ -1007,7 +959,6 @@ class PlotItem(GraphicsWidget): else: mode = False return mode - def resizeEvent(self, ev): if self.autoBtn is None: ## already closed down @@ -1016,7 +967,6 @@ class PlotItem(GraphicsWidget): y = self.size().height() - btnRect.height() self.autoBtn.setPos(0, y) - def getMenu(self): return self.ctrlMenu @@ -1050,7 +1000,6 @@ class PlotItem(GraphicsWidget): self.mouseHovering = False self.updateButtons() - def getLabel(self, key): pass @@ -1099,7 +1048,6 @@ class PlotItem(GraphicsWidget): v = (v,) self.setLabel(k, *v) - def showLabel(self, axis, show=True): """ Show or hide one of the plot's axis labels (the axis itself will be unaffected). @@ -1172,8 +1120,6 @@ class PlotItem(GraphicsWidget): raise Exception("X array must be 1D to plot (shape is %s)" % x.shape) c = PlotCurveItem(arr, x=x, **kargs) return c - - def _plotMetaArray(self, arr, x=None, autoLabel=True, **kargs): inf = arr.infoCopy() @@ -1200,17 +1146,12 @@ class PlotItem(GraphicsWidget): self.setLabel('left', text=name, units=units) return c - def setExportMode(self, export, opts=None): GraphicsWidget.setExportMode(self, export, opts) self.updateButtons() - #if export: - #self.autoBtn.hide() - #else: - #self.autoBtn.show() - def _choose_filename_dialog(self, handler): + def _chooseFilenameDialog(self, handler): self.fileDialog = FileDialog() if PlotItem.lastFileDir is not None: self.fileDialog.setDirectory(PlotItem.lastFileDir)