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]
这是我代码的一部分的简单 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]