【问题标题】:How do I stop closed Matplotlib plots coming back to haunt me?如何阻止关闭的 Matplotlib 情节再次困扰我?
【发布时间】:2020-10-27 19:48:47
【问题描述】:

我使用 PyQt5 和 Matplotlib 编写了一个大型数据处理和绘图应用程序,并且在创建新绘图时重新出现旧绘图困扰着我。我已经看到很多关于此的问题得到了解答,但建议的方法都不适合我。

我已将问题简化为以下简化代码:

from PyQt5.QtWidgets import (QApplication, QMainWindow, QDialog, QPushButton,
    QVBoxLayout, QGroupBox)
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import sys

class PlotCanvas(FigureCanvas):
    """ Subclass of the MPL FigureCanvas class. """
    def __init__(self, parent=None):
        self.parent = parent
        self.fig = Figure()
        self.ax = self.fig.add_subplot(111)
        self.fig.set_tight_layout(True)
        FigureCanvas.__init__(self, self.fig)

class PlotWindow(QDialog):
    """ Create plot in window and plot supplied data. """
    def __init__(self, parent, x, y):
        super().__init__()
        self.parent = parent
        self.x = x
        self.y = y
        self.set_up_gui()
        self.l2d = self.canvas.ax.plot(self.x, self.y)[0]

    def set_up_gui(self):
        """ Create GUI. """
        vbl = QVBoxLayout()
        self.canvas = PlotCanvas(self)
        vbl.addWidget(self.canvas)
        self.setLayout(vbl)

    def closeEvent(self, event):
        """ Delete figure on closing window. """
        plt.close(self.canvas.fig)
        super(PlotWindow, self).closeEvent(event)

class MainApp(QMainWindow):
    """ My main application class """
    def __init__(self, parent=None):
        super(QMainWindow, self).__init__()
        # Create some arbitrary data
        self.x = [1, 2, 3, 4, 5, 6, 7, 8, 9]
        self.y = [2, 4, 6, 4, 2, 4, 6, 8, 6]
        self.set_up_gui()

    def set_up_gui(self):
        """ Create GUI with 2 buttons for creating 2 plots. """
        btn1 = QPushButton()
        btn1.setText('Button 1')
        btn1.clicked.connect(self.plot1)
        btn2 = QPushButton()
        btn2.setText('Button 2')
        btn2.clicked.connect(self.plot2)
        vbl = QVBoxLayout()
        vbl.addWidget(btn1)
        vbl.addWidget(btn2)
        gb = QGroupBox()
        gb.setLayout(vbl)
        self.setCentralWidget(gb)

    def plot1(self):
        """ Plot using my PlotWindow class. """
        p = PlotWindow(self, self.x, self.y)
        p.show()

    def plot2(self):
        """ Plot using regular MPL plot call. """
        fig = plt.figure()
        plt.plot([10, 20, 30], [4, 8, 2])
        plt.show()
        plt.close(fig)

if __name__=='__main__':
    app = QApplication(sys.argv)
    main = MainApp()
    main.show()
    sys.exit(app.exec_())

奇怪的行为是这样的:

  • 按下按钮 1,出现绘图 1,按下红色 X 关闭窗口。
  • 按下按钮 2,出现绘图 2,按下红色 X 关闭窗口。
  • 按下按钮 1,出现绘图 1,按下红色 X 关闭窗口。
  • 按下按钮 2,绘图 2 出现,但绘图 1 也出现,按红色 X 关闭绘图 2,绘图 1 也关闭。
  • 从这里开始,按钮 1 会显示图 1,按钮 2 会显示两个图。

基于对类似问题的回答(例如matplotlib.pyplot will not forget previous plots - how can I flush/refresh?),我尝试了各种方法,例如关注plt.show() 和plt.cla() plt.clf() 和plt.close(),但它们都没有解决问题。正如您在我的代码中看到的那样,我已将 plt.close() 添加到 PyQt5 窗口关闭事件以及常规 MPL 图中,但这似乎无济于事。

编辑:既然问题已经得到解答,我已经清理了一些随后的思考并将其简化为相关信息。

我在我的工作 Linux 机器(最初在 Mac 上开发)上尝试了这段代码,但没有得到相同的双标行为。这表明它依赖于平台。

在 Linux 上,我实际上可以从 plot2 方法中删除 plt.close(),我可以从 PlotWindow 类中删除 closeEvent 方法,它可以正常工作而不会显示任何双图。

【问题讨论】:

  • 几个可能的错误......如果您的代码是直接复制的,那么可能没有帮助。但是在使用 guis 时,最小的事情有时会导致大问题......无论如何。在 plot1 下,您创建了一个 PlotWindow 并传入 self.x 两次,但它需要 x 和 y。此外,在 plot2 中,您使用 plt.figure(),但在 PlotCanvas 中,您使用 matplotlib.figure.Figure()。不知道为什么要使用两种不同的实现
  • 老实说,将其剥离为有效的东西有点折磨人,我不得不稍微改变一些小东西,例如正在处理的数据。修复了 self.x 重复,正如您正确预期的那样,它应该是 x,y。至于使用不同的实现, plt.figure() 是我的首选快速绘图方式,所以我转向快速获得顶级绘图而无需创建新的绘图窗口。我很确定我会从一些在线示例中获得 matplotlib.figure.Figure() 用法。现在已经尝试了这两种实现,但问题仍然存在。
  • 这可能很愚蠢,但是您是否尝试过在 plot2 创建中使用 fig=plt.figure(1) ?可能两个调用实际上都指的是同一个图形窗口(即使您试图避免这种情况)。在其中添加“1”将(希望)强制它使用不同的数字
  • 刚试了一下 Andrew 没区别 :-(

标签: matplotlib pyqt5


【解决方案1】:

好的,通过试用结束错误,我已经解决了这个问题。我并没有声称确切知道它是如何修复的机制。也许看到我是如何修复它的,一些聪明的人可以解释它为什么修复它。

如果我将PlotCanvas 中的closeEvent 方法更改为以下内容(我已用星号标记了添加的行):

    def closeEvent(self, event):
        """ Delete figure on closing window. """
        self.canvas.ax.cla() # ****
        self.canvas.fig.clf() # ****
        plt.close(self.canvas.fig)
        super(PlotWindow, self).closeEvent(event) 

并将plot2 方法更改为以下内容:

    def plot2(self):
        """ Plot using regular MPL call. """
        fig = plt.figure()
        ax = fig.add_subplot(111)
        ax.plot([10, 20, 30], [4, 8, 2])
        plt.show()
        ax.cla() # ****
        fig.clf() # ****
        plt.close(fig)

我现在没有出现多个图。我原以为axes 会在关闭时与figure 容器一起关闭,但显然不是。我也希望我能获得独立于平台的相同行为,但 MacOSX 后端的行为似乎与其他人不同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-02-15
    • 1970-01-01
    • 2011-11-14
    • 1970-01-01
    • 1970-01-01
    • 2015-10-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多