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