-
Notifications
You must be signed in to change notification settings - Fork 1
/
multiplotwidget.py
executable file
·189 lines (142 loc) · 6 KB
/
multiplotwidget.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
##
## This file is part of the sigrok-meter project.
##
## Copyright (C) 2015 Jens Steinhauser <jens.steinhauser@gmail.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import qtcompat
QtCore = qtcompat.QtCore
QtGui = qtcompat.QtGui
pyqtgraph = qtcompat.pyqtgraph
# Black foreground on white background.
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):
# 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.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().setColumnStretchFactor(0, 0)
self.layout().setColumnStretchFactor(1, 100)
# List of 'Plot' objects that are shown.
self._plots = []
self._hideActions = {}
def addPlot(self):
'''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)
yaxis = pyqtgraph.AxisItem(parent=self, orientation='left')
yaxis.linkToView(view)
yaxis.setGrid(255)
xaxis = pyqtgraph.AxisItem(parent=self, orientation='bottom')
xaxis.linkToView(view)
xaxis.setGrid(255)
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().setRowStretchFactor(row, 100)
self.layout().setRowStretchFactor(row + 1, 0)
plot.visible = True
self.plotShown.emit()
class MultiPlotWidget(pyqtgraph.GraphicsView):
'''Widget that aligns multiple plots on top of each other.
(The built in classes fail at doing this correctly when the axis grow,
just try zooming in the "GraphicsLayout" or the "Linked View" examples.)'''
def __init__(self, parent=None):
pyqtgraph.GraphicsView.__init__(self, parent)
self.multiPlotItem = MultiPlotItem()
self.setCentralItem(self.multiPlotItem)
for m in [
'addPlot',
'hidePlot',
'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)