【问题标题】:PyQt Segmentation Fault on exit matplotlib退出 matplotlib 时出现 PyQt 分段错误
【发布时间】:2017-02-21 19:41:51
【问题描述】:

我正在使用 PyQt4 和 matplotlib 制作一个 gui。我发现this 对制作我正在尝试使用的选项卡式 gui 窗口很有帮助。一切似乎都运行良好(进行了一些小的调整);但是,每当我关闭主窗口时,我都会收到“Python 意外退出”错误以及分段错误。虽然这不会影响程序的运行,但它很烦人,我想找出问题所在。

现在,在试图弄清楚发生了什么的同时,我想到了下面的 MWE(如果你愿意的话,也可以是最小的损坏示例)

import sys

import matplotlib
matplotlib.use('Qt4agg')

import matplotlib.pyplot as plt

import numpy as np

from PyQt4 import QtCore
from PyQt4 import QtGui as qt


if __name__ == "__main__":

    fig = plt.figure()

    x, y = np.random.randn(2, 40)

    ax = fig.add_subplot(111)

    ax.plot(x, y, 'o')    

    ax.hold(False)

    app = qt.QApplication(sys.argv)

    # ui = MplMultiTab(figures=[fig], labels=["yay"])

    ui = qt.QMainWindow()
    ui.main_frame = qt.QWidget()

    vbox = qt.QVBoxLayout()

    vbox.addWidget(fig.canvas)

    ui.main_frame.setLayout(vbox)
    ui.setCentralWidget(ui.main_frame)

    ui.show()

    sys.exit(app.exec_())

问题似乎是将图形画布添加到窗口中,好像我不这样做而是制作一个空的gui(删除vbox.addWidget(fig.canvas))一切都很好并且没有段错误。

任何人都可以看到出了什么问题,还是 matplotlib 或 pyqt 中的错误?同样有趣的是,如果我将我的 gui 设置为类似于this answer,那么我就没有分段错误,但我无法真正弄清楚它们之间的区别是什么。

为了大家的信息,我在 python 3.5.2 上使用 PyQt 版本 4.11.4 和 OSX 10.11.6 上的 matplotlib 版本 1.5.3 运行它。

【问题讨论】:

    标签: matplotlib pyqt4


    【解决方案1】:

    PyQt documentation中所述:

    对于任何使用 Qt 的 GUI 应用程序,只有一个 QApplication 对象,无论该应用程序在任何给定时间是否有 0、1、2 个或更多窗口。

    我认为这里最有可能发生的是QApplication 在生成图形时由pyplot 静默创建。此 QApplication 用于管理 FigureManager 的主事件循环,它是由 pyplot 创建的用于在屏幕上显示图形的 GUI。

    所以,由于已经使用 pyplot 创建了一个 QApplication,我认为当在代码中进一步调用 qt.QApplication(sys.argv) 时通常会引发错误,但 Qt 似乎没有看到它并允许创建另一个Q应用。这可能是导致尝试关闭应用程序时发生冲突的原因。我看到 3 个不同的选项来解决这个问题:

    1 - 将 pyplot 代码放入您的 QApplication 中,以便 pyplot 看到并使用它,而不是构建自己的代码:

    import sys
    import matplotlib
    matplotlib.use('Qt4agg')
    import matplotlib.pyplot as plt
    import numpy as np
    from PyQt4 import QtGui as qt
    
    if __name__ == '__main__':
    
        app = qt.QApplication(sys.argv)
    
        fig, ax = plt.subplots()
        x, y = np.random.randn(2, 40)
        ax.plot(x, y, 'o')
    
        ui = qt.QWidget()
        vbox = qt.QVBoxLayout()
        vbox.addWidget(fig.canvas)
        ui.setLayout(vbox)
        ui.show()
    
        sys.exit(app.exec_())
    

    2 - 使用指向 pyplot 已经构建的 QApplication 的指针,而不是创建一个新的:

    import sys
    import matplotlib
    matplotlib.use('Qt4agg')
    import matplotlib.pyplot as plt
    import numpy as np
    from PyQt4 import QtGui as qt
    
    if __name__ == '__main__':
    
        fig, ax = plt.subplots()
        x, y = np.random.randn(2, 40)
        ax.plot(x, y, 'o')
    
        app = qt.QApplication.instance()
    
        ui = qt.QWidget()
        vbox = qt.QVBoxLayout()
        vbox.addWidget(fig.canvas)
        ui.setLayout(vbox)
        ui.show()
    
        sys.exit(app.exec_())
    

    3 - 正如matplotlib documentation 中所建议的,在 Qt 接口中嵌入 mpl 图形时避免使用 pyplot 接口,而是使用面向对象的 API。在这种情况下,您的 MWE 可以重写为:

    import sys
    import matplotlib
    from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg
    import numpy as np
    from PyQt4 import QtGui as qt
    
    if __name__ == '__main__':
    
        app = qt.QApplication(sys.argv)
    
        fig = matplotlib.figure.Figure()
        canvas = FigureCanvasQTAgg(fig)
        ax = fig.add_subplot(111)
        x, y = np.random.randn(2, 40)
        ax.plot(x, y, 'o')
    
        ui = qt.QWidget()
        vbox = qt.QVBoxLayout()
        vbox.addWidget(canvas)
        ui.setLayout(vbox)
        ui.show()
    
        sys.exit(app.exec_())
    

    【讨论】:

    • 感谢让-塞巴斯蒂安。实际上,我在重组代码时偶然发现了数字 1,所以现在我知道为什么我的问题消失了。顺便说一句,这是一个很棒且写得很好的答案。
    猜你喜欢
    • 2017-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-14
    • 2016-09-13
    • 1970-01-01
    • 2011-10-22
    相关资源
    最近更新 更多