I am trying to expand the example presented here on how to integrate a matplotlib
figure into a PyQt5 window and update it as the application is running.
What I have changed with respect to the code linked above is the condition for when the plot updates, i.e. instead of a timer, I added a button to the application that is linked to a function called simulate
and runs a for
-loop. Inside the loop, we call the update_plot
function, everything else stayed the same.
def simulate(self, ydata):
for k in trange(0, 500, 10):
tmp = np.random.uniform(0, 1, size=(2, 1000))
self.xdata = tmp[0]
self.ydata = tmp[1]
self.update_plot()
The full code is
import sys
from PyQt5.QtWidgets import QVBoxLayout, QSizePolicy, QWidget, QPushButton, QHBoxLayout, QSpacerItem
from PyQt5 import QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import random
import numpy as np
from time import sleep
import sys
import matplotlib
matplotlib.use('Qt5Agg')
from matplotlib.figure import Figure
class MplCanvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111)
super(MplCanvas, self).__init__(fig)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
widget = QWidget(self)
self.setCentralWidget(widget)
vlay = QVBoxLayout(widget)
hlay = QHBoxLayout()
vlay.addLayout(hlay)
pybutton = QPushButton('Fit!', self)
pybutton.clicked.connect(self.simulate)
hlay2 = QHBoxLayout()
hlay2.addWidget(pybutton)
hlay2.addItem(QSpacerItem(1000, 10, QSizePolicy.Expanding))
vlay.addLayout(hlay2)
self.canvas = MplCanvas(self, width=5, height=4, dpi=100)
vlay.addWidget(self.canvas)
self.n_data = 999
self.xdata = list(range(self.n_data))
self.ydata = [random.uniform(0, 1) for i in range(self.n_data)]
# We need to store a reference to the plotted line
# somewhere, so we can apply the new data to it.
self._plot_ref = None
self.update_plot()
self.show()
def simulate(self):
for k in range(0, 5):
print(k)
tmp = np.random.uniform(0, 1, size=(2,self.n_data))
self.xdata = tmp[0]
self.ydata = tmp[1]
self.update_plot()
sleep(0.5)
def update_plot(self):
# Drop off the first y element, append a new one.
#self.ydata = self.ydata[1:] + [random.randint(0, 10)]
# Note: we no longer need to clear the axis.
if self._plot_ref is None:
plot_refs = self.canvas.axes.plot(self.xdata, self.ydata, 'r')
self._plot_ref = plot_refs[0]
else:
self._plot_ref.set_ydata(self.ydata)
# Trigger the canvas to update and redraw.
self.canvas.draw()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
app.exec_()
What happens now is that whenever you click the button self.simulate()
gets called and thus the for
-loop starts... And as you can see in the function, I call self.update_plot
every time, but the displayed plot only gets updated once the loop is over, i.e. the function finishes...
Can somebody explain what is going on?