子窗口关闭时如何清除存储在父应用程序中的子窗口引用?

2024-02-11

我不确定如何解决以下问题:

  • 我有一个 PyQT 应用程序,每当您按下按钮时它都会打开一些子窗口
  • 我正在我的应用程序类中保存对此弹出窗口的引用(self.w below)
  • 如果我关闭弹出窗口,变量self.w仍将保留对关闭窗口的引用
  • 如果我在代码中的某个地方调用self.w.repaint()我收到一条错误消息RuntimeError: underlying C/C++ object has been deleted。这个错误是由于我们仍然有一个存储在关闭窗口的引用self.w.

问题:我该如何修改下面的代码,以便当弹出窗口关闭时,该属性self.w被设定为None自动地?实施这一点的好方法和坏方法是什么?我想设置的原因self.w to None我可以检查这个属性是否是None如果是这样,我可以在调用之前重新初始化一个弹出窗口repaint()从而避免错误消息。

#!/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)

回答你的问题:

我该如何修改下面的代码,以便当弹出窗口时 关闭后,属性 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

我希望这是有用的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

子窗口关闭时如何清除存储在父应用程序中的子窗口引用? 的相关文章

随机推荐