X-Git-Url: http://sigrok.org/gitweb/?a=blobdiff_plain;f=multiplotwidget.py;fp=multiplotwidget.py;h=1d257bd450f3cfa63877a7195c454b44d84c928c;hb=d0aa45b43873337a187f274d5c2919e994941f7f;hp=69e871a2414e47cf02995db99c0f9a8e03922af3;hpb=32b1665156dbd4a767aa83bff6397e696a409dc6;p=sigrok-meter.git diff --git a/multiplotwidget.py b/multiplotwidget.py index 69e871a..1d257bd 100755 --- a/multiplotwidget.py +++ b/multiplotwidget.py @@ -28,62 +28,136 @@ pyqtgraph = qtcompat.pyqtgraph pyqtgraph.setConfigOption('background', 'w') pyqtgraph.setConfigOption('foreground', 'k') +class Plot(object): + '''Helper class to keep all graphics items of a plot together.''' + + def __init__(self, view, xaxis, yaxis): + self.view = view + self.xaxis = xaxis + self.yaxis = yaxis + self.visible = False + class MultiPlotItem(pyqtgraph.GraphicsWidget): - class Plot: - def __init__(self, view, xaxis, yaxis): - self.view = view - self.xaxis = xaxis - self.yaxis = yaxis + # Emitted when a plot is shown. + plotShown = QtCore.Signal() + + # Emitted when a plot is hidden by the user via the context menu. + plotHidden = QtCore.Signal(Plot) def __init__(self, parent=None): pyqtgraph.GraphicsWidget.__init__(self, parent) - self.layout = QtGui.QGraphicsGridLayout() - self.layout.setContentsMargins(10, 10, 10, 1) - self.layout.setHorizontalSpacing(0) - self.layout.setVerticalSpacing(0) - self.setLayout(self.layout) - - self._plots = [] + self.setLayout(QtGui.QGraphicsGridLayout()) + self.layout().setContentsMargins(10, 10, 10, 1) + self.layout().setHorizontalSpacing(0) + self.layout().setVerticalSpacing(0) for i in range(2): - self.layout.setColumnPreferredWidth(i, 0) - self.layout.setColumnMinimumWidth(i, 0) - self.layout.setColumnSpacing(i, 0) + self.layout().setColumnPreferredWidth(i, 0) + self.layout().setColumnMinimumWidth(i, 0) + self.layout().setColumnSpacing(i, 0) + + self.layout().setColumnStretchFactor(0, 0) + self.layout().setColumnStretchFactor(1, 100) + + # List of 'Plot' objects that are shown. + self._plots = [] - self.layout.setColumnStretchFactor(0, 0) - self.layout.setColumnStretchFactor(1, 100) + self._hideActions = {} def addPlot(self): - row = self.layout.rowCount() + '''Adds and returns a new plot.''' + + row = self.layout().rowCount() view = pyqtgraph.ViewBox(parent=self) + + # If this is not the first plot, link to the axis of the previous one. if self._plots: view.setXLink(self._plots[-1].view) - self.layout.addItem(view, row, 1) yaxis = pyqtgraph.AxisItem(parent=self, orientation='left') yaxis.linkToView(view) yaxis.setGrid(255) - self.layout.addItem(yaxis, row, 0, QtCore.Qt.AlignRight) xaxis = pyqtgraph.AxisItem(parent=self, orientation='bottom') xaxis.linkToView(view) xaxis.setGrid(255) - self.layout.addItem(xaxis, row + 1, 1) + + plot = Plot(view, xaxis, yaxis) + self._plots.append(plot) + + self.showPlot(plot) + + # Create a separate action object for each plots context menu, so that + # we can later find out which plot should be hidden by looking at + # 'self._hideActions'. + hideAction = QtGui.QAction('Hide', self) + hideAction.triggered.connect(self._onHideActionTriggered) + self._hideActions[id(hideAction)] = plot + view.menu.insertAction(view.menu.actions()[0], hideAction) + + return plot + + def _rowNumber(self, plot): + '''Returns the number of the first row a plot occupies.''' + + # Every plot takes up two rows + return 2 * self._plots.index(plot) + + @QtCore.Slot() + def _onHideActionTriggered(self, checked=False): + # The plot that we want to hide. + plot = self._hideActions[id(self.sender())] + self.hidePlot(plot) + + def hidePlot(self, plot): + '''Hides 'plot'.''' + + # Only hiding wouldn't give up the space occupied by the items, + # we have to remove them from the layout. + self.layout().removeItem(plot.view) + self.layout().removeItem(plot.xaxis) + self.layout().removeItem(plot.yaxis) + + plot.view.hide() + plot.xaxis.hide() + plot.yaxis.hide() + + row = self._rowNumber(plot) + self.layout().setRowStretchFactor(row, 0) + self.layout().setRowStretchFactor(row + 1, 0) + + plot.visible = False + self.plotHidden.emit(plot) + + def showPlot(self, plot): + '''Adds the items of the plot to the scene's layout and makes + them visible.''' + + if plot.visible: + return + + row = self._rowNumber(plot) + self.layout().addItem(plot.yaxis, row, 0, QtCore.Qt.AlignRight) + self.layout().addItem(plot.view, row, 1) + self.layout().addItem(plot.xaxis, row + 1, 1) + + plot.view.show() + plot.xaxis.show() + plot.yaxis.show() for i in range(row, row + 2): - self.layout.setRowPreferredHeight(i, 0) - self.layout.setRowMinimumHeight(i, 0) - self.layout.setRowSpacing(i, 0) + self.layout().setRowPreferredHeight(i, 0) + self.layout().setRowMinimumHeight(i, 0) + self.layout().setRowSpacing(i, 0) - self.layout.setRowStretchFactor(row, 100) - self.layout.setRowStretchFactor(row + 1, 0) + self.layout().setRowStretchFactor(row, 100) + self.layout().setRowStretchFactor(row + 1, 0) - p = MultiPlotItem.Plot(view, xaxis, yaxis) - self._plots.append(p) - return p + plot.visible = True + self.plotShown.emit() class MultiPlotWidget(pyqtgraph.GraphicsView): '''Widget that aligns multiple plots on top of each other. @@ -98,6 +172,18 @@ class MultiPlotWidget(pyqtgraph.GraphicsView): self.setCentralItem(self.multiPlotItem) for m in [ - 'addPlot' + 'addPlot', + 'showPlot' ]: setattr(self, m, getattr(self.multiPlotItem, m)) + + self.multiPlotItem.plotShown.connect(self._on_plotShown) + + # Expose the signal of the plot item. + self.plotHidden = self.multiPlotItem.plotHidden + + def _on_plotShown(self): + # This call is needed if only one plot exists and it was hidden, + # without it the layout would start acting weird and not make the + # MultiPlotItem fill the view widget after showing the plot again. + self.resizeEvent(None)