本文介绍了子窗口关闭时如何清除存储在父应用程序中的子窗口引用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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 message RuntimeError: 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 in self.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

我希望这是有用的。

这篇关于子窗口关闭时如何清除存储在父应用程序中的子窗口引用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-20 08:42