重新启动包含 matplotlib 的 PyQt QApplication canvas

Restarting PyQt QApplication that contains matplotlib canvas

您可以看到 window 在使用 QApplicaiton.exit() 时仍然存在,然后在第二次尝试重新启动时崩溃。认为这可能是一个线程问题,但当我尝试更细致的方法在退出前关闭 matplotlib canvas 时,它似乎仍然存在。

import sys
import matplotlib

matplotlib.use('Qt5Agg')

from PyQt5 import QtCore, QtGui, QtWidgets


from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg, NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure


class MplCanvas(FigureCanvasQTAgg):

    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):
    REBOOT_CODE = -654321
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        self.sc = MplCanvas(self, width=5, height=4, dpi=100)
        self.sc.axes.plot([0, 1, 2, 3, 4], [10, 1, 20, 3, 40])

        button = QtWidgets.QPushButton('reload', self)
        button.clicked.connect(lambda: self.reloadapp())

        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(button)
        layout.addWidget(self.sc)

        # Create a placeholder widget
        widget = QtWidgets.QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)


    def reloadapp(self):
        self.sc.close_event()
        QtWidgets.QApplication.exit( MainWindow.REBOOT_CODE )


if __name__ == '__main__':
    exit_code = MainWindow.REBOOT_CODE
    while exit_code == MainWindow.REBOOT_CODE:
        app = QtWidgets.QApplication(sys.argv)
        w = MainWindow()
        w.show()
        app.exec_()
        app = None

问题是 app = None 并没有消除 QApplication,因为在 Python 中,另一个变量维护对象的引用就足够了,这样它就可以保持活动状态,而 matplotlib 会发生这种情况维护 QApplication 的引用。一种可能的解决方案是使用 sip.delete 删除 C++ 对象。

另一个错误是您没有将 app.exec_() returns 分配给 exit_code ,这会产生无限循环。另一方面,没有必要使用 self.sc.close_event ().

from PyQt5 import QtCore, QtGui, QtWidgets, sip
if __name__ == "__main__":
    exit_code = MainWindow.REBOOT_CODE
    while exit_code == MainWindow.REBOOT_CODE:
        app = QtWidgets.QApplication(sys.argv)
        w = MainWindow()
        w.show()
        exit_code = app.exec_()
        sip.delete(app)
        app = None