【问题标题】:How to delete QTreeWidgetItem如何删除 QTreeWidgetItem
【发布时间】:2012-10-15 06:24:36
【问题描述】:

有几个网页说QTreeWidgetItem可以通过删除或QTreeWidget.clearing来删除。但是我下面的代码示例似乎没有这样做。我做错什么了吗?

#!/usr/bin/python
import sys
from PySide.QtGui import QApplication, QWidget, QTreeWidget, QTreeWidgetItem
#from PyQt4.QtGui import QApplication, QWidget, QTreeWidget, QTreeWidgetItem # Result was the same with `PySide`
import time

class TreeWidgetItemChild(QTreeWidgetItem):
    def __init__(self):
        super(TreeWidgetItemChild, self).__init__()
        print 'TreeWidgetItemChild init'

    def __del__(self):
        print 'TreeWidgetItemChild del'

def test_QTree_clear_children():
    tree = QTreeWidget()
    tree.setHeaderLabel('funksoul')
    i = TreeWidgetItemChild()    
    tree.addTopLevelItem(i)    
    print 'Before clearing'
    #tree.clear()                  # Didn't call destructor (__del__)
    #tree.removeItemWidget (i, 0)  # Didn't call destructor
    #i.__del__()                   # Called destructor but it's called again afterward
    del i                          # Didn't call destructor
    time.sleep(1)
    print 'After clearing'

if __name__ == '__main__':
    app = QApplication(sys.argv)
    test_QTree_clear_children()

打印为:

TreeWidgetItemChild init
Before clearing
After clearing
TreeWidgetItemChild del

在我看来 TreeWidgetItemChild 在进程终止时被删除,而不是我的任何删除操作。

【问题讨论】:

  • 不确定 python,但在 C++ 中删除 QListWidgetItem 也会使其从列表中删除(析构函数会这样做)。

标签: python qt pyside qtreewidget qtreewidgetitem


【解决方案1】:

作为Avaris'excellent answer 的尾声,让我们提出一个更多适用于所有小部件和小部件项目(而不仅仅是顶级树小部件项目)的通用方法。这是不是香格里拉好得令人难以置信?

引用马里奥的话:“哇!我们走吧!”

我们来了

具体来说,如果您的项目利用:

  • PySide2,导入shiboken2模块并将每个要删除的树部件项传递给shiboken2.delete()函数ala:

    # Well, isn't that nice. Thanks, Qt Company.
    from PySide2 import shiboken2
    
    # Add this item to this tree.
    tree = QTreeWidget()
    item = TreeWidgetItemChild()
    tree.addTopLevelItem(item)
    
    # Remove this item from this tree. We're done here, folks.
    shiboken2.delete(item)
    
  • PyQt5,导入sip 模块并将每个要删除的树小部件项传递给sip.delete() 函数ala:

    # Well, isn't that not-quite-so-nice. You are now required to import any
    # arbitrary PyQt5 submodule *BEFORE* importing "sip". Hidden side effects are
    # shameful, of course, but we don't make the rules. We only enforce them. For
    # detailed discussion, see:
    #
    #     http://pyqt.sourceforge.net/Docs/PyQt5/incompatibilities.html#pyqt-v5-11
    #
    # If your project requires PyQt5 >= 5.11, the following compatibility hack may
    # be safely reduced to the following one-liner:
    #
    #     from PyQt5 import sip
    from PyQt5 import QtCore
    import sip
    
    # Add this item to this tree.
    tree = QTreeWidget()
    item = TreeWidgetItemChild()
    tree.addTopLevelItem(item)
    
    # Remove this item from this tree.
    sip.delete(item)
    

不会出错

是的,这在所有平台和 (PyQt5|PySide2) 版本下都符合预期。特定于 Python 的 sip.delete()shiboken2.delete() 方法是底层 C++ delete 运算符的高级包装器 - 并且操作完全相同。对于 QTreeWidgetItem 实例,这重现了 C++ 行为,即立即从其父树中删除传递的项目。

是的,它既光荣又粗略。感谢alexisdmrelevant answer elsewhere 提供了这个过度劳累的答案背后的动力。 荣耀归于alexisdm

【讨论】:

    【解决方案2】:

    通过调用del i,您只是删除了引用,而不是它所引用的实际 C++ 对象(referent),而不是对象本身。

    将您的 TreeWidgetItemChild.__del__ 函数更改为:

    def __del__(self):
        treeWidget = self.treeWidget()
        #removing the QTreeItemWidget object
        treeWidget.takeTopLevelItem(treeWidget.indexOfTopLevelItem(self))
        print 'TreeWidgetItemChild del'
    

    【讨论】:

      【解决方案3】:

      您将树项目(即树节点)与给定项目可以包含的小部件混淆了。

      以下示例创建一个QTreeWidget 并向其中添加两项:顶级项和嵌套项。移除 cmets,您可以看到它们是如何从树中移除的。

      #!/usr/bin/env python
      import sys
      from PyQt4.QtGui import *
      
      class MyMainWindow(QMainWindow):
          def __init__(self, parent=None):
              super(MyMainWindow, self).__init__(parent)
              self.tree = QTreeWidget(self)
              self.setCentralWidget(self.tree)
              self.tree.setHeaderLabel('funksoul')
              i = QTreeWidgetItem(self.tree, ['top level'])   
              self.tree.addTopLevelItem(i)
              j = QTreeWidgetItem(i ,['nested level'])
              #i.takeChild(0)
              #self.tree.takeTopLevelItem(0)
      
      if __name__ == "__main__":
          app = QApplication(sys.argv)
          ui = MyMainWindow()
          ui.show()
          sys.exit(app.exec_())
      

      要从树中删除这两种类型的项目,您需要项目索引。如果您对要删除的项目有引用,则可以使用 indexOfTopLevelItemindexOfChild 函数获取相应的索引。

      【讨论】:

        【解决方案4】:

        在内存管理/删除对象的意义上,Python 与 C++ 不同。 Python 有一个垃圾收集器 (GC),可以自动管理对象的销毁。当对象的引用计数达到零时,就会发生这种情况。

        del i 仅表示“将引用计数减一”。它永远不会导致直接调用__del__。对象的__del__ 仅在引用计数达到零并且即将被垃圾回收时调用。 (虽然对于 CPython 来说是这样,但不能保证每个实现都可以。这取决于 GC 实现。所以你根本不应该依赖__del__

        长话短说,__del__ 的通话时间不明确。您永远不应该直接调用__del__(或任何其他__foo__ 特殊方法)。事实上,出于上述原因,您应该完全避免使用__del__(通常)。

        除此之外,还有一个问题。

        tree.removeItemWidget(i, 0)
        

        这不会从QTreeWidget 中删除项目。顾名思义,它从项目中删除 widget,而不是 QTreeWidgetItem。它对应于setItemWidget 方法,而不是addTopLevelItem 方法。

        如果您需要从树中删除特定项目,您应该使用takeTopLevelItem

        tree.takeTopLevelItem(tree.indexOfTopLevelItem(i))
        

        tree.clear() 很好。它将从树中删除每个顶级项目。

        【讨论】:

        • 基于我对 Qt 对象的 python 垃圾回收所做的一些测试,我发现 python 无法收集它们,尽管它有零个裁判。
        • @hus787:哦?你是怎么测试的?
        • 所以我开发了这个小型 GUI 应用程序(基于 Qt)并通过调用一个 python 函数来实例化它,该函数还返回它的引用名称(plugins.foo.bar.some_random_string,其父级是主 Maya 窗口)。然后在不关闭/隐藏窗口的情况下,调用del pluginsdel plugins.foo.bar.some_random_string,然后甚至调用gc.collect,并没有导致窗口消失,即被python 的垃圾收集破坏。 pluginsfoo 是包,bar 是模块,some_random_string 是窗口对象本身。
        • @hus787:不,它不是“无法访问”。您可以通过父级访问它(例如parent_object.children())。如果你在删除之前检查gc.get_referrers,除了你给的名字外,你还会看到父级也在那里。
        • @hus787:我希望这可以消除混淆:bpaste.net/show/tiHqOiU2NNJ87gpQIUnS 有一个 QPushButton 创建两个 QDialogs 并将它们存储为属性。一个对话框是无父级的,一个有QPushButton 作为父级。单击该按钮将删除属性(即删除这些引用)。无父对话框将被垃圾收集,因为它没有其他引用。 Parented one 保持打开状态,因为该按钮仍然存在并且它保留了一个参考。即使self.window 不再可用。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-04-05
        • 1970-01-01
        • 1970-01-01
        • 2012-08-21
        • 1970-01-01
        • 2016-10-12
        • 1970-01-01
        相关资源
        最近更新 更多