回答你的问题:
我该如何修改下面的代码,以便当弹出窗口时
关闭后,属性 self.w 自动设置为 None ?什么是
实施这个的好方法和坏方法?
您应该尝试将其添加到弹出类的 init 方法中:self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
您可以在此处的文档中找到它:http://qt-project.org/doc/qt-4.8/qt.html#WidgetAttribute-enum http://qt-project.org/doc/qt-4.8/qt.html#WidgetAttribute-enum
回想起来,如果对 self.w.repaint() 的调用抛出该错误RuntimeError: underlying C/C++ object has been deleted
,这意味着该对象实际上已被删除。self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
将确保该对象在关闭时被删除。这是最干净的方法。
根据您的评论,您希望在发生此关闭事件时彻底释放保存此小部件的类属性。为此,我们可以让弹出窗口在关闭时发出一个信号,主窗口可以捕获该信号以进行释放。你可以这样做:
#!/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 作为其中心小部件,并将您的主窗口设置为父窗口。这将确保此弹出窗口始终被视为主窗口的子窗口,并让弹出窗口使用NativeParentWidget() http://qt-project.org/doc/qt-4.8/qwidget.html#nativeParentWidget方法。要执行该更改,您只需像这样进行一些重构:
#!/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()
方法来测试您的对象是否确实已被删除。如果您愿意,只需额外检查即可。
import sip
sip.isdeleted(self.w)
在此处查看 sip 模块提供的用于处理 swig/C/C++ 对象的更多方法:http://pyqt.sourceforge.net/Docs/sip4/python_api.html http://pyqt.sourceforge.net/Docs/sip4/python_api.html
我希望这是有用的。