Reputation: 21
I would like to create a widget that displays several time signals on top of each other. The signals have the same time axis but different value axes. The time axes should be of a custom type. The time axes should be linked.
The whole widget is to be displayed in a QHBox layout next to a QListWidget with an item with a checkbox for each signal that enables the user to enable or disable the individual plots.
I currently have a half working solution but I guess I'm doing things wrong on a conceptual level. I can't get the autoSIprefix and label for the value axis to work and strange floating axes show up that shouldn't.
My current widget with floating axes on to left and missing value axis labels:
class MultiPlotWidget(QWidget):
def __init__(self, parent, data_frame):
super().__init__(parent)
# Data
self._df = data_frame
self._grouped_data = self._grouped_data()
# everything is enabled by default
self._enabled_parameters = set(self._grouped_data.keys())
self._parameter_name_map = lambda name: name
self._plot_items = self._make_plot_items(self._grouped_data)
# Plot widget
self._fig_sa = QScrollArea(self)
self._fig = pg.GraphicsLayoutWidget(self._fig_sa)
self._fig.setMinimumSize(QSize(400, len(self._enabled_parameters) * 200))
self._fig_sa.setWidgetResizable(True)
self._fig_sa.setWidget(self._fig)
# Side widget
self._sw = SideWidget(self, sorted(self._enabled_parameters))
self._sw.parameter_state_change.connect(self._parameter_state_change_action)
# Layout
h_layout = QHBoxLayout()
h_layout.addWidget(self._fig_sa)
h_layout.addWidget(self._sw)
self.setLayout(h_layout)
self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
def data(self):
return self._df
def set_parameter_name_map(self, pnm):
self._parameter_name_map = pnm
self._plot_items = self._make_plot_items(self._grouped_data)
self._sw.set_parameter_name_map(pnm)
def _grouped_data(self):
"""Regroup columns of self._df into a dict with groups with common
name but different suffix e.g. self._df has columns ["foo/a",
"foo/b", "foo/c"] then this function returns a dict {"foo": {"a": column
foo/a , "b": column foo/b, "c": column foo/c}}
"""
grouped = {}
for col in self._df.columns:
parts = col
name = col
suffix = DES
if len(parts) == 2:
name, suffix = parts
if name not in grouped: grouped[name] = {}
grouped[name].update({suffix: self._df[col].as_matrix()})
return grouped
def _make_plot_items(self, grouped_data):
plot_items = {}
last_vb = None
for group_name in self._enabled_parameters:
group_cols = grouped_data[group_name]
ts_name = self._parameter_name_map(group_name)
date_axis = DateAxisItem(self._df.index.to_series(), "bottom")
pi = pg.PlotItem(axisItems={"bottom": date_axis# , "left": value_axis
})
pi.setLabel("left", ts_name, unit="m")
vb = pi.getViewBox()
pi.setTitle(ts_name)
pi.showGrid(x=True, y=True, alpha=0.5)
value_axis = pi.getAxis("left")
value_axis.enableAutoSIPrefix()
value_axis.setLabel(text=ts_name, unit="m")
value_axis.showLabel(show=True)
colormap = {DES: pg.hsvColor(0, 0, 1),
ADV: pg.hsvColor(0, 1, 1),
FAV: pg.hsvColor(0.33, 1, 1)}
for suffix, col in group_cols.items():
vb.setLimits(xMin=0, xMax=len(col)-1)
pi.plot(col, name=suffix, antialias=True,
pen={"color": colormap.get(suffix, colormap[DES])})
# # Only show legend if more than one curve per plot is
# # present
# if len(group_cols.items()) > 1:
# pi.addLegend()
if last_vb is not None: # Link all time axes together
last_vb.setXLink(vb)
vb.setXLink(last_vb)
last_vb = vb
plot_items[group_name] = pi
return plot_items
def draw(self):
self._fig.setMinimumSize(QSize(400, len(self._enabled_parameters) * 220))
# Remove all items
row = 0
while True:
try:
item = self._fig.getItem(row, 0)
# for child_item in item.allChildItems():
# item.removeItem(child_item)
self._fig.removeItem(item)
row += 1
except Exception: # Thats exactly what pyqtgraph throws
break
# Insert the enabled plot items
self._fig.clear()
for group_name in sorted(self._enabled_parameters):
self._fig.addItem(self._plot_items[group_name])
self._fig.nextRow()
def _parameter_state_change_action(self, tpl):
"""Handles enabling and disabling the display of time series when they
are ticked or unticked in the side widget.
"""
p_name, p_enabled = tpl
if p_enabled:
self._enabled_parameters.add(p_name)
else:
self._enabled_parameters.remove(p_name)
self.draw()
I've had a good look at the documentation and examples but didn't find anything suitable. Could you please give me a hint on how to achieve the features I want the "correct" way? Thanks a lot!
Upvotes: 2
Views: 1672
Reputation: 41
1) Floating Axis -
This is a known bug in PlotItem.py (PyQtGraph 0.9.10), It will be fixed in later releases. The fixed code is already available on GitHub https://github.com/pyqtgraph/pyqtgraph/blob/develop/pyqtgraph/graphicsItems/PlotItem/PlotItem.py. Pulling the repository and re-installing fixed it for me.
2) SI Units -
You can add AutoUpdating SI units to PlotItem using the setLabels()
method.
yourPlotItem.setLabels(left = ('Amplitude','V'))
This will set 'V' as your unit for the left axis, and it will change to mV, kV etc based on scaling.
Upvotes: 1