将新窗口小部件设置为QScrollArea

时间:2016-08-03 07:29:42

标签: python qt pyside

这是我编写的应用程序的简化。

应用程序的主窗口有一个按钮和一个复选框。 该复选框位于QScrollArea内(通过小部件)。 该复选框有一个数字,表明该复选框的创建次数。 单击该按钮将打开一个带有刷新按钮的对话框。

单击刷新按钮将使用新复选框将新窗口小部件设置到滚动区域。

该复选框有一个上下文菜单,可以打开相同的对话框。 但是,当使用复选框的上下文菜单触发的对话框创建窗口小部件时,应用程序崩溃并出现以下错误:

  

2016-08-03 09:22:00.036 Python [17690:408202] modalSession已经发布   提前退出 - 检查对endModalSession的重新调用:

     

Python(17690,0x7fff76dcb300)malloc: *对象的错误   0x7fff5fbfe2c0:未分配被释放的指针              * 在malloc_error_break中设置断点以进行调试

单击按钮打开对话框并单击对话框中的刷新时,不会发生崩溃。

崩溃发生在Mac和Windows上。 我使用Python 2.7.10和PySide 1.2.4

Context menu of the checkbox Main Window

#!/usr/bin/env python
import sys
from itertools import count
from PySide import QtCore, QtGui

class MainWindow(QtGui.QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self.centralwidget = QtGui.QWidget(parent=self)

        self.open_diag_btn = QtGui.QPushButton('Open dialog', parent=self)
        self.open_diag_btn.clicked.connect(self.open_dialog)
        self.scroll_widget = QtGui.QScrollArea(parent=self)

        layout = QtGui.QGridLayout(self.centralwidget)
        layout.addWidget(self.scroll_widget)
        layout.addWidget(self.open_diag_btn)
        self.setCentralWidget(self.centralwidget)
        self.set_scroll_widget()

    def open_dialog(self):
        dialog = Dialog(parent=self)
        dialog.refresh.connect(self.set_scroll_widget)  # Connecting the signal from the dialog to set a new widget to the scroll area
        dialog.exec_()
        # Even if I call the function here, after the dialog was closed instead of using the signal above the application crashes, but only via the checkbox
        # self.set_scroll_widget()

    def set_scroll_widget(self):
        """Replacing the widget of the scroll area with a new one.
           The checkbox in the layout of the widget has an class instance counter so you can see how many times the checkbox was created."""
        widget = QtGui.QWidget()
        layout = QtGui.QVBoxLayout(widget)
        widget.setLayout(layout)
        open_diag_check = RefreshCheckbox(parent=self)
        open_diag_check.do_open_dialog.connect(self.open_dialog)  # Connecting the signal to open the dialog window

        layout.addWidget(open_diag_check)
        self.scroll_widget.setWidget(widget)


class RefreshCheckbox(QtGui.QCheckBox):
    """A checkbox class that has a context menu item which emits a signal that eventually opens a dialog window"""
    do_open_dialog = QtCore.Signal()
    _instance_counter = count(1)

    def __init__(self, *args, **kwargs):
        super(RefreshCheckbox, self).__init__(unicode(self._instance_counter.next()), *args, **kwargs)
        self.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
        action = QtGui.QAction(self)
        action.setText("Open dialog")
        action.triggered.connect(self.emit_open_dialog)
        self.addAction(action)

    def emit_open_dialog(self):
        self.do_open_dialog.emit()


class Dialog(QtGui.QDialog):
    """A dialog window with a button that emits a refresh signal when clicked.
       This signal is used to call MainWindow.set_scroll_widget()"""
    refresh = QtCore.Signal()

    def __init__(self, *args, **kwargs):
        super(Dialog, self).__init__(*args, **kwargs)
        self.refresh_btn = QtGui.QPushButton('Refresh')
        self.refresh_btn.clicked.connect(self.do_refresh)
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.refresh_btn)

    def do_refresh(self):
        self.refresh.emit()


if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    mySW = MainWindow()
    mySW.show()
    mySW.raise_()
    app.exec_()

1 个答案:

答案 0 :(得分:1)

看起来Python正试图删除一个不再存在的对象(或其中一个子对象) - 但是导致这种情况发生的原因并不完全清楚。有问题的对象是滚动区域上设置的小部件。如果你明确地提到它:

    def set_scroll_widget(self):
        self._old_widget = self.scroll_widget.takeWidget()
        ...

您的示例将不再转储核心。

我认为运行带有exec的对话框可能会以某种方式成为问题的近因,因为这意味着它将使用自己的事件循环运行(这可能会影响与删除相关的事件的顺序)。通过使用show

运行对话框,我能够为您的示例找到更好的解决方案
def open_dialog(self):
    dialog = Dialog(parent=self)
    dialog.setAttribute(QtCore.Qt.WA_DeleteOnClose)
    dialog.refresh.connect(self.set_scroll_widget)
    dialog.setModal(True)
    dialog.show()

这样做意味着不再需要保持对前一个滚动区域小部件的显式引用。