Internal C++ object already deleted (pyside)
<p>The aim of this program is to show the tradeWindow as a QWidget and then show a QDialog each time doStuff is called (via button) if there are results. The code works first time, but second time i get error messages:</p>

<pre class="lang-none prettyprint-override"><code>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.
</code></pre>

<p>It seems my layout gets deleted when i close QDialog the first time.
Moving <code>popLayout = QHBoxLayout()</code> to start of <code>doStuff</code> which I thought would fix the problem gives me this error instead:</p>

<pre class="lang-none prettyprint-override"><code>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
</code></pre>

<p>That doesn't make much sense to me at all since it should always be getting defined before being referenced? I can't find the problem anyway. I'm sure a lot of my code could be improved as well as I am very new to classes etc. </p>

<p>If you have any tips on how to open the QDialog each time that is better than what I'm currently trying or other helpful tips, please don't hesitate to mention that as well. (Try to ignore the crappy naming convention, I will fix that in the future.)</p>

<p>Thank you for any help!</p>

<p><a href="http://i.stack.imgur.com/fGDea.png" rel="nofollow"><img src="http://i.stack.imgur.com/fGDea.png" alt="GUI example"></a></p>

<pre><code>#!/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) &gt; 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_()
</code></pre>

<p>After your <code>Dialog</code> is closed, and the <code>popup</code> variable referencing it goes out of scope, Python will garbage-collect it.  This causes the entire underlying C++ object, including all of its sub-widgets and layouts, to be deleted. However, you're keeping a reference to a layout used by the dialog, and hence the layout will have been deleted by the second time you try to open the dialog.</p>

<p>I find it odd that you're doing all of the initialization of your <code>Dialog</code> class outside of the <code>Dialog</code> class.  Instead, I would recommend moving the creation of <code>popLayout</code>, and all of the creation and setup of <code>table</code>, inside your <code>Dialog</code> class. This way the layout gets created each time the dialog is opened.</p>

<p>You'll need to add <code>gameResults</code> as a parameter to the <code>__init__</code> method of <code>Dialog</code>, and you can also remove the <code>layout</code> parameter you have there at the moment because it isn't used.  </p>

<p>After doing this, your <code>Dialog</code> class should look like the following:</p>

<pre><code>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()
</code></pre>

<p>and your <code>doStuff()</code> method should look like the following:</p>

<pre><code>def doStuff():
    gameResults = {'doom' : '111232', 'quake' : '355324'}
    if len(gameResults) &gt; 0:
        popup = Dialog((Qt.WindowSystemMenuHint | Qt.WindowTitleHint), gameResults)
        popup.exec_()
    else:
        msgBox = QMessageBox()
        msgBox.setText("No results.")
        msgBox.exec_()
</code></pre>

<p>I made these changes to your code and I was able to open the dialog multiple times.  </p>

<p>I'll leave it up to you to move your main window set-up code inside your <code>Window</code> class in the same way.</p>

<p>Finally, please note that I have only tested this using PyQt.  However, I would expect that my changes would also work for PySide.</p>

python qt pyside