问题描述
我不确定如何解决以下问题:
I'm unsure how to solve the following problem:
- 我有一个PyQT应用程序,只要您按一下按钮
- 我正在保存我的应用程序类(
self.w
)中的此弹出窗口的引用 - 如果我关闭弹出窗口,变量
self.w
将仍然保留对封闭窗口的引用 - 如果我的代码中的某个地方我调用
self.w.repaint()
我收到错误消息RuntimeError:底层C / C ++对象已被删除
。这个错误是由于我们仍然引用存储在self.w
中的关闭窗口。
- I have a PyQT app which opens some child windows whenever you press a button
- I'm saving the reference to this pop-up window in my application class (
self.w
below) - If I close the pop-up window, the variable
self.w
will still hold a reference to the closed window - if somewhere in my code I call
self.w.repaint()
I get an error messageRuntimeError: underlying C/C++ object has been deleted
. This error is due to the fact that we still have a reference to the closed window stored inself.w
.
问题:
如何修改下面的代码,以便当弹出窗口关闭时,属性 self.w
自动设置为无
什么是好的和穷的实现这个方法?我想将 self.w
设置为无
的原因是我可以检查这个属性是否无
,如果是,我可以在调用 repaint()
之前重新弹出一个弹出窗口,从而避免错误消息。
Question:How do I have to modify the code below, so that when the pop-up is closed, the attribute self.w
is set to None
automatically? What are good and poor ways of implementing this? The reason I want to set self.w
to None
is that I'll be able to check whether this attribute is None
and in case it is, I can re-initalize a pop-up window before calling repaint()
and thus avoiding the error message.
#!/usr/bin/env python
#-*- coding: utf-8 -*-
import sys
from PyQt4.Qt import *
class MyPopup(QWidget):
def __init__(self):
QWidget.__init__(self)
def paintEvent(self, e):
dc = QPainter(self)
dc.drawLine(0, 0, 100, 100)
dc.drawLine(100, 0, 0, 100)
class MainWindow(QMainWindow):
def __init__(self, *args):
QMainWindow.__init__(self, *args)
self.cw = QWidget(self)
self.setCentralWidget(self.cw)
self.btn1 = QPushButton("Click me", self.cw)
self.btn1.setGeometry(QRect(0, 0, 100, 30))
self.connect(self.btn1, SIGNAL("clicked()"), self.doit)
self.w = None
def doit(self):
print "Opening a new popup window..."
self.w = MyPopup()
self.w.setGeometry(QRect(100, 100, 400, 200))
self.w.show()
class App(QApplication):
def __init__(self, *args):
QApplication.__init__(self, *args)
self.main = MainWindow()
self.connect(self, SIGNAL("lastWindowClosed()"), self.byebye )
self.main.show()
def byebye( self ):
self.exit(0)
def main(args):
global app
app = App(args)
app.exec_()
if __name__ == "__main__":
main(sys.argv)
推荐答案
要回答您的问题:
To answer your question:
你应该尝试在弹出类的init方法中添加它:
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
You should try adding this in your popup class' init method:self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
你会在文档中找到它:
You will find it in the docs here:http://qt-project.org/doc/qt-4.8/qt.html#WidgetAttribute-enum
回想起来,如果你的self.w.repaint()上的调用会抛出该错误 RuntimeError:底层C / C ++对象已被删除
,这意味着对象实际上已被删除。 self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
将确保对象确实在关闭时被删除。这是最干净的方法。
On retrospect, if a call on your self.w.repaint() throws that error RuntimeError: underlying C/C++ object has been deleted
, it means that the object has actually been deleted. self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
will ensure that the object does get deleted on close. This is the cleanest way to do it.
从您的评论中收集,您希望在发生此关闭事件时清理您的持有此小部件的类属性。要做到这一点,我们可以让弹出窗口关闭时发出信号,主窗口可以捕捉到释放。你可以这样做:
Gathering from your comments, you want to cleanly free up your class property that holds this widget when this close event occurs. To do this, we can just have the popup emit a signal when it closes, that the main window can catch to do the freeing up. You can do this like so:
#!/usr/bin/env python
#-*- coding: utf-8 -*-
import sys
from PyQt4.Qt import *
from PyQt4 import QtCore
class MyPopup(QWidget):
close_signal = pyqtSignal()
def __init__(self):
QWidget.__init__(self)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
def paintEvent(self, e):
dc = QPainter(self)
dc.drawLine(0, 0, 100, 100)
dc.drawLine(100, 0, 0, 100)
def closeEvent(self, event):
self.on_close()
def on_close(self):
""" Perform on close stuff here """
self.close_signal.emit()
class MainWindow(QMainWindow):
def __init__(self, *args):
QMainWindow.__init__(self, *args)
self.cw = QWidget(self)
self.setCentralWidget(self.cw)
self.btn1 = QPushButton("Click me", self.cw)
self.btn1.setGeometry(QRect(0, 0, 100, 30))
self.connect(self.btn1, SIGNAL("clicked()"), self.doit)
self.btn2 = QPushButton("repaint me", self.cw)
self.btn2.setGeometry(QRect(100, 30, 200, 50))
self.connect(self.btn2, SIGNAL("clicked()"), self.repaintPopup)
self.w = None
def doit(self):
print "Opening a new popup window..."
self.w = MyPopup()
self.w.setGeometry(QRect(100, 100, 400, 200))
self.w.show()
self.w.close_signal.connect(self.on_popup_closed)
def repaintPopup(self):
self.w.repaint()
def on_popup_closed(self):
""" Cleanup the popup widget here """
print "Popup closed."
self.w = None
class App(QApplication):
def __init__(self, *args):
QApplication.__init__(self, *args)
self.main = MainWindow()
self.connect(self, SIGNAL("lastWindowClosed()"), self.byebye )
self.main.show()
def byebye( self ):
self.exit(0)
def main(args):
global app
app = App(args)
app.exec_()
if __name__ == "__main__":
main(sys.argv)
这里,尽管您的弹出窗口是没有父项的独立窗口小部件。这就是为什么它显示为一个窗口。如果要维护父子关系,您的弹出窗口必须是一个窗口(例如QMainWindow),MyPopup QWidget作为其中心小部件,您的主窗口设置为父级。这将确保该弹出窗口始终被视为主窗口的小孩,并允许弹出窗口访问主窗口使用方法。为了执行这个改变,你只需要重构一下:
Here, though your Popup is a standalone widget without any parent. That is why it shows up as a window. If you want to maintain a parent child relationship, your popup would have to be a window (eg. QMainWindow) with the MyPopup QWidget as its central widget, and your main window set as the parent. This will ensure that this popup will always be treated as your main window's child, and lets the popup access the main window using the nativeParentWidget() method. To perform that change, you'd just have to refactor a little like so:
#!/usr/bin/env python
#-*- coding: utf-8 -*-
import sys
from PyQt4.Qt import *
from PyQt4 import QtCore
class MyPopup(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
def paintEvent(self, e):
dc = QPainter(self)
dc.drawLine(0, 0, 100, 100)
dc.drawLine(100, 0, 0, 100)
class PopupWindow(QMainWindow):
close_signal = QtCore.pyqtSignal()
def __init__(self, *args):
QMainWindow.__init__(self, *args)
self.cw = MyPopup(parent=self)
self.setCentralWidget(self.cw)
self.setGeometry(QRect(100, 100, 400, 200))
self.cw.setGeometry(QRect(100, 100, 400, 200))
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
print "I am the popup Window. My parent is: %s" % self.nativeParentWidget() # access the parent here
def closeEvent(self, event):
""" Perform on close stuff here """
self.on_close()
def on_close(self):
self.close_signal.emit()
class MainWindow(QMainWindow):
def __init__(self, *args):
QMainWindow.__init__(self, *args)
self.cw = QWidget(self)
self.setCentralWidget(self.cw)
self.btn1 = QPushButton("Click me", self.cw)
self.btn1.setGeometry(QRect(0, 0, 100, 30))
self.connect(self.btn1, SIGNAL("clicked()"), self.doit)
self.btn2 = QPushButton("repaint me", self.cw)
self.btn2.setGeometry(QRect(100, 30, 200, 50))
self.connect(self.btn2, SIGNAL("clicked()"), self.repaintPopup)
self.w = None
def doit(self):
print "Opening a new popup window..."
self.w = PopupWindow(self)
self.w.show()
self.w.repaint()
self.w.close_signal.connect(self.on_popup_closed)
def repaintPopup(self):
self.w.repaint()
def on_popup_closed(self):
""" Cleanup the popup widget here """
print "Popup closed."
self.w = None
class App(QApplication):
def __init__(self, *args):
QApplication.__init__(self, *args)
self.main = MainWindow()
self.connect(self, SIGNAL("lastWindowClosed()"), self.byebye )
self.main.show()
def byebye( self ):
self.exit(0)
def main(args):
global app
app = App(args)
app.exec_()
if __name__ == "__main__":
main(sys.argv)
您可以使用sip模块的 isdeleted()
方法来测试您的对象是否已被删除。只需要额外检查一下。
You can use the sip module's isdeleted()
method to test if your object has infact been deleted. Just an extra check if you want to.
import sip
sip.isdeleted(self.w)
查看sip模块提供的更多用于处理swig / C / C ++对象的方法:
Check out more methods offered by the sip module for dealing with swig/C/C++ objects here: http://pyqt.sourceforge.net/Docs/sip4/python_api.html
我希望这是有用的。
这篇关于子窗口关闭时如何清除存储在父应用程序中的子窗口引用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!