PyQt5 和 matplotlib,图形仅在屏幕最大化或最小化时更新

PyQt5 and matplotlib, graph only updates when screen gets maximized or minimized

这是我代码的一部分的简单 MRE,如果你能帮助我,在此先感谢你。如果您愿意更正一些错误或以更好的方式编写这段代码,请随时这样做。

import sys
import random
import matplotlib
import matplotlib.pyplot as plt
from PyQt5 import QtCore, QtWidgets
from matplotlib.backends.backend_qt5agg import (
   FigureCanvasQTAgg as FigureCanvas,
   NavigationToolbar2QT)
from matplotlib.figure import Figure

matplotlib.use('Qt5Agg')


class MainWindow(QtWidgets.QMainWindow):

   def __init__(self):
       super().__init__()

       self.resize(500, 500)
       button = QtWidgets.QPushButton('Press me')
       self.widget_central = QtWidgets.QStackedWidget()
       self.widget_central.addWidget(button)
       self.setCentralWidget(self.widget_central)

       button.clicked.connect(self.screen)

   def screen(self):

       s = _screen()

       self.widget_central.addWidget(s)
       self.widget_central.setCurrentWidget(s)


class _screen(QtWidgets.QWidget):

   def __init__(self):
       super().__init__()

       lay = QtWidgets.QHBoxLayout()
       groupbox = QtWidgets.QGroupBox()
       lay_2 = QtWidgets.QVBoxLayout()
       splitter = QtWidgets.QSplitter(QtCore.Qt.Horizontal)

       self.slider = QtWidgets.QSlider()
       self.slider.setFocusPolicy(QtCore.Qt.NoFocus)
       self.slider.setGeometry(30, 40, 200, 30)
       self.slider.setRange(0, 100)
       self.slider.setValue(100)
       self.slider.setInvertedAppearance(True)
       self.slider.setTickPosition(QtWidgets.QSlider.TicksBelow)
       self.slider.setTickInterval(1)

       x_axis = list(range(10))
       y_axis = [random.randint(0, 10) for i in range(10)]

       self.area_plot = MplCanvas(self, width=5, height=4, dpi=100)
       toolbar = NavigationToolbar2QT(self.area_plot, self)
       self.area_plot.figure, self.ax = plt.subplots()
       self.ax.plot(x_axis, y_axis)

       lay_2.addWidget(toolbar)
       lay_2.addWidget(self.area_plot)
       groupbox.setLayout(lay_2)
       splitter.addWidget(self.slider)
       splitter.addWidget(groupbox)

       lay.addWidget(splitter)

       self.setLayout(lay)

       self.slider.valueChanged[int].connect(self.graph_uptade)

   def graph_uptade(self, value):

       QtWidgets.QApplication.processEvents()
       x_axis = list(range(10))
       y_axis = [value * random.randint(0, 10) for i in range(10)]

       self.ax.cla()
       self.ax.plot(x_axis, y_axis, 'r')
       self.area_plot.figure.canvas.draw_idle()


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)


app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()

有一个 QSlider,其范围从 0 到 100,每次激活滑块时,该值都会发送到一个函数,该函数根据 QSlider 值更新图形(matplotlib 图)。图形实际上更新了,程序没有崩溃,但是更改在主屏幕上是不可见的,为了看到新的图形,我需要最大化 window,如果我在最大化时再次更新图形,我必须最小化 window 才能看到更新的版本等等。

我试过使用 QApplication.ProcessEvents(), plt.cla() plt.plot() plt.draw(), aldo plt.draw_idle (), 和其他一些东西,但无济于事。在这方面似乎没有什么对我有用。

如果你能帮助我,我将不胜感激。

您的问题是您使用 matplotlib.pyplot (plt.) 定义了子图。

self.area_plot.figure, self.ax = plt.subplots() <-- 这里

如果您在定义的图形上注册子图(并稍微清理一下代码),它应该会按预期工作:

import sys
import random
import matplotlib
import matplotlib.pyplot as plt
from PyQt5 import QtCore, QtWidgets
from matplotlib.backends.backend_qt5agg import (
   FigureCanvasQTAgg as FigureCanvas,
   NavigationToolbar2QT)
from matplotlib.figure import Figure

matplotlib.use('Qt5Agg')

class MainWindow(QtWidgets.QMainWindow):

   def __init__(self):
       super().__init__()

       self.resize(500, 500)
       button = QtWidgets.QPushButton('Press me')
       self.widget_central = QtWidgets.QStackedWidget()
       self.widget_central.addWidget(button)
       self.setCentralWidget(self.widget_central)

       button.clicked.connect(self.screen)

   def screen(self):

       s = _screen()

       self.widget_central.addWidget(s)
       self.widget_central.setCurrentWidget(s)


class _screen(QtWidgets.QWidget):

   def __init__(self):
       super().__init__()

       lay = QtWidgets.QHBoxLayout()
       groupbox = QtWidgets.QGroupBox()
       lay_2 = QtWidgets.QVBoxLayout()
       splitter = QtWidgets.QSplitter(QtCore.Qt.Horizontal)

       self.slider = QtWidgets.QSlider()
       self.slider.setFocusPolicy(QtCore.Qt.NoFocus)
       self.slider.setGeometry(30, 40, 200, 30)
       self.slider.setRange(0, 100)
       self.slider.setValue(100)
       self.slider.setInvertedAppearance(True)
       self.slider.setTickPosition(QtWidgets.QSlider.TicksBelow)
       self.slider.setTickInterval(1)

       x_axis = list(range(10))
       y_axis = [random.randint(0, 10) for i in range(10)]

       self.area_plot = MplCanvas(self, width=5, height=4, dpi=100)
       toolbar = NavigationToolbar2QT(self.area_plot, self)
       self.area_plot.figure
       self.area_plot.figure.clf() #remove the initial axis labels
       self.ax = self.area_plot.figure.add_subplot(111) #add subplot, retrieve axis object
       self.ax.plot(x_axis, y_axis)

       lay_2.addWidget(toolbar)
       lay_2.addWidget(self.area_plot)
       groupbox.setLayout(lay_2)
       splitter.addWidget(self.slider)
       splitter.addWidget(groupbox)

       lay.addWidget(splitter)

       self.setLayout(lay)

       self.slider.valueChanged[int].connect(self.graph_uptade)

   def graph_uptade(self, value):

       QtWidgets.QApplication.processEvents()
       x_axis = list(range(10))
       y_axis = [value * random.randint(0, 10) for i in range(10)]
       
       self.ax.cla()
       self.ax.plot(x_axis, y_axis, 'r')
       self.area_plot.figure.canvas.draw_idle()


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)


app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()

你正在用 subplots() 覆盖图形,如果你想更新轴,你可以只使用当前图形的轴。

更改此行:

        self.area_plot.figure, self.ax = plt.subplots()

为此:

        self.ax = self.area_plot.figure.axes[0]