0

I am currently facing an issue with matplotlib in combination with PyQt5 which I do not understand at all. Basically, I just want to plot different curves in the same figure, triggered by a QPushButton. I have recreated the problem in the minimal working example below.

Expectation:

Every time I hit the "plot" button, the current figure gets updated with a new line

Outcome:

The first plot works correctly, however after that the figure doesn't get updated with the correct data anymore. If the figure is closed and the button is pressed again, the plot starts to work again. If the code is changed to

fig = plt.figure()

that is, if every time a new figure is created it works as expected. Is this a bug or am I dead-wrong somehow?

The following packages are used for the minimal working example:

matplotlib==3.1.3
PyQt5==5.14.1
pyqt5-tools==5.13.0.1.5

The sample code:

import matplotlib.pyplot as plt
import numpy as np
import sys
from PyQt5 import QtWidgets

class MaterialBrowser(QtWidgets.QMainWindow):

    def __init__(self):
        super(MaterialBrowser, self).__init__()
        self.setEnabled(True)
        self.setGeometry(0, 0, 543, 700)
        self.setMinimumSize(543, 400)
        self.save_button = QtWidgets.QPushButton(self)
        self.save_button.setGeometry(100, 100, 110, 32)
        self.save_button.setObjectName("plot_button")
        self.save_button.setText("Plot")
        self.save_button.clicked.connect(self.plot_bhcurve) 
        self.show()

    def plot_bhcurve(self):
        t = np.arange(0.0, 2.0, 0.01)
        omega = np.random.randint(2, 50)
        s = 1 + np.sin(2 * np.pi * omega* t)
        fig = plt.figure(num='MYFIGURE')
        ax = fig.gca()
        ax.plot(t, s, '.-', label=f'mycurve{omega}')
        ax.grid(True, which="both")
        ax.set_xlabel('Field Strength (A/m)')
        ax.set_ylabel('Flux Density (T)')
        ax.set_xlim(left=0)
        ax.set_ylim(bottom=0)
        plt.legend()
        plt.show()

app = QtWidgets.QApplication(sys.argv)
materialbrowser = MaterialBrowser()
sys.exit(app.exec_())

1 Answer 1

1

When integrating Matplotlib into PyQt you should never use the matplotlib.pyplot module since that creates it's own event and maintains a list of figures and axes. It clashes with Qt.

There is a good example on how to embedding Matplotlib in PyQt here. I've adapted your example based on that.

import numpy as np
import sys
from PyQt5 import QtWidgets

from matplotlib.backends.backend_qt5agg import (
    FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure


class MaterialBrowser(QtWidgets.QMainWindow):

    def __init__(self):
        super(MaterialBrowser, self).__init__()
        self.setEnabled(True)
        self.setGeometry(0, 0, 543, 700)
        self.setMinimumSize(543, 400)

        self.main_widget = QtWidgets.QWidget()
        self.setCentralWidget(self.main_widget)

        self.main_layout = QtWidgets.QVBoxLayout()
        self.main_widget.setLayout(self.main_layout)

        self.save_button = QtWidgets.QPushButton(self)
        self.save_button.setGeometry(100, 100, 110, 32)
        self.save_button.setObjectName("plot_button")
        self.save_button.setText("Plot")
        self.save_button.clicked.connect(self.plot_bhcurve) 
        self.main_layout.addWidget(self.save_button)

        self._fig = Figure(figsize=(5, 3))
        self._canvas = FigureCanvas(self._fig)
        self.main_layout.addWidget(self._canvas)

        self._axes = self._fig.subplots()
        self._axes.grid(True, which="both")
        self._axes.set_xlabel('Field Strength (A/m)')
        self._axes.set_ylabel('Flux Density (T)')
        self._axes.set_xlim(left=0)
        self._axes.set_ylim(bottom=0)
        self._axes.legend()

        self.addToolBar(NavigationToolbar(self._canvas, self))

        self.show()



    def plot_bhcurve(self):
        print("plotting")
        t = np.arange(0.0, 2.0, 0.01)
        omega = np.random.randint(2, 50)
        s = 1 + np.sin(2 * np.pi * omega* t)

        self._axes.plot(t, s, '.-', label=f'mycurve{omega}')
        self._canvas.draw()


app = QtWidgets.QApplication(sys.argv)
materialbrowser = MaterialBrowser()
sys.exit(app.exec_())

By the way, I strongly recommend to use Qt layouts to layout your widgets.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.