内部 C++ 对象已删除 (pyside)
Internal C++ object already deleted (pyside)
该程序的目的是将 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_()
在您的 Dialog
关闭并且引用它的 popup
变量超出范围后,Python 将对其进行垃圾回收。这会导致整个底层 C++ 对象(包括其所有子小部件和布局)被删除。但是,您保留了对对话框使用的布局的引用,因此布局将在您第二次尝试打开对话框时被删除。
我觉得很奇怪,您在 Dialog
class 之外进行 Dialog
class 的所有初始化。相反,我建议将 popLayout
的创建以及 table
的所有创建和设置移动到您的 Dialog
class 中。这样每次打开对话框时都会创建布局。
您需要将 gameResults
作为参数添加到 Dialog
的 __init__
方法中,您也可以删除那里的 layout
参数此刻,因为它没有被使用。
完成此操作后,您的 Dialog
class 应如下所示:
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 设置代码移动到你的 Window
class 中。
最后,请注意,我只使用 PyQt 对此进行了测试。但是,我希望我的更改也适用于 PySide。
该程序的目的是将 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_()
在您的 Dialog
关闭并且引用它的 popup
变量超出范围后,Python 将对其进行垃圾回收。这会导致整个底层 C++ 对象(包括其所有子小部件和布局)被删除。但是,您保留了对对话框使用的布局的引用,因此布局将在您第二次尝试打开对话框时被删除。
我觉得很奇怪,您在 Dialog
class 之外进行 Dialog
class 的所有初始化。相反,我建议将 popLayout
的创建以及 table
的所有创建和设置移动到您的 Dialog
class 中。这样每次打开对话框时都会创建布局。
您需要将 gameResults
作为参数添加到 Dialog
的 __init__
方法中,您也可以删除那里的 layout
参数此刻,因为它没有被使用。
完成此操作后,您的 Dialog
class 应如下所示:
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 设置代码移动到你的 Window
class 中。
最后,请注意,我只使用 PyQt 对此进行了测试。但是,我希望我的更改也适用于 PySide。