FigureCanvasQTAgg, вложенный в segfaults QMdiSubWindow, когда я минимизирую QMdiSubWindow

Я пытаюсь использовать FigureCanvasQTAgg внутри QMdiSubWindow, чтобы пользователь мог создавать свои собственные графики на лету. Я сделал этот очень маленький автономный код:

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. Когда я делаю это, программа segfaults и выходит без описания ошибки. Это может быть ошибка в 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()

2 ответа

Решение

Кажется, проблема в том, что когда свернутый виджет имеет отрицательную высоту (я думаю, это имеет смысл, но я не могу найти документацию по этому факту; я заметил это, добавив некоторые операторы печати). Решение состоит в том, чтобы просто не рисовать в этих случаях. Я отправил пиар, чтобы исправить это, но вам может понадобиться патч 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.

Кажется, проблема связана с неправильным размером, сообщаемым для виджета matplotlib. Как указывает @tcaswell, matplotlib должен быть исправлен, чтобы гарантировать, что это не вызовет segfault.

Я собираюсь атаковать проблему с другой стороны и попытаться помешать Qt сообщать о поддельных измерениях. Кажется, что использование "встроенного" макета вызывает проблему. Это, вероятно, потому что существование макета наследуется QMdiSubWindow от QWidget, но реализация QMdiSubWindow скорее всего не правильно его использует. Пока вы используете QMdiSubWindow.setWidget() метод, и создавать свои собственные макеты, segfault избегается.

Вот пример кода с макетом, которым вы управляете сами:

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

РЕДАКТИРОВАТЬ

Если вы посмотрите на базовую реализацию C++, вы увидите, что вызов QMdiSubWindow.setWidget() это намного сложнее, чем просто добавить виджет в макет!

Другие вопросы по тегам