]> sigrok.org Git - sigrok-meter.git/blame - multiplotwidget.py
Make it possible to hide plots.
[sigrok-meter.git] / multiplotwidget.py
CommitLineData
f76b9df8
JS
1##
2## This file is part of the sigrok-meter project.
3##
4## Copyright (C) 2015 Jens Steinhauser <jens.steinhauser@gmail.com>
5##
6## This program is free software; you can redistribute it and/or modify
7## it under the terms of the GNU General Public License as published by
8## the Free Software Foundation; either version 2 of the License, or
9## (at your option) any later version.
10##
11## This program is distributed in the hope that it will be useful,
12## but WITHOUT ANY WARRANTY; without even the implied warranty of
13## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14## GNU General Public License for more details.
15##
16## You should have received a copy of the GNU General Public License
17## along with this program; if not, write to the Free Software
18## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19##
20
21import qtcompat
22
23QtCore = qtcompat.QtCore
24QtGui = qtcompat.QtGui
25pyqtgraph = qtcompat.pyqtgraph
26
27# black foreground on white background
28pyqtgraph.setConfigOption('background', 'w')
29pyqtgraph.setConfigOption('foreground', 'k')
30
d0aa45b4
JS
31class Plot(object):
32 '''Helper class to keep all graphics items of a plot together.'''
33
34 def __init__(self, view, xaxis, yaxis):
35 self.view = view
36 self.xaxis = xaxis
37 self.yaxis = yaxis
38 self.visible = False
39
f76b9df8
JS
40class MultiPlotItem(pyqtgraph.GraphicsWidget):
41
d0aa45b4
JS
42 # Emitted when a plot is shown.
43 plotShown = QtCore.Signal()
44
45 # Emitted when a plot is hidden by the user via the context menu.
46 plotHidden = QtCore.Signal(Plot)
f76b9df8
JS
47
48 def __init__(self, parent=None):
49 pyqtgraph.GraphicsWidget.__init__(self, parent)
50
d0aa45b4
JS
51 self.setLayout(QtGui.QGraphicsGridLayout())
52 self.layout().setContentsMargins(10, 10, 10, 1)
53 self.layout().setHorizontalSpacing(0)
54 self.layout().setVerticalSpacing(0)
f76b9df8
JS
55
56 for i in range(2):
d0aa45b4
JS
57 self.layout().setColumnPreferredWidth(i, 0)
58 self.layout().setColumnMinimumWidth(i, 0)
59 self.layout().setColumnSpacing(i, 0)
60
61 self.layout().setColumnStretchFactor(0, 0)
62 self.layout().setColumnStretchFactor(1, 100)
63
64 # List of 'Plot' objects that are shown.
65 self._plots = []
f76b9df8 66
d0aa45b4 67 self._hideActions = {}
f76b9df8
JS
68
69 def addPlot(self):
d0aa45b4
JS
70 '''Adds and returns a new plot.'''
71
72 row = self.layout().rowCount()
f76b9df8
JS
73
74 view = pyqtgraph.ViewBox(parent=self)
d0aa45b4
JS
75
76 # If this is not the first plot, link to the axis of the previous one.
f76b9df8
JS
77 if self._plots:
78 view.setXLink(self._plots[-1].view)
f76b9df8
JS
79
80 yaxis = pyqtgraph.AxisItem(parent=self, orientation='left')
81 yaxis.linkToView(view)
82 yaxis.setGrid(255)
f76b9df8
JS
83
84 xaxis = pyqtgraph.AxisItem(parent=self, orientation='bottom')
85 xaxis.linkToView(view)
86 xaxis.setGrid(255)
d0aa45b4
JS
87
88 plot = Plot(view, xaxis, yaxis)
89 self._plots.append(plot)
90
91 self.showPlot(plot)
92
93 # Create a separate action object for each plots context menu, so that
94 # we can later find out which plot should be hidden by looking at
95 # 'self._hideActions'.
96 hideAction = QtGui.QAction('Hide', self)
97 hideAction.triggered.connect(self._onHideActionTriggered)
98 self._hideActions[id(hideAction)] = plot
99 view.menu.insertAction(view.menu.actions()[0], hideAction)
100
101 return plot
102
103 def _rowNumber(self, plot):
104 '''Returns the number of the first row a plot occupies.'''
105
106 # Every plot takes up two rows
107 return 2 * self._plots.index(plot)
108
109 @QtCore.Slot()
110 def _onHideActionTriggered(self, checked=False):
111 # The plot that we want to hide.
112 plot = self._hideActions[id(self.sender())]
113 self.hidePlot(plot)
114
115 def hidePlot(self, plot):
116 '''Hides 'plot'.'''
117
118 # Only hiding wouldn't give up the space occupied by the items,
119 # we have to remove them from the layout.
120 self.layout().removeItem(plot.view)
121 self.layout().removeItem(plot.xaxis)
122 self.layout().removeItem(plot.yaxis)
123
124 plot.view.hide()
125 plot.xaxis.hide()
126 plot.yaxis.hide()
127
128 row = self._rowNumber(plot)
129 self.layout().setRowStretchFactor(row, 0)
130 self.layout().setRowStretchFactor(row + 1, 0)
131
132 plot.visible = False
133 self.plotHidden.emit(plot)
134
135 def showPlot(self, plot):
136 '''Adds the items of the plot to the scene's layout and makes
137 them visible.'''
138
139 if plot.visible:
140 return
141
142 row = self._rowNumber(plot)
143 self.layout().addItem(plot.yaxis, row, 0, QtCore.Qt.AlignRight)
144 self.layout().addItem(plot.view, row, 1)
145 self.layout().addItem(plot.xaxis, row + 1, 1)
146
147 plot.view.show()
148 plot.xaxis.show()
149 plot.yaxis.show()
f76b9df8
JS
150
151 for i in range(row, row + 2):
d0aa45b4
JS
152 self.layout().setRowPreferredHeight(i, 0)
153 self.layout().setRowMinimumHeight(i, 0)
154 self.layout().setRowSpacing(i, 0)
f76b9df8 155
d0aa45b4
JS
156 self.layout().setRowStretchFactor(row, 100)
157 self.layout().setRowStretchFactor(row + 1, 0)
f76b9df8 158
d0aa45b4
JS
159 plot.visible = True
160 self.plotShown.emit()
f76b9df8
JS
161
162class MultiPlotWidget(pyqtgraph.GraphicsView):
163 '''Widget that aligns multiple plots on top of each other.
164
165 (The built in classes fail at doing this correctly when the axis grow,
166 just try zooming in the "GraphicsLayout" or the "Linked View" examples.)'''
167
168 def __init__(self, parent=None):
169 pyqtgraph.GraphicsView.__init__(self, parent)
170
171 self.multiPlotItem = MultiPlotItem()
172 self.setCentralItem(self.multiPlotItem)
173
174 for m in [
d0aa45b4
JS
175 'addPlot',
176 'showPlot'
f76b9df8
JS
177 ]:
178 setattr(self, m, getattr(self.multiPlotItem, m))
d0aa45b4
JS
179
180 self.multiPlotItem.plotShown.connect(self._on_plotShown)
181
182 # Expose the signal of the plot item.
183 self.plotHidden = self.multiPlotItem.plotHidden
184
185 def _on_plotShown(self):
186 # This call is needed if only one plot exists and it was hidden,
187 # without it the layout would start acting weird and not make the
188 # MultiPlotItem fill the view widget after showing the plot again.
189 self.resizeEvent(None)