I am trying to plot a line with matplotlib, but the data points is huge (4410000), so I plan to down-sample it, I refer to the example on the matplotlib website resampling data, and an another similar question Matplotlib callbacks don't work when matplotlib.FigureCanvas is embedded in a PyQt5 application?
but my demo-code bellow doesn't work yet, why?:
import sys
import numpy as np
from PyQt5 import QtWidgets # import PyQt5 before matplotlib
import matplotlib
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
matplotlib.use("Qt5Agg")
class DataDisplayDownsampler(object):
def __init__(self, xdata, ydata):
self.origYData = ydata
self.origXData = xdata
self.max_points = 3000
self.delta = xdata[-1] - xdata[0]
self.line = None
def downsample(self, xstart, xend):
# get the points in the view range
mask = (self.origXData > xstart) & (self.origXData < xend)
# dilate the mask by one to catch the points just outside
# of the view range to not truncate the line
mask = np.convolve([1, 1], mask, mode='same').astype(bool)
# sort out how many points to drop
ratio = max(np.sum(mask) // self.max_points, 1)
# mask data
xdata = self.origXData[mask]
ydata = self.origYData[mask]
# downsample data
xdata = xdata[::ratio]
ydata = ydata[::ratio]
print("using {} of {} visible points".format(
len(ydata), np.sum(mask)))
return xdata, ydata
def update(self, ax):
# Update the line
lims = ax.viewLim
if np.abs(lims.width - self.delta) > 1e-8:
self.delta = lims.width
xstart, xend = lims.intervalx
self.line.set_data(*self.downsample(xstart, xend))
ax.figure.canvas.draw_idle()
class MplCanvas(FigureCanvasQTAgg):
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
self.axe = fig.add_subplot(111)
super().__init__(fig)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
sc = MplCanvas(self, width=5, height=4, dpi=100)
fs = 44100
time_length = 100
x = np.arange(fs * time_length) / fs
y1 = np.random.randn(len(x))
ds = DataDisplayDownsampler(x, y1)
ds.line, = sc.axe.plot(x, y1, lw=2, label='random noise')
sc.axe.callbacks.connect('xlim_changed', ds.update) # doesn't work
toolbar = NavigationToolbar(sc, self)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(toolbar)
layout.addWidget(sc)
widget = QtWidgets.QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
self.show()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
mw = window
mw.show()
app.exec_()