QTreeWidget 选择第一个项目

5

我正在从XML文件中制作一个QTreeWidget。 XML文件如下所示,我想要树的名称:

<root>
    <f name='foo'>bar
        <f name='foo2'>baz</f>
    </f>
</root>

目前我正在使用以下代码来完成此操作(简化的代码)

import lxml.etree as et

#...

self.xml = et.XML(filters.filtersxml)
self.tree_widget = QTreeWidget(parent)

def add_items(parent, xmlroot):
    for i in xmlroot.getchildren():
        item = QTreeWidgetItem(parent, [i.get('name')])
        if len(i.getchildren()) != 0:
            add_items(item, i)

add_items(self.tree_widget, self.xml)

关于此问题我实际上有两个问题:

  1. 主要问题:是否有一种方法可以选择树中的第一个项目,这里是foo。我试图使用 setCurrentItem()setCurrentIndex() 做一些事情,但无法使其工作。我已经在Google上搜索了一下,但所有我找到的解决方案都是用于模型。
  2. (可选) 这个递归函数是处理这个问题的好方法,还是有更好的方法?
1个回答

10

递归很好,应该保持不变。针对你主要的问题,只需要这样做:

# after all your code
add_items(self.tree_widget, self.xml)

# select the root item
self.tree_widget.setCurrentItem(self.tree_widget.topLevelItem(0))

当你调用setCurrentItem时,确保该项已经被添加到树中,否则它不会真正起作用。有些方法要求该项已经与树相关联(例如setExpanded和setSelected)。

编辑

要递归地建立树结构而不影响树,请执行以下操作:

import lxml.etree as et

#...

self.xml = et.XML(filters.filtersxml)
self.tree_widget = QTreeWidget(parent)

def add_items(parent, xmlroot):
    output = []
    for i in xmlroot.getchildren():
        item = QTreeWidgetItem(parent, [i.get('name')])
        output.append(item)
        if len(i.getchildren()) != 0:
            add_items(item, i)
    return output

items = add_items(None, self.xml)
self.tree_widget.addTopLevelItems(items)
self.tree_widget.setCurrentItem(items[0])

编辑2:一次性全部加载

为了更进一步地进行优化,我个人会这样做来尽可能减少不必要的调用和列表:只构建一次子级循环:

import lxml.etree as et

#...

self.xml = et.XML(filters.filtersxml)
self.tree_widget = QTreeWidget(parent)

def create_item(parent, xmlroot):
    item = QTreeWidgetItem(parent, [xmlroot.get('name')])
    for xmlchild in xmlroot.getchildren():
       create_item(item, xmlchild)
    return item

items = [create_item(None, xmlchild) for xmlchild in self.xml.getchildren()]
self.tree_widget.addTopLevelItems(items)
if ( items ):
    self.tree_widget.setCurrentItem(items[0])

编辑 3:动态加载

既然提到了…一种动态加载子级的方法是存储每个层级,然后在展开后加载它们:

import lxml.etree as et
from PyQt4.QtGui import QTreeWidgetItem

# ...    

class XmlTreeWidgetItem(QTreeWidgetItem):
    def __init__( self, parent, xmlitem ):
        super(MyTreeWidgetItem, self).__init__(parent)
        self.setText(0, xmlitem.get('name'))
        self.setChildIndicatorPolicy(self.ShowIndicator)

        self._xmlitem = xmlitem
        self._loaded = False

    def loadChildren( self ):
        if ( self._loaded ):
            return

        self._loaded = True
        self.setChildIndicatorPolicy(self.DontShowIndicatorWhenChildless)
        for xmlchild in self._xmlitem.getchildren():
            XmlTreeWidgetItem(self, xmlchild)

# ...

class SomeClass(QWidget):
    def __init__( self, parent = None ):
        super(SomeClass, self).__init__(parent)

        self.tree_widget = QTreeWidget(parent)

        xml = et.XML(filters.filtersxml)
        items = [XmlTreeWidgetItem(None, xchild) for xchild in xml.getchildren()]
        self.tree_widget.addTopLevelItems(items)
        if ( items ):
            self.tree_widget.setCurrentItem(items[0])

        # create connections
        self.tree_widget.itemExpanded.connect(self.loadItem)

    def loadItem( self, item ):
        item.loadChildren()

是的,这通常是我做事的方式......不太确定哪种更好或更快,所以我会说两者都可行。我也建议像这样对树小部件进行任何修改时,同时在 blockSignals(True/False) 和 setUpdatesEnabled(False/True) 之间进行包装。 - Eric Hulser
1
这真的取决于你想要做什么 - 有时候我需要在创建过程中引用一个项目的树小部件,所以我会在创建过程中将它们添加到树中,而不是事后再添加,这种情况下,我会阻止信号被发送。大多数时候,我同意jdi的观点,先在树外部构建,然后在最后添加顶层项目是最简单的方法。我编辑了答案,以递归方式完成,而不是首先分配给树。 - Eric Hulser
不错的更新。我在想是否有一种修改方法,可以避免对整个未使用的子层级进行大量无用的列表附加操作。它只被用于返回根项目。也许可以检查 if parent is None - jdi
1
是的,这是一个很好的问题和话题。大家干杯。我刚刚更新了答案,并提供了一个没有递归的动态加载示例。 - Eric Hulser
@EricHulser,我很抱歉重新开启这个旧线程.. 但使用“lxml.objectify”怎么样? - gmas80
显示剩余12条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接