Использование QTreeWidgetItemIterator в PyQT4 для возврата isChecked из QTreeWidget в качестве словаря (или чего-то еще)

Проверьте редактирование для окончательного кода!

Итак... я признаю, что рисую здесь абсолютную пустоту из-за недостатка знаний, и просто представлю свой код и немного помолюсь.

Используя этот фантастический генератор xml в QTreeWidget, который ekhumoro закодировал, я я добавил флажки (tristate для родительских узлов), и теперь я пытаюсь перебрать эти флажки и вернуть словарь или список списков или... что-то с отношением родитель: дети (сервер: службы в этом дереве), поэтому что я могу построить пути к папкам из проверенных результатов дерева. Это произойдет при нажатии кнопки «Пуск» в качестве первой функции в серии.

Конечная цель состоит в том, чтобы иметь инструмент, который извлекает журналы с нескольких серверов и служб для каждого сервера на платформе на основе предоставленного пользователем времени запуска и остановки и доставляет эти журналы в окно администратора с идентичной структурой папок.

Я успешно просмотрел тестовый словарь, нашел и нашел файлы на основе 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) 

Создание виджета в моем графическом интерфейсе:

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

Виджет двухуровневый с родителями и детьми, вот и все:

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

И вот мы подошли к тому, где я застрял. Я могу привязать сигнал к нажатию кнопки, я могу делать все остальное, но получать проверенные элементы из QTreeWidget. Кажется, мне нужно было бы создать итератор и установить флаг Checked в его init, но все, что я пробовал, приводило к 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

а затем смог использовать этот пример для выбора конкретных дочерних элементов на основе другого поля со списком в другом месте графического интерфейса:

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)

Я уверен, что код неприятный, но, выбирая только дочерние элементы, он частично проверяет родителей, что позволяет функции экспорта работать правильно.


person Amdingo    schedule 16.12.2013    source источник


Ответы (2)


Я предполагаю, что суть вашего вопроса заключается в том, что вы хотите перебрать виджет дерева и создать словарь, содержащий отмеченные элементы.

В приведенном ниже примере предполагается, что дерево имеет два уровня в глубину и не пытается управлять состоянием проверки:

# 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_())
person ekhumoro    schedule 18.12.2013
comment
Это работает, проверьте вопрос. Я добавил еще один elif для проверки родителя, а также благодаря этому разработал еще одну функцию графического интерфейса! - person Amdingo; 20.12.2013

Я использую приведенный ниже код без перебора дерева

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), чтобы выделить ячейку в дереве с помощью, но вы можете добавить некоторые данные в список или что-то в этом роде - если он отмечен.

У меня были небольшие проблемы с чтением вашего вопроса, но, пожалуйста, дайте мне знать, если я ошибаюсь? - Думаю, я могу помочь, так как я уже использовал несколько деревьев :-)

person Svend Feldt    schedule 17.12.2013
comment
Я не уверен? Но я ценю время! Хотя я как бы понимаю, что вы делаете, похоже, что updateHighlight это то, что вы создали самостоятельно, и что мне нужно будет сделать то же самое для моей функции (if item.checkState() == 1 or 2 добавить в список/словарь), а также изменить self.ConfigTree.isItemExpanded на isChecked(True) или checkState() == 1 or 2? Я прошел долгий путь за два месяца, что я работал над этим (хотя мой код в основном находится в одном файле, а не в модулях), но я просто не могу понять эту часть TreeWidget/TreeWidgetItemIterator. - person Amdingo; 18.12.2013
comment
Ты прав. Функция updateHighlight — это просто функция, которую я использую. Вы должны просто проверить, проверен ли каждый дочерний элемент, и если это так, добавить соответствующий путь в свой список. Вы изо всех сил пытаетесь отображать информацию? Если это так, у вас может быть несколько столбцов — не все видимые — они могут содержать все виды данных и виджетов. - Я предлагаю вам использовать мой код только для повторения, просто распечатав путь или около того, просто чтобы вы знали, что можете пройтись по дереву, как только оно запустится, попробуйте извлечь информацию. Также всегда полезно потратить время на реструктуризацию кода. - person Svend Feldt; 18.12.2013
comment
Я определенно борюсь с отображением тогда. Все, что мне нужно, это текст элемента, хотя сопоставление его с любым выводом, который мне нужен, кажется, то, что меня удерживает. Я нашел этот пример по ekhumoro, но, похоже, не может найти способ отправить текст с помощью этого сигнала. Я исследовал другие слоты/сигналы, но... никогда не думал, что часть пользовательского интерфейса будет сложнее, чем фактические фоновые функции, когда я начал это! - person Amdingo; 18.12.2013
comment
Я могу неправильно понять вашу проблему, но почему бы вам просто не перебирать все дерево всякий раз, когда я щелкнул дерево? добавить все отмеченные элементы в список. использовать список для вашей операции? Набор данных слишком велик? или я совершенно неправильно понял ваш вопрос? :-) - person Svend Feldt; 19.12.2013