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

时间:2014-12-11 10:17:12

标签: python events pyqt pyqt4

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

  • 我有一个PyQT应用程序,只要按下按钮
  • 就会打开一些子窗口
  • 我正在我的应用程序类中保存对此弹出窗口的引用(self.w下面的内容)
  • 如果我关闭弹出窗口,变量self.w仍会保留对已关闭窗口的引用
  • 如果在我的代码中某处我调用了self.w.repaint(),我会收到一条错误消息RuntimeError: underlying C/C++ object has been deleted。此错误是由于我们仍然存在对self.w
  • 中存储的已关闭窗口的引用

问题: 如何修改下面的代码,以便在弹出窗口关闭时,属性self.w会自动设置为None?有什么好的和坏的方式来实现这个?我想将self.w设置为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)

1 个答案:

答案 0 :(得分:1)

回答你的问题:

  

如何修改下面的代码,以便弹出窗口时   关闭,属性self.w自动设置为None?是什么   好的和差的方式实现这个?

您应该尝试在弹出式课程中添加此项' init方法: self.setAttribute(QtCore.Qt.WA_DeleteOnClose)

您可以在此处的文档中找到它: 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)

虽然你的Popup是一个没有任何父级的独立小部件。这就是为什么它显示为一个窗口。如果要维护父子关系,则弹出窗口必须是一个窗口(例如QMainWindow),其中MyPopup QWidget作为其中心窗口小部件,并且您的主窗口设置为父窗口。这将确保此弹出窗口始终被视为主窗口的子窗口,并允许弹出窗口使用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

我希望这很有用。