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:Note that the
qt5
in the module is not a typo, the Qt4 functionality is derived from the Qt5 support.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
fromQWidget
, but the implementation ofQMdiSubWindow
likely does not use it correctly. As long as you use theQMdiSubWindow.setWidget()
method, and create your own layouts, the segfault is avoided.Here is some example code with a layout that you manage yourself:
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!