Qtreewidget в зеркальный словарь python

есть ли способ сделать QTreeWidget отразить изменения, внесенные во внутреннюю структуру данных, такую как словарь? Похоже, они создали эту функциональность в API, потому что есть много программ, которые могут взаимодействовать с QTreeWidgets из нескольких областей GUI, но основная цель, требуемая от QTreeWidget показать структуру данных в любой момент времени. Документация для QtGui items не так просто для меня понять, как это обычно относится к документации C, и Я не уверен, как он переходит на Python.

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

это легко выполнимо? Я не смог найти ничего, чтобы сделать простое зеркалирование данных structres такой еще.

4 ответов


Это простая реализация:

def fill_item(item, value):
  item.setExpanded(True)
  if type(value) is dict:
    for key, val in sorted(value.iteritems()):
      child = QTreeWidgetItem()
      child.setText(0, unicode(key))
      item.addChild(child)
      fill_item(child, val)
  elif type(value) is list:
    for val in value:
      child = QTreeWidgetItem()
      item.addChild(child)
      if type(val) is dict:      
        child.setText(0, '[dict]')
        fill_item(child, val)
      elif type(val) is list:
        child.setText(0, '[list]')
        fill_item(child, val)
      else:
        child.setText(0, unicode(val))              
      child.setExpanded(True)
  else:
    child = QTreeWidgetItem()
    child.setText(0, unicode(value))
    item.addChild(child)

def fill_widget(widget, value):
  widget.clear()
  fill_item(widget.invisibleRootItem(), value)

я добавил поддержку списка на всякий случай, если кому-то это нужно.

использование:

d = { 'key1': 'value1', 
  'key2': 'value2',
  'key3': [1,2,3, { 1: 3, 7 : 9}],
  'key4': object(),
  'key5': { 'another key1' : 'another value1',
            'another key2' : 'another value2'} }

widget = QTreeWidget()
fill_widget(widget, d)
widget.show()

результат:

screenshot


просто потому, что мне недавно понадобилась эта реализация для Python3 и PyQt5 вот немного более короткий (и полный) порт данного примера:

from PyQt5.QtWidgets import  QApplication, QTreeWidget, QTreeWidgetItem

class ViewTree(QTreeWidget):
    def __init__(self, value):
        super().__init__()
        def fill_item(item, value):
            def new_item(parent, text, val=None):
                child = QTreeWidgetItem([text])
                fill_item(child, val)
                parent.addChild(child)
                child.setExpanded(True)
            if value is None: return
            elif isinstance(value, dict):
                for key, val in sorted(value.items()):
                    new_item(item, str(key), val)
            elif isinstance(value, (list, tuple)):
                for val in value:
                    text = (str(val) if not isinstance(val, (dict, list, tuple))
                            else '[%s]' % type(val).__name__)
                    new_item(item, text, val) 
            else:
                new_item(item, str(value))

        fill_item(self.invisibleRootItem(), value)

if __name__ == '__main__':
    app = QApplication([])
    window = ViewTree({ 'key1': 'value1', 'key3': [1,2,3, { 1: 3, 7 : 9}]})
    window.show()
    app.exec_()

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

  def update_right_dock(self, file_list):   
            obj_list = []
            maps = []
            level = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]         #Stores Maximum and Current Levels For The TreeWidget
            level_name = ["", "", "", "", "", "", "", "", "", "", "", "", "", ""]   #Stores Previous File Path To Compare before adding file or folder to tree
            tree.clear()
            prev = ""
            tot_len = 2

            p = 0
            for file in file_list:
                if(os.path.isdir(file)):
                    is_file = 0
                else:
                    is_file = 1

                tmp_map = []

                file = file[1:]
                abs_path = file.split('/')
                abs_path_len = len(abs_path)
                filename = abs_path[-1]

                if(prev == file[:tot_len - 1]):
                    #print("LOOOOOOOOOOOOP ------ 1")
                    while (i < abs_path_len - 1):
                        level[level_counter + 1] = QTreeWidgetItem(level[level_counter], [abs_path[i]])

                        tmp_map.append(level[level_counter + 1])
                        tmp_map.append(level_counter + 1)
                        tmp_map.append(1)
                        obj_list.append(tmp_map)
                        tmp_map = []

                        level[level_counter + 1].setCheckState(0, Qt.Checked)
                        tree.expandItem(level[level_counter + 1])
                        level_counter = level_counter + 1
                        level_name[i] = abs_path[i]
                        i = i + 1
                    level[level_counter + 1] = QTreeWidgetItem(level[level_counter], [abs_path[i]])

                    tmp_map.append(level[level_counter + 1])
                    tmp_map.append(level_counter + 1)
                    tmp_map.append(0)
                    obj_list.append(tmp_map)
                    tmp_map = []

                    level[level_counter + 1].setCheckState(0, Qt.Checked)
                    tree.expandItem(level[level_counter + 1])

                    file_len = len(filename)
                    tot_len = len(file) - file_len
                    prev = file[:tot_len - 1]
                    continue


                len2 = len(level_name)
                k = 0
                while k < abs_path_len and k < len2:
                    if (level_name[k] == abs_path[k]):
                        k  = k + 1
                        continue
                    break
                level_counter = k + 1
                i = level_counter - 1
                while k < abs_path_len:
                    level_name[k] = abs_path[k]
                    k = k + 1

                if level_counter > 1:
                    #print("LOOOOOOOOOOOOP ------ 2")
                    if(i == abs_path_len - 1):
                        level_counter = level_counter - 1
                    while i < abs_path_len - 1:
                        level[level_counter] = QTreeWidgetItem(level[level_counter - 1], [abs_path[i]])

                        tmp_map.append(level[level_counter])
                        tmp_map.append(level_counter)
                        tmp_map.append(1)
                        obj_list.append(tmp_map)
                        tmp_map = []

                        level[level_counter].setCheckState(0, Qt.Checked)
                        tree.expandItem(level[level_counter])
                        level_counter = level_counter + 1
                        level_name[i] = abs_path[i]

                        i = i + 1
                        if i == abs_path_len - 1:
                                level_counter = level_counter - 1

                    level[level_counter + 1] = QTreeWidgetItem(level[level_counter], [abs_path[i]])

                    tmp_map.append(level[level_counter + 1])
                    tmp_map.append(level_counter + 1)
                    tmp_map.append(0)
                    obj_list.append(tmp_map)
                    tmp_map = []

                    level[level_counter + 1].setCheckState(0, Qt.Checked)
                    tree.expandItem(level[level_counter + 1])

                    file_len = len(filename)
                    tot_len = len(file) - file_len
                    prev = file[:tot_len - 1]
                    continue

                if(abs_path_len == 1):
                    level[level_counter + 1] = QTreeWidgetItem(tree, [abs_path[i]])

                    tmp_map.append(level[level_counter + 1])
                    tmp_map.append(level_counter + 1)
                    tmp_map.append(0)
                    obj_list.append(tmp_map)
                    tmp_map = []

                    level[level_counter + 1].setCheckState(0, Qt.Checked)
                    tree.expandItem(level[level_counter + 1])
                    continue

                i = 1    
                #print("LOOOOOOOOOOOOP ------ 3")
                level[level_counter] = QTreeWidgetItem(tree, [abs_path[0]])

                tmp_map.append(level[level_counter])
                tmp_map.append(level_counter)
                tmp_map.append(1)
                obj_list.append(tmp_map)
                tmp_map = []

                level[level_counter].setCheckState(0, Qt.Checked)
                tree.expandItem(level[level_counter])
                level_name[level_counter - 1] = abs_path[0]       

                while i < abs_path_len - 1:
                    level[level_counter + 1] = QTreeWidgetItem(level[level_counter], [abs_path[i]])
                    tmp_map.append(level[level_counter + 1])
                    tmp_map.append(level_counter + 1)
                    tmp_map.append(1)
                    obj_list.append(tmp_map)
                    tmp_map = []

                    level[level_counter + 1].setCheckState(0, Qt.Checked)
                    tree.expandItem(level[level_counter + 1])
                    level_counter = level_counter + 1
                    level_name[i] = abs_path[i]
                    if i == abs_path_len - 1:
                            level_counter = level_counter - 1
                    i = i + 1

                level[level_counter + 1] = QTreeWidgetItem(level[level_counter], [abs_path[i]])
                tmp_map.append(level[level_counter + 1])
                tmp_map.append(level_counter + 1)
                tmp_map.append(0)
                obj_list.append(tmp_map)
                tmp_map = []

                level[level_counter + 1].setCheckState(0, Qt.Checked)
                tree.expandItem(level[level_counter + 1])
                level_name[i] = abs_path[i]

                file_len = len(filename)
                tot_len = len(file) - file_len
                prev = file[:tot_len - 1]
                p = p + 1

несколько дней назад я искал такую тему, но все, что я смог найти - Как заполнить дерево, но не как извлечь его. Вот я и пишу. Может, кому-то это еще пригодится. Благодаря автору prev, я использовал его код с некоторыми улучшениями. Это загрузит вашу коллекцию (это может быть список или дикт или кортеж со многими детьми)

def load_tree(self, d):
    self.fill_widget(self.tree, d)

def fill_widget(self, widget, value):
    widget.clear()
    self.fill_item(widget.invisibleRootItem(), value)

def fill_item(self, item, value):
    def new_item(parent, text):
        child = QTreeWidgetItem([str(text)])
        if text not in ("[dict]", "[list]", "[tuple]"):
            child.setFlags(child.flags() | Qt.ItemIsEditable)
        parent.addChild(child)
        child.setExpanded(True)

        return child

    if isinstance(value, dict):
        new_parent = new_item(item, f"[{value.__class__.__name__}]")
        for elem_k, elem_v in value.items():
            sub_parent = new_item(new_parent, elem_k)
            self.fill_item(sub_parent, elem_v)
    elif isinstance(value, (tuple, list)):
        new_parent = new_item(item, f"[{value.__class__.__name__}]")
        for val in value:
            self.fill_item(new_parent, val)
    else:
        new_item(item, f"{value}")

и извлечь код немного больше.. сложный. Если кто-то может сделать его более причудливым-pls.

def get_dict(self):
    result = None

    def unpack(to_unpack, key, source=None):
        for child_index in range(to_unpack.childCount()):
            child = to_unpack.child(child_index)
            child_text = child.text(0)
            try:
                child_text = float(child_text)
            except ValueError:
                try:
                    child_text = int(child_text)
                except ValueError:
                    pass

            if source is None:
                core = result
            else:
                core = source

            if key == "[dict]":
                core.update({child_text: None})
                if child.childCount() > 0:
                    unpack(child, child_text, core)
            elif key == "[list]" or key == "[tuple]":
                if child_text == "[dict]":
                    core.append({})
                elif child_text == "[list]" or child_text == "[tuple]":
                    core.append([])
                else:
                    core.append(child_text)

                if child.childCount() > 0:
                    unpack(child, child_text, core[child_index])
            else:
                if child_text == "[dict]":
                    core.update({key: {}})
                elif child_text == "[list]" or child_text == "[tuple]":
                    core.update({key: []})
                else:
                    core.update({key: child_text})

                if child.childCount() > 0:
                    unpack(child, child_text, core[key])

    for index in range(self.tree.topLevelItemCount()):
        parent = self.tree.topLevelItem(index)
        element_text = parent.text(0)
        if element_text == "[dict]":
            result = {}
            unpack(parent, element_text)
        elif element_text == "[list]" or element_text == "[tuple]":
            result = []
            unpack(parent, element_text)
        else:
            result = element_text

    return result

где self.дерево - QTreeWidget объект в окне.