【问题标题】:A FigureCanvasQTAgg nested in a QMdiSubWindow segfaults when i minimize the QMdiSubWindow当我最小化 QMdiSubWindow 时,嵌套在 QMdiSubWindow 段中的 FigureCanvasQTAgg
【发布时间】:2016-06-28 21:19:30
【问题描述】:

我试图在 QMdiSubWindow 中使用 FigureCanvasQTAgg,以便用户可以即时创建他/她自己的绘图。 我制作了这个非常小的自包含代码:

from PyQt4 import QtGui
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import sys

class ExampleApp(QtGui.QMainWindow):
    def __init__(self):
        super(self.__class__, self).__init__()
        self.mdiarea = QtGui.QMdiArea()
        self.setCentralWidget(self.mdiarea)
        sub = QtGui.QMdiSubWindow(self.mdiarea)
        fig = Figure()
        p = FigureCanvas(fig)
        sub.layout().addWidget(p)
        sub.show()

def main():
    app = QtGui.QApplication(sys.argv)
    form = ExampleApp()
    form.show()
    app.exec_()

if __name__ == '__main__':
    main()

当我运行程序并尝试最小化 QtGui.QMdiSubWindow 对象时会出现问题。当我这样做时,程序会出现段错误并退出而没有错误描述。 这可能是 qt、python 绑定或 FigureCanvasQTAgg 对象中的错误。当然也可能是我错误地使用了这些对象。请帮助我理解为什么在我最小化子窗口时会发生段错误并帮助我弄清楚如何解决这个问题。 谢谢。

我的环境是 ubuntu 14.04 并使用 Qt版本:4.8.7 SIP 版本:4.16.9 PyQt 版本:4.11.4 MatplotLib 版本:1.5.0

这里是拖放属性集的示例。好像也有问题。

from PyQt4 import QtGui
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import sys

class QtZListView(QtGui.QListView):
    def __init__(self, *args, **kwargs):
        QtGui.QListView.__init__(self, *args, **kwargs)
        self.model = QtGui.QStringListModel(['a','b','c'])
        self.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
        self.setModel(self.model)
        self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
        self.setDragEnabled(True)

    def setStringList(self, *args, **kwargs):
        return self.model.setStringList(*args, **kwargs)

class mplsubwindow(QtGui.QMdiSubWindow):

    def __init__(self, *args, **kwargs):
        QtGui.QMdiSubWindow.__init__(self, *args, **kwargs)
        self.setWindowTitle("testing")
        self.setAcceptDrops(True)
        self.resize(400,400)
        self.show()

    def dragEnterEvent(self, event):
        print('entering')
        super(mplsubwindow, self).dragEnterEvent(event)

    def dragMoveEvent(self, event):
        print('drag moving')
        super(mplsubwindow, self).dragMoveEvent(event)

    def dropEvent(self, event):
        print('dropped')
        super(mplsubwindow, self).dropEvent(event)

class ExampleApp(QtGui.QMainWindow):
    def __init__(self):
        super(self.__class__, self).__init__()
        mainwid = QtGui.QWidget()
        self.mdiarea = QtGui.QMdiArea()

        layout = QtGui.QGridLayout(mainwid)
        layout.addWidget(self.mdiarea)
        sub = mplsubwindow(self.mdiarea)
        sub.show()
        layout.addWidget(QtZListView())
        self.setCentralWidget(mainwid)
        #self.setWidget(mainwid)

def main():
    app = QtGui.QApplication(sys.argv)
    form = ExampleApp()
    form.show()
    app.exec_()

if __name__ == '__main__':
    main()

【问题讨论】:

  • 在 Windows 10、matplotlib v1.3.1、PyQt v4.11.1 上,我没有收到段错误,但我确实收到有关 matplotlib 渲染器的无效宽度/高度的警告。如果我通过 sub.setWidget(p) 而不是通过布局添加小部件,这些就会消失。让我知道这是否能解决您的问题,以便我将其发布为答案!
  • 您能将此作为错误报告给 mpl 吗?问题似乎是,当窗口最小化时,画布的请求高度为负数,Agg 不喜欢。
  • three_pineapples,这实际上似乎解决了问题。非常感谢。
  • three_pineapples,但是这意味着我只能在画布上添加一个小部件,我认为...因为如果我想添加 2 个或更多并在每个上调用 setwidget,它将替换前一个。跨度>

标签: python qt matplotlib pyqt qmdiarea


【解决方案1】:

问题似乎是,当最小化时,小部件的高度为负(我想这是有道理的,但我找不到任何有关此事实的文档;我通过添加一些打印语句注意到了这一点)。解决方案是在这些情况下不绘制。我已经提交了PR 来修复这个上游问题,但您可能需要修改 matplotlib.backends.backend_qt5agg.FigureCanvasQTAggBase.__draw_idle_agg 的僧侣补丁:

def __draw_idle_agg(self, *args):
    if self.height() < 0 or self.width() < 0:
        self._agg_draw_pending = False
        return
    try:
        FigureCanvasAgg.draw(self)
        self.update()
    finally:
        self._agg_draw_pending = False

请注意,模块中的qt5 不是拼写错误,Qt4 功能源自 Qt5 支持。

【讨论】:

  • 非常感谢您的快速回答。看来你已经在上游报告了这个错误,所以我不需要采取任何行动?你知道这个补丁发布之前或多或少需要多长时间吗?
【解决方案2】:

这个问题似乎与 matplotlib 小部件报告的大小不正确有关。正如@tcaswell 指出的那样,应该修复matplotlib 以确保这不会导致段错误。

我将从另一面解决问题并尝试阻止 Qt 报告虚假维度。似乎使用“内置”布局会导致问题。这很可能是因为布局的存在是由QMdiSubWindowQWidget 继承而来的,但是QMdiSubWindow 的实现很可能没有正确使用它。只要您使用QMdiSubWindow.setWidget() 方法,并创建自己的布局,就可以避免段错误。

以下是一些示例代码,其中包含您自己管理的布局:

p = FigureCanvas(fig)
container = QtGui.QWidget()
layout = QtGui.QVBoxLayout(container)
layout.addWidget(p)        
sub.setWidget(container)

编辑

如果您查看underlying C++ implementation,您会发现调用QMdiSubWindow.setWidget() 比将小部件添加到布局要复杂得多!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-11
    • 1970-01-01
    • 2019-11-14
    • 2017-11-03
    相关资源
    最近更新 更多