Отображение дерева в qml

в 3:17, , рубрики: QML, qt, qt quick, Qt Software, метки: , ,

image
Однажды один мой знакомый спросил: «Как можно отобразить древовидную структуру в qml?» Получившейся вариант я уже пытался опубликовать на хабре, но в тот раз проспал инвайт… Под катом вторая попытка опубликовать таки статью.
Цель: разработать тестовое приложение отображающее древовидную структуру с помою Qt Quick.
Результат можно посмотреть на GitHub: github.com/1KoT1/QMLPresentTree

Структура данных приложения

В нашем приложении элементы дерева описаны классом TreeItem.

image

Свойство content хранит некоторое содержимое элемента. Для тестового приложения вполне подойдёт строка.
Каждый экземпляр элемента имеет список содержащихся в нём подэлементов – childItems. Таким образом строится дерево.
Свойство isOpen хранит состояние списка внутренних элементов: развёрнут или скрыт.
Свойство hasChild показывает, имеются ли дочерние элементы. Можно было бы обойтись использованием !item.childItems().isEmpty(), но для простоты дальнейшего использования ввёл поле hasChild.
Код реализующий данный класс см. в.
В model.h и model.cpp описана модель данных приложения. Модель содержит единственное свойство – дерево элементов.
В создаю модель, представление и экспортирую модель в qml. Контроллер отсутствует, т. к. нет функционала, кроме отображения.

Отображение дерева в qml

Теперь переходим к самому интересному. Опишем отображение нашей древовидной структуры с помощью qml.
Главный файл qml содержит следующий код:

import QtQuick 2.0

Rectangle {
    width: 360
    height: 360
    ListView{
        anchors.fill: parent
        model: programmModel.tree
        delegate: ItemView{}
    }
}

Основная идея в следующем: Отображаем плоский список элементов первого уровня. Делегат, описывающий элемент содержит плоский список для отображения подэлементов. И т. д. по структуре дерева.

Окно целиком заполнено элементом ListView. ListView отображает плоский список и позволяет прокручивать его. В качестве модели указываю наше дерево. По сути это плоский список элементов, каждый из которых в свою очередь содержит список подэлементов. Отображение каждого элемента описано в ItemView.qml:

import QtQuick 2.0

Row{
    id: itemView
    Text{
        width: 10
        height: 10
        text: modelData.hasChild? modelData.isOpen ? "-" : "+" : ""
        MouseArea{
            anchors.fill: parent
            onClicked: modelData.isOpen = !modelData.isOpen;
        }
    }
    Column{
        Text{ text: modelData.content }
        Loader{
            source: modelData.isOpen ? "TreeItemsList.qml" : "Empty.qml"
        }
    }
}

Элемент состоит из вывода содержимого:

Text{ text: modelData.content }

Элемента управления, показывающего наличие подэлементов и позволяющего раскрыть список:

Text{
        width: 10
        height: 10
        text: modelData.hasChild? modelData.isOpen ? "-" : "+" : ""
        MouseArea{
            anchors.fill: parent
            onClicked: modelData.isOpen = !modelData.isOpen;
        }
    }

Списка поделементов:

Loader{
            source: modelData.isOpen ? "TreeItemsList.qml" : "Empty.qml"
        }

Список подэлементов надо отображать только, когда он открыт. Для этого использую элемент Loader. В закрытом состоянии в него подгружается Empty.qml – прямоуголник размером 0 на 0. Отображение отрытого списка подэлементов описано в TreeItemsList.qml.

Рассмотрим его:

import QtQuick 2.0

Column{
    Repeater{
        model: modelData.childItems
        delegate: ItemView{}
    }
}

Построение вертикального списка проиходит с помощью комбинации элементов Column и Repeater. В отличии от ListView Column не позволяет прокручивать содержимое и занимает всё небходимое пространство для отображение внутренних элементов. Прокрутка дерева целиком обеспечивается с помощью ListView в главном файле.

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

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

Автор: 1KoT1

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js