2016-08-03 3 views
1

Это упрощение приложения, которое я написал.Приложение PySide аварийно завершает работу при настройке нового виджета на QScrollArea

Главное окно приложения имеет кнопку и флажок. Флажок находится внутри QScrollArea (через виджет). Флажок имеет число, указывающее, сколько раз этот флажок был создан. Нажав на кнопку, вы откроете диалоговое окно с кнопкой обновления.

Нажав на кнопку обновления, вы установите новый виджет в область прокрутки с новым флажком.

Флажок имеет контекстное меню, открывающее тот же диалог. Однако, когда виджет создается с помощью диалогового окна, запускаемые в контекстном меню флажком сбой приложения со следующей ошибкой:

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

Катастрофа не происходит при нажатии на кнопку, чтобы открыть диалоговое окно и нажмите «Обновить» в диалоговом окне.

Аварийное происшествие происходит как на Mac, так и на Windows. Я использую Python 2.7.10 с PySide 1.2.4

Main WindowContext menu of the checkbox

#!/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

Похоже, 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() 

Поступать таким образом означает, что нет больше необходимости держать прямую ссылку на предыдущую прокрутке области виджета.

+0

Замена dialog.exec_() с помощью show() разрешает сбой. Другие комментарии также полезны. Благодаря! – Nir

Смежные вопросы