2016-08-27 47 views
0

该程序的目的是将tradeWindow显示为QWidget,然后在每次调用doStuff(通过按钮)时显示QDialog(如果有结果)。代码工作第一次,但第二次我得到错误信息:已删除的内部C++对象(pyside)

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每次都比我目前尝试或其他有用的提示,请不要犹豫,以及提及。 (尽量忽略蹩脚的命名惯例,我将在未来修复此问题。)

谢谢任何​​帮助!

GUI example

#!/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类的所有初始化。相反,我会建议您在Dialog课程中创建popLayout以及table的所有创建和设置。这样每次打开对话框时都会创建布局。

你需要作为一个参数添加到gameResultsDialog__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。

+0

谢谢:)我是在重构代码的过程中,因为我之前有一个“原型”并运行了,所以我只研究了昨天的类的基础知识。总是觉得它有点烦人,但从现在开始,我认为我会更喜欢类。尽管如此,尽管如此欣赏所有的输入。我将通过这个帖子,并接受答案之前进行修改,但谢谢你的一个非常翔实的文章:) – raecer

+0

工程精美,似乎是一个更好的方式去做事情。我现在也将对主窗口进行更改。谢谢你也教育我:) – raecer

+0

不太确定我应该在哪里画班级。现在我在思考如果doStuff函数应该在mainwindow中定义,我应该只传入gameresults? – raecer