【问题标题】:Using QTreeWidgetItemIterator in PyQT4 to return isChecked from QTreeWidget as a dictionary (or...something)在 PyQT4 中使用 QTreeWidgetItemIterator 从 QTreeWidget 返回 isChecked 作为字典(或...某物)
【发布时间】:2013-12-16 19:06:56
【问题描述】:

检查最终代码的编辑!

所以...我承认由于缺乏知识,我在这里画了一个绝对空白,我只是展示我的代码并祈祷一下。

使用ekhumoro 编码的这个奇妙的 xml 到 QTreeWidget 生成器,我已在复选框(父节点的三态)中添加,现在我正在尝试遍历这些复选框并返回字典或列表列表或.. .something 与父:子(服务器:此树中的服务)关系,以便我可以根据树的检查结果构建文件夹的路径。这将在按下“开始”按钮时发生,作为系列中的第一个功能。

最终目标是拥有一个工具,该工具可以根据用户提供的开始和停止时间从平台上的多个服务器和每个服务器的服务中提取日志,并将这些日志传送到具有相同文件夹结构的管理框。

我已经成功地浏览了一个测试字典,根据 os.path.getctime 和 getmtime 搜索和定位文件,将它们压缩,然后将它们复制到一个单独的驱动器中,该驱动器具有作为函数的一部分创建的相同文件夹结构。 ..

但是,我发现几乎没有关于 TreeWidgetItemIterator 的文档(只有 http://pyqt.sourceforge.net/Docs/PyQt4/qtreewidgetitemiterator.html#details),这对我没有多大帮助。所以这个谜题的组成部分(也是最后一部分)让我迷失了!

XMLHandler:

from PyQt4 import QtCore, QtGui, QtXml
from PyQt4.QtXml import *


class XmlHandler(QXmlDefaultHandler):
    def __init__(self, root):
        QtXml.QXmlDefaultHandler.__init__(self)
        self._root = root
        self._item = None
        self._text = ''
        self._error = ''

    def startElement(self, namespace, name, qname, attributes):
        if qname == 'Machine' or qname == 'Feature':
            if self._item is not None:
                self._item = QtGui.QTreeWidgetItem(self._item)
            else:
                self._item = QtGui.QTreeWidgetItem(self._root)
            self._item.setData(0, QtCore.Qt.UserRole, qname)
            self._item.setText(0, 'Unknown Machine')
            if qname == 'Machine':
                self._item.setExpanded(False)
                self._item.setCheckState(0, QtCore.Qt.Unchecked)
                self._item.setFlags(QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsDragEnabled|QtCore.Qt.ItemIsUserCheckable|QtCore.Qt.ItemIsEnabled|QtCore.Qt.ItemIsTristate)
            elif qname == 'FeatureName':
                self._item.setText(1, self._text)
        self._text = ''
        return True

    def endElement(self, namespace, name, qname):
        if qname == 'FeatureName' or qname == 'MachineName':
            if self._item is not None:
                self._item.setText(0, self._text)
                self._item.setCheckState(0, QtCore.Qt.Unchecked)
        elif qname == 'Feature' or qname == 'Machine':
            self._item = self._item.parent()
        return True

    def characters(self, text):
        self._text += text
        return True

    def fatalError(self, exception):
        print('Parse Error: line %d, column %d:\n  %s' % (
              exception.lineNumber(),
              exception.columnNumber(),
              exception.message(),
              ))
        return False

使用 XmlHandler 制作小部件的类:

class MakeWidget(QtGui.QTreeWidget):
    def __init__(self):
        QtGui.QTreeWidget.__init__(self)
        self.header().setResizeMode(QtGui.QHeaderView.Stretch)
        self.setHeaderLabels(['Servers and Services'])
        source = QtXml.QXmlInputSource()
        source.setData(xml)
        handler = XmlHandler(self)
        reader = QtXml.QXmlSimpleReader()
        reader.setContentHandler(handler)
        reader.setErrorHandler(handler)
        reader.parse(source) 

在我的 GUI 中创建小部件:

        self.treeServiceSelection = MakeWidget(xml, parent=self.ui.groupBoxServiceSelection)

小部件有父母和孩子两层,仅此而已:

http://i.imgur.com/npPhG41.jpg

现在我们已经到了我被困的地方。我可以将信号与按钮按下联系起来,我可以做其他所有事情,但从 QTreeWidget 中获取已检查的项目。看来我需要构造一个迭代器并在它的 init 中包含 Checked 标志,但我尝试过的任何东西都想出了 bupkiss。我很乐意找到解决方案,而不是为我提供解决方案,但没有起点一直令人沮丧。

编辑:所以我实际发布的所有内容都没有任何用处,但 ekhumoro 了解我的问题的症结并提供了他的解决方案(接受的答案)。我添加了一个elif 来处理检查父母的任何事情(因为它似乎没有读到孩子被检查作为这样的效果):

def exportTree(self):
    mapping = defaultdict(list)
    root = self.tree.invisibleRootItem()
    for index in range(root.childCount()):
        parent = root.child(index)
        if parent.checkState(0) == QtCore.Qt.PartiallyChecked:
            features = mapping[parent.text(0)]
            for row in range(parent.childCount()):
                child = parent.child(row)
                if child.checkState(0) == QtCore.Qt.Checked:
                    features.append(child.text(0))
        elif parent.checkState(0) == QtCore.Qt.Checked:
            features = mapping[parent.text(0)]
            for row in range(parent.childCount()):
                features.append((parent.child(row)).text(0))
    return mapping

然后能够使用此示例根据 gui 中其他地方的另一个组合框选择特定子项:

def checkbox2mods(self):
    d = {'DMD2K8COR2VM': ['CC2DCP', 'Centercord'],
         'DMD2K8COR1VM': ['CC2DCP', 'Centercord']}
    root = self.tree.invisibleRootItem()
    if self.checkBox2.checkState() == QtCore.Qt.Checked:
        for index in range(root.childCount()):
            parent = root.child(index)
            for row in range(parent.childCount()):
                child = parent.child(row)
                x = d.values()
                y = d.keys()
                for _ in x:
                    if child.text(0) in _:
                        if parent.text(0) in y:
                            child.setCheckState(0, QtCore.Qt.Checked)
                            parent.setExpanded(True)

我确信代码很糟糕,但是通过只选择孩子,它会部分检查父母,这允许导出功能正常工作。

【问题讨论】:

    标签: python pyqt pyqt4 qtreewidget qtreewidgetitem


    【解决方案1】:

    我假设您的问题的核心是您想要遍历树小部件并构建包含检查项目的字典。

    下面的示例假设树有两层深,并且不尝试对检查状态进行任何管理:

    # not needed for python 3
    import sip
    sip.setapi('QString', 2)
    
    from collections import defaultdict
    from PyQt4 import QtGui, QtCore
    
    class Window(QtGui.QWidget):
        def __init__(self):
            QtGui.QWidget.__init__(self)
            self.tree = QtGui.QTreeWidget(self)
            self.tree.header().hide()
            for index in range(5):
                parent = QtGui.QTreeWidgetItem(self.tree, ['NUS2K%s' % index])
                if index % 3:
                    parent.setCheckState(0, QtCore.Qt.PartiallyChecked)
                else:
                    parent.setCheckState(0, QtCore.Qt.Unchecked)
                features = 'Loader Reports Logging'.split()
                for count, item in enumerate(features):
                    child = QtGui.QTreeWidgetItem(parent, [item])
                    if index % 3 and count % 3:
                        child.setCheckState(0, QtCore.Qt.Checked)
                    else:
                        child.setCheckState(0, QtCore.Qt.Unchecked)
                parent.setExpanded(True)
            self.button = QtGui.QPushButton('Export', self)
            self.button.clicked.connect(self.handleExport)
            layout = QtGui.QVBoxLayout(self)
            layout.addWidget(self.tree)
            layout.addWidget(self.button)
    
        def handleExport(self):
            mapping = self.exportTree()
            for machine, features in mapping.items():
                print('%s:' % machine)
                for feature in features:
                    print('  %s' % feature)
    
        def exportTree(self):
            mapping = defaultdict(list)
            root = self.tree.invisibleRootItem()
            for index in range(root.childCount()):
                parent = root.child(index)
                if parent.checkState(0) == QtCore.Qt.PartiallyChecked:
                    features = mapping[parent.text(0)]
                    for row in range(parent.childCount()):
                        child = parent.child(row)
                        if child.checkState(0) == QtCore.Qt.Checked:
                            features.append(child.text(0))
            return mapping
    
    if __name__ == '__main__':
    
        import sys
        app = QtGui.QApplication(sys.argv)
        window = Window()
        window.setGeometry(800, 300, 300, 300)
        window.show()
        sys.exit(app.exec_())
    

    【讨论】:

    • 这行得通,检查我添加了另一个elif 的问题是否检查了父级,并因此制定了 GUI 的另一个功能!
    【解决方案2】:

    我使用下面的代码没有遍历树

    def updateTreeItems(self):
        for x in range(0,self.ConfigTree.topLevelItemCount()):
            topLvlItem = self.ConfigTree.topLevelItem(x)
            for y in range(numColums):
                self.updateHighlight(topLvlItem,y)
            if self.ConfigTree.isItemExpanded(topLvlItem):
                for i in range(0,topLvlItem.childCount()):
                    childItem = topLvlItem.child(i)
                    for y in range(numColums):
                        self.updateHighlight(childItem,y)
    

    我使用 self.updateHighlight(childItem,y) 来突出显示树中的一个单元格,但您可以将一些数据添加到列表或其他内容中 - 如果它被选中。

    我确实在阅读您的问题时遇到了一些麻烦,但如果我还没有解决问题,请告诉我? - 我想我可以帮忙,因为我现在已经使用了一些树 :-)

    【讨论】:

    • 我……不确定?我很感激时间!虽然我有点看到你在做什么,但似乎 updateHighlight 是你自己创建的东西,我也需要为我的函数做同样的事情(if item.checkState() == 1 or 2 附加到列表/字典)将self.ConfigTree.isItemExpanded 修改为isChecked(True)checkState() == 1 or 2?在我一直致力于此的两个月中,我已经走了很长一段路(即使我的代码大量位于一个文件而不是模块中),但我似乎无法掌握 TreeWidget/TreeWidgetItemIterator 的这一部分。
    • 你是对的。函数 updateHighlight 只是我使用的一个函数。您应该简单地检查是否检查了每个孩子,如果是,请将相应的路径添加到您的列表中。您是否在为信息映射而苦苦挣扎?如果是这样,您可以有几列 - 并非全部可见 - 这些可以包含各种数据和小部件。 - 我建议您可以使用我的代码进行迭代,只打印路径左右,这样您就知道您可以穿过树,一旦运行,尝试提取信息。花时间重组代码总是一个好主意
    • 那时我肯定在为映射而苦苦挣扎。我需要的只是项目的文本,尽管将它映射到我需要的任何输出似乎是阻碍我的原因。我通过ekhumoro 找到了this example,但似乎找不到使用该信号推送文本的方法。我已经探索了其他插槽/信号,但是......当我开始这个时,我从未想过 UI 部分会比实际的后台功能更难!
    • 我可能误解了你的问题,但是你为什么不在我点击树的时候迭代整个树呢?将所有选中的项目添加到列表中。使用列表进行操作?数据集是否太大?还是我完全误解了你的问题? :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-03
    • 1970-01-01
    • 2021-12-15
    相关资源
    最近更新 更多