当我最小化 QMdiSubWindow 时,嵌套在 QMdiSubWindow 中的 FigureCanvasQTAgg 出现段错误
A FigureCanvasQTAgg nested in a QMdiSubWindow segfaults when i minimize the QMdiSubWindow
我正在尝试将 FigureCanvasQTAgg 放入 QMdiSubWindow 中,以便用户可以动态创建 his/her 自己的绘图。
我制作了这个非常小的独立代码:
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 对象时出现问题。当我这样做时,程序出现段错误并退出,没有错误描述。
这可能是 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()
问题似乎是当最小化时小部件具有负高度(我想这是有道理的,但我找不到关于这个事实的任何文档;我通过添加一些打印语句注意到了这一点)。解决方案就是不在这些情况下绘制。我已经提交了一个 PR 来修复这个上游,但你可能需要修改 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 小部件报告的不正确大小有关。正如 指出的那样,应该修复 matplotlib 以确保这不会导致段错误。
我将从另一个方面来解决这个问题,并尝试阻止 Qt 报告虚假维度。似乎使用 "inbuilt" 布局会导致问题。这可能是因为 QMdiSubWindow
从 QWidget
继承了布局的存在,但是 QMdiSubWindow
的实现可能没有正确使用它。只要使用 QMdiSubWindow.setWidget()
方法,并创建自己的布局,就可以避免段错误。
下面是一些示例代码,其中包含您自己管理的布局:
p = FigureCanvas(fig)
container = QtGui.QWidget()
layout = QtGui.QVBoxLayout(container)
layout.addWidget(p)
sub.setWidget(container)
编辑
如果您查看 underlying C++ implementation,您会发现调用 QMdiSubWindow.setWidget()
比仅将小部件添加到布局要复杂得多!
我正在尝试将 FigureCanvasQTAgg 放入 QMdiSubWindow 中,以便用户可以动态创建 his/her 自己的绘图。 我制作了这个非常小的独立代码:
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 对象时出现问题。当我这样做时,程序出现段错误并退出,没有错误描述。 这可能是 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()
问题似乎是当最小化时小部件具有负高度(我想这是有道理的,但我找不到关于这个事实的任何文档;我通过添加一些打印语句注意到了这一点)。解决方案就是不在这些情况下绘制。我已经提交了一个 PR 来修复这个上游,但你可能需要修改 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 小部件报告的不正确大小有关。正如
我将从另一个方面来解决这个问题,并尝试阻止 Qt 报告虚假维度。似乎使用 "inbuilt" 布局会导致问题。这可能是因为 QMdiSubWindow
从 QWidget
继承了布局的存在,但是 QMdiSubWindow
的实现可能没有正确使用它。只要使用 QMdiSubWindow.setWidget()
方法,并创建自己的布局,就可以避免段错误。
下面是一些示例代码,其中包含您自己管理的布局:
p = FigureCanvas(fig)
container = QtGui.QWidget()
layout = QtGui.QVBoxLayout(container)
layout.addWidget(p)
sub.setWidget(container)
编辑
如果您查看 underlying C++ implementation,您会发现调用 QMdiSubWindow.setWidget()
比仅将小部件添加到布局要复杂得多!