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()
это намного сложнее, чем просто добавить виджет в макет!