2016-08-03 27 views
1

Dies ist eine Vereinfachung einer Anwendung, die ich geschrieben habe.PySide-Anwendung stürzt ab, wenn ein neues Widget auf QScrollArea gesetzt wird

Das Hauptfenster der Anwendung hat eine Schaltfläche und ein Kontrollkästchen. Das Kontrollkästchen befindet sich in einer QScrollArea (über ein Widget). Das Kontrollkästchen enthält eine Zahl, die angibt, wie oft dieses Kontrollkästchen erstellt wurde. Durch Klicken auf die Schaltfläche wird ein Dialogfeld mit einer Schaltfläche zum Aktualisieren geöffnet.

Wenn Sie auf die Schaltfläche zum Aktualisieren klicken, wird ein neues Widget auf den Bildlaufbereich mit einem neuen Kontrollkästchen gesetzt.

Das Kontrollkästchen hat ein Kontextmenü, das den gleichen Dialog öffnet. Wenn jedoch das Widget erstellt wird, um den Dialog über das Kontextmenü der Checkbox ausgelöst indem die Anwendung abstürzt mit dem folgenden Fehler:

2016-08-03 09:22:00.036 Python[17690:408202] modalSession has been exited prematurely - check for a reentrant call to endModalSession:

Python(17690,0x7fff76dcb300) malloc: * error for object 0x7fff5fbfe2c0: pointer being freed was not allocated * set a breakpoint in malloc_error_break to debug

Der Absturz passiert nicht, wenn Sie auf den Button um den Dialog zu öffnen und klicken Sie im Dialogfeld auf Aktualisieren.

Der Absturz passiert auf Mac und Windows. Ich verwende Python 2.7.10 mit pyside 1.2.4

Context menu of the checkboxMain 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_() 

Antwort

1

Es sieht aus wie Python versucht, ein Objekt (oder eines seiner untergeordneten Objekte), die nicht mehr da ist zu löschen - aber was genau das bewirkt, ist mir nicht ganz klar. Das problematische Objekt ist das Widget im Scroll-Bereich. Wenn Sie explizit einen Verweis darauf halten:

Ihr Beispiel wird nicht mehr Dump Core.

Ich denke, das Ausführen des Dialogs mit exec kann irgendwie die proximale Ursache des Problems sein, da dies bedeutet, dass es mit seiner eigenen Ereignisschleife ausgeführt wird (die sich auf die Reihenfolge der löschungsbezogenen Ereignisse auswirken kann). Ich konnte eine bessere Lösung für Ihr Beispiel finden, indem Sie den Dialog mit show ausgeführt wird:

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

Dinge zu tun, auf diese Weise bedeutet, es ist nicht mehr notwendig, einen ausdrücklichen Hinweis auf den vorherigen scroll-area-Widget zu halten.

+0

Ersetzen dialog.exec_() mit show() löst den Absturz. Die anderen Kommentare sind ebenfalls nützlich. Vielen Dank! – Nir