A FigureCanvasQTAgg nested in a QMdiSubWindow segfaults when i minimize the QMdiSubWindow

寵の児 提交于 2019-12-13 00:56:24

问题


I am trying to have FigureCanvasQTAgg inside QMdiSubWindow such that the user can create his/her own plots on the fly. I have made this very small self contained code:

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()

The problem occurs when i run the program and try to minimize the QtGui.QMdiSubWindow object. When I do that the program segfaults and exits with no error description. This could be an error in qt, in the python bindings or in the FigureCanvasQTAgg object. Of course it could also be me who just use these objects wrongly. Please help me understand why the segfault occurs when i minimize the subwindow and help me figure out how i can solve this problem. Thank you.

My environment is ubuntu 14.04 and using Qt version: 4.8.7 SIP version: 4.16.9 PyQt version: 4.11.4 MatplotLib version: 1.5.0

Here is an example of drag and drop properties set. It seems that there are issues with that as well.

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()

回答1:


The issue seems to be that when minimized the widget has a negative height (I guess that makes sense, but I can not find any documentation of this fact; I noticed this by adding some print statements). The solution is to just not draw in those cases. I have submitted a PR to fix this upstream, but you might need to monky patch matplotlib.backends.backend_qt5agg.FigureCanvasQTAggBase.__draw_idle_agg with:

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

Note that the qt5 in the module is not a typo, the Qt4 functionality is derived from the Qt5 support.




回答2:


The issue seems to revolve around an incorrect size being reported for the matplotlib widget. As @tcaswell points out, matplotlib should be fixed to ensure this doesn't cause a segfault.

I'm going to attack the problem from the other side and try to stop Qt from reporting bogus dimensions. It seems that using the "inbuilt" layout causes the issue. This is likely because the existence of the layout is inherited by QMdiSubWindow from QWidget, but the implementation of QMdiSubWindow likely does not use it correctly. As long as you use the QMdiSubWindow.setWidget() method, and create your own layouts, the segfault is avoided.

Here is some example code with a layout that you manage yourself:

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

EDIT

If you look at the underlying C++ implementation you can see that a call to QMdiSubWindow.setWidget() is a lot more complicated than just adding the widget to the layout!



来源:https://stackoverflow.com/questions/35977326/a-figurecanvasqtagg-nested-in-a-qmdisubwindow-segfaults-when-i-minimize-the-qmdi

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!