Issue
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:
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.
It seems my layout gets deleted when i close QDialog the first time.
Moving popLayout = QHBoxLayout()
to start of doStuff
which I thought would fix the problem gives me this error instead:
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
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.
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.)
Thank you for any help!
#!/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_()
Solution
After your Dialog
is closed, and the popup
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.
I find it odd that you're doing all of the initialization of your Dialog
class outside of the Dialog
class. Instead, I would recommend moving the creation of popLayout
, and all of the creation and setup of table
, inside your Dialog
class. This way the layout gets created each time the dialog is opened.
You'll need to add gameResults
as a parameter to the __init__
method of Dialog
, and you can also remove the layout
parameter you have there at the moment because it isn't used.
After doing this, your Dialog
class should look like the following:
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()
and your doStuff()
method should look like the following:
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_()
I made these changes to your code and I was able to open the dialog multiple times.
I'll leave it up to you to move your main window set-up code inside your Window
class in the same way.
Finally, please note that I have only tested this using PyQt. However, I would expect that my changes would also work for PySide.
Answered By - Luke Woodward
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.