代码之家  ›  专栏  ›  技术社区  ›  Justin Iurman

QgraphicsItem在Pyqt中何时被销毁?

  •  1
  • Justin Iurman  · 技术社区  · 6 年前

    似乎是 QGraphicsItem 状态来自 To be destroyed by: C/C++ To be destroyed by: Python ,但未销毁,仍可以访问。这是预期行为吗?如果是,有人能解释吗?

    创建部分

    node = QGraphicsRectItem()
    self.scene.addItem(node)
    
    print("Deleted ? ", sip.isdeleted(node))
    print("Owned by Python ? ", sip.ispyowned(node))
    sip.dump(node)
    

    产量

    Deleted ?  False
    Owned by Python ?  False
    <PyQt5.QtWidgets.QGraphicsRectItem object at 0x7fcdb82371f8>
        Reference count: 3
        Address of wrapped object: 0x214bf80
        Created by: Python
        To be destroyed by: C/C++
        Parent wrapper: <PyQt5.QtWidgets.QGraphicsScene object at 0x7fcdb821ea68>
        Next sibling wrapper: <__main__.Host object at 0x7fcdb8237dc8>
        Previous sibling wrapper: NULL
        First child wrapper: NULL
    

    删除部分

    self.scene.removeItem(node)
    
    print("Deleted ? ", sip.isdeleted(node))
    print("Owned by Python ? ", sip.ispyowned(node))
    sip.dump(node)
    

    产量

    Deleted ?  False
    Owned by Python ?  True
    <PyQt5.QtWidgets.QGraphicsRectItem object at 0x7fcdb82371f8>
        Reference count: 2
        Address of wrapped object: 0x214bf80
        Created by: Python
        To be destroyed by: Python
        Parent wrapper: NULL
        Next sibling wrapper: NULL
        Previous sibling wrapper: NULL
        First child wrapper: NULL
    

    可以看到,它现在在删除后归python所有。它仍然存在。为什么?

    2 回复  |  直到 6 年前
        1
  •  1
  •   ekhumoro    6 年前

    当你 add the item to the scene, Qt takes ownership of it ,所以您不需要在Python端保留对它的显式引用。这意味着当场景被删除时,该项目的C++部分也会被删除——如果没有其他引用保存到Python部分,它也将被垃圾收集(因此将没有剩下任何东西)。

    当你 remove the item from the scene, ownership passes back to the caller ,他现在完全有责任删除它。如果 QGraphicsRectItem 继承 QObject ,你可以打电话 deleteLater() 删除C++部分——但这仍然会留给你空的Python包装器,所以它不会有太大的区别。

    删除pyqt包装器的方法与任何其他python对象相同,即使用 del 或者只是让它超出范围,让它以正常的方式被垃圾收集。没有特殊的Pyqt方法来删除这些对象,因为不需要特殊的方法。

    在您的示例中,实际上您从未尝试删除该项,因此SIP报告的信息完全如预期的那样。

    最后,请注意,始终可以独立于PYQT包装器删除C++部分。如果您这样做,然后尝试访问项的方法,您将得到 RuntimeError :

    >>> scene = QGraphicsScene()
    >>> node = QGraphicsRectItem()
    >>> scene.addItem(node)
    >>> del scene
    >>> node.rect()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    RuntimeError: wrapped C/C++ object of type QGraphicsRectItem has been deleted
    

    更新 :

    下面是一个演示,演示了当项目从场景中移除并允许其超出范围时,如何对其进行垃圾收集。当然,如果在其他地方有任何对该项目的引用(即,如果引用计数为>1),则不会删除该项目。

    import sip, gc
    from PyQt5 import QtCore, QtWidgets
    
    app = QtWidgets.QApplication(['test'])
    
    scene = QtWidgets.QGraphicsScene()
    
    def test():
        node = QtWidgets.QGraphicsRectItem()
        scene.addItem(node)
        scene.removeItem(node)
        print("Deleted ? ", sip.isdeleted(node))
        print("Owned by Python ? ", sip.ispyowned(node))
        sip.dump(node)
        print()
    
    test()
    
    print('gc collect...')
    
    gc.collect()
    
    for obj in gc.get_objects():
        if isinstance(obj, QtWidgets.QGraphicsRectItem):
            print(obj)
            break
    else:
        print('deleted')
    

    输出:

    Deleted ?  False
    Owned by Python ?  True
    <PyQt5.QtWidgets.QGraphicsRectItem object at 0x7f90f66d5798>
        Reference count: 3
        Address of wrapped object: 0x205bd80
        Created by: Python
        To be destroyed by: Python
        Parent wrapper: NULL
        Next sibling wrapper: NULL
        Previous sibling wrapper: NULL
        First child wrapper: NULL
    
    gc collect...
    deleted
    
        2
  •  0
  •   buck54321    6 年前

    删除后不会让它超出范围。它被从所有qt中删除,但python仍然具有 node 用于引用项的变量。