2016-08-27 3 views
0

Цель этой программы - показать tradeWindow как QWidget, а затем показать QDialog каждый раз, когда doStuff вызывается (через кнопку), если есть результаты. Код работает в первый раз, но второй раз я получаю сообщение об ошибке:Внутренний объект 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. Вместо этого я бы рекомендовал переместить создание popLayout и все создание и настройку table, внутри вашего класса Dialog. Таким образом, макет создается каждый раз при открытии диалога.

Вам нужно добавить gameResults в качестве параметра к __init__ метода Dialog, и вы также можете удалить параметр 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_() 

Я сделал эти изменения в свой код и I удалось открыть диалог несколько раз.

Я оставлю это для вас, чтобы переместить свой код настройки главного окна внутри вашего класса Window таким же образом.

Наконец, обратите внимание, что я тестировал это только с помощью PyQt. Однако я бы ожидал, что мои изменения также будут работать для PySide.

+0

Спасибо :) Я был в процессе рефакторинга кода для более классного подхода, так как раньше у меня был «прототип», и я только что изучил основы классов вчера. Всегда находил, что это немного раздражает, но теперь я думаю, что предпочитаю классы на самом деле. Но все же новые, хотя так оценивают все входные данные. Я сейчас займусь этим сообщением и сделаю изменения, прежде чем принимать ответ, но спасибо за очень информативный пост :) – raecer

+0

Прекрасно работает и кажется намного лучшим способом обойти все. Я также внес изменения в mainwindow. Спасибо за то, что я тоже обучил :) – raecer

+0

Не уверен, где я должен рисовать линию, когда дело доходит до занятий. Теперь я размышляю, следует ли определять функцию doStuff в mainwindow, и я должен просто пройти в gameresults? – raecer

Смежные вопросы