内部 C++ 对象已被删除(pyside)

Posted

技术标签:

【中文标题】内部 C++ 对象已被删除(pyside)【英文标题】:Internal C++ object already deleted (pyside) 【发布时间】:2016-08-27 23:31:01 【问题描述】:

这个程序的目的是将tradeWindow显示为一个QWidget,然后在每次调用doStuff(通过按钮)时显示一个QDialog(如果有结果)。该代码第一次工作,但第二次我收到错误消息:

Traceback (most recent call last):
  File "GUI.py", line 68, in doStuff
    popup = Dialog((Qt.WindowSystemMenuHint | Qt.WindowTitleHint), popLayout)
  File "GUI.py", line 47, in __init__
    self.setLayout(popLayout)
RuntimeError: Internal C++ object (PySide.QtGui.QHBoxLayout) already deleted.

当我第一次关闭 QDialog 时,我的布局似乎被删除了。 将 popLayout = QHBoxLayout() 移动到 doStuff 的开头,我认为这会解决这个问题,而是给我这个错误:

Traceback (most recent call last):
   File "GUI.py", line 69, in doStuff
     popup = Dialog((Qt.WindowSystemMenuHint | Qt.WindowTitleHint), popLayout)
   File "GUI.py", line 47, in __init__
     self.setLayout(popLayout)
NameError: name 'popLayout' is not defined

这对我来说根本没有多大意义,因为它应该总是在被引用之前被定义?反正我找不到问题。我确信我的很多代码都可以改进,而且我对类等很陌生。

如果您对如何每次打开 QDialog 有任何比我目前正在尝试的更好的提示或其他有用的提示,请不要犹豫提及。 (尽量忽略蹩脚的命名约定,我会在未来解决这个问题。)

感谢您的帮助!

#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
from PySide.QtCore import *
from PySide.QtGui import *
import webbrowser

class Window(QWidget):
    def __init__(self, windowTitle, layout):
        super().__init__()
        self.resize(800,500)
        self.setWindowTitle(windowTitle)
        self.setLayout(layout)

class TextField(QTextEdit):
    def __init__(self, tooltip, layout):
        super().__init__()
        self.setToolTip(tooltip)
        layout.addWidget(self)

class Button(QPushButton):
    def __init__(self, text, layout):
        super().__init__()
        self.setText(text)
        layout.addWidget(self)

class Label(QLabel):
    def __init__(self, text, layout):
        super().__init__()
        self.setText(text)
        layout.addWidget(self)

class Table(QTableWidget):
    def __init__(self, layout):
        super().__init__()
        self.cellDoubleClicked.connect(self.slotItemDoubleClicked)
        layout.addWidget(self)
    def slotItemDoubleClicked(self,row,col):
        if col == 0 or col == 1:
            webbrowser.open(self.item(row, 1).text())

class Dialog(QDialog):
    def __init__(self, flags, layout):
        super().__init__()
        self.setWindowFlags(flags)    
        self.resize(800,500)
        self.setLayout(popLayout)

#Layouts        
mainLayout = QVBoxLayout()
subLayout = QHBoxLayout()
subLayout2 = QHBoxLayout()      
mainLayout.addLayout(subLayout)
mainLayout.addLayout(subLayout2)          
popLayout = QHBoxLayout()

#Main            
tradeApp = QApplication(sys.argv)         
textedit = TextField('bla',subLayout)
textedit2 = TextField('bla2',subLayout)
label = Label('Hover over input fields for instructions.', subLayout2)
button = Button('click me', subLayout2)
label2 = Label('Hover over input fields for instructions.', subLayout2)

def doStuff():
    gameResults = 'doom' : '111232', 'quake' : '355324'
    if len(gameResults) > 0:
        popup = Dialog((Qt.WindowSystemMenuHint | Qt.WindowTitleHint), popLayout)
        table = Table(popLayout)
        table.setRowCount(len(gameResults))
        table.setColumnCount(2);
        table.setHorizontalHeaderItem(0, QTableWidgetItem("Game"))
        table.setHorizontalHeaderItem(1, QTableWidgetItem("URL"))
        for index, game in enumerate(sorted(gameResults)):
            table.setItem(index,0,QTableWidgetItem(game))
            table.item(index,0).setFlags( Qt.ItemIsSelectable |  Qt.ItemIsEnabled )
            table.setItem(index,1,QTableWidgetItem('http://store.steampowered.com/app/'+gameResults[game]+'/'))
            table.item(index,1).setFlags( Qt.ItemIsSelectable |  Qt.ItemIsEnabled )
        table.resizeColumnsToContents()
        popup.exec_()
    else:
        msgBox = QMessageBox()
        msgBox.setText("No results.")
        msgBox.exec_()

button.clicked.connect(doStuff)
tradeWindow = Window('Tradefinder', mainLayout)
tradeWindow.show()
tradeApp.exec_()

【问题讨论】:

【参考方案1】:

在您的Dialog 关闭并且引用它的popup 变量超出范围后,Python 将对它进行垃圾收集。这会导致整个底层 C++ 对象(包括其所有子小部件和布局)被删除。但是,您保留了对对话框使用的布局的引用,因此在您第二次尝试打开对话框时,该布局将被删除。

我觉得奇怪的是,您在 Dialog 类之外对 Dialog 类进行了所有初始化。相反,我建议将popLayout 的创建以及table 的所有创建和设置移到Dialog 类中。这样每次打开对话框时都会创建布局。

您需要将gameResults 作为参数添加到Dialog__init__ 方法中,并且您还可以删除您目前拥有的layout 参数,因为它没有被使用。

完成此操作后,您的 Dialog 类应如下所示:

class Dialog(QDialog):
    def __init__(self, flags, gameResults):
        super().__init__()
        self.setWindowFlags(flags)    
        self.resize(800,500)
        popLayout = QHBoxLayout()
        self.setLayout(popLayout)

        table = Table(popLayout)
        table.setRowCount(len(gameResults))
        table.setColumnCount(2);
        table.setHorizontalHeaderItem(0, QTableWidgetItem("Game"))
        table.setHorizontalHeaderItem(1, QTableWidgetItem("URL"))
        for index, game in enumerate(sorted(gameResults)):
            table.setItem(index,0,QTableWidgetItem(game))
            table.item(index,0).setFlags( Qt.ItemIsSelectable |  Qt.ItemIsEnabled )
            table.setItem(index,1,QTableWidgetItem('http://store.steampowered.com/app/'+gameResults[game]+'/'))
            table.item(index,1).setFlags( Qt.ItemIsSelectable |  Qt.ItemIsEnabled )
        table.resizeColumnsToContents()

您的 doStuff() 方法应如下所示:

def doStuff():
    gameResults = 'doom' : '111232', 'quake' : '355324'
    if len(gameResults) > 0:
        popup = Dialog((Qt.WindowSystemMenuHint | Qt.WindowTitleHint), gameResults)
        popup.exec_()
    else:
        msgBox = QMessageBox()
        msgBox.setText("No results.")
        msgBox.exec_()

我对您的代码进行了这些更改,并且能够多次打开该对话框。

我会让你以同样的方式将你的主窗口设置代码移动到你的 Window 类中。

最后,请注意我只使用 PyQt 进行了测试。但是,我希望我的更改也适用于 PySide。

【讨论】:

谢谢 :) 我正在将代码重构为更加面向类的方法,因为我之前已经启动并运行了一个“原型”,而我昨天刚刚学习了类的基础知识。总是觉得使用起来有点烦人,但从现在开始我想我会更喜欢上课。仍然是新的,但非常感谢所有的输入。我现在将浏览这篇文章并在接受答案之前进行更改,但感谢您提供的内容非常丰富的帖子:) 效果很好,似乎也是一种更好的处理方式。我现在也将对主窗口进行更改。也谢谢你教育我:) 不太确定在上课时我应该在哪里划清界限。现在我正在考虑是否应该在主窗口中定义 doStuff 函数,我应该只传递游戏结果? @raecer:我建议将doStuff() 函数移到主窗口中。

以上是关于内部 C++ 对象已被删除(pyside)的主要内容,如果未能解决你的问题,请参考以下文章

在“更新 Maven 项目”期间发生内部错误。首选项节点“org.eclipse.wst.validation”已被删除

对对象成员、对象内部、向量内部所做的更改不会在方法结束后继续存在。 C++

编写 C++ API - 如何保持对 API 内部对象的外部引用?

PySide 中的多线程提升 Python C++ 代码

C++类和对象:构造函数初始化友元匿名对象内部类

C++类和对象:构造函数初始化友元匿名对象内部类