Это минимально жизнеспособная модель TreeView в QML? - PullRequest
1 голос
/ 22 мая 2019

Я делаю список из трех пунктов: «Привет», «Что?» И «Вверх?».Я хочу поместить это в дерево.Я знаю, что этот список будет только когда-либо содержать эти три элемента. Поэтому я хотел бы знать, как "вкладывать" эти элементы вместе.

Я знаю, что существуют реализации для гибких систем, которые поддерживаютдобавление и удаление родительских / дочерних объектов, поиск индексов ... мощные модели.Однако мне буквально нужно отображать эти элементы только в развертываемом / складываемом виде.Вот что я прочитал в отношении C ++ и QAbstractItemModels:

Вот самый простой жизнеспособный код для реализации дерева с моделью:

import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 1.4


Window {
    id: mywindow
    visible: true
    width: 640
    height: 480

    TreeView {
        id: treeview
        anchors.fill: parent

        TableViewColumn {
            title: "Phrase"
            role: "phrase"
        }
        model: phraseModel
    }

    ListModel {
        id: phraseModel
        ListElement { phrase: "Hey"; }
        ListElement { phrase: "What's"; }
        ListElement { phrase: "Up?"; }
    }
}

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

Hey
    What's
        Up?

Но я получаю все в одном столбце все выровнены друг с другом:

Hey
What's
Up?

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

Ответы [ 2 ]

3 голосов
/ 23 мая 2019

Нет собственной модели QML, которая могла бы использовать TreeView, поэтому я реализовал модель, которая пытается быть универсальной:

TreeElement

// treeelement.h
#ifndef TreeElement_H
#define TreeElement_H

#include <QObject>
#include <QQmlListProperty>

class TreeElement : public QObject
{
    Q_OBJECT
public:
    Q_PROPERTY(QQmlListProperty<TreeElement> items READ items)
    Q_CLASSINFO("DefaultProperty", "items")
    TreeElement(QObject *parent = Q_NULLPTR);

    Q_INVOKABLE TreeElement *parentItem() const;
    bool insertItem(TreeElement *item, int pos = -1);
    QQmlListProperty<TreeElement> items();

    TreeElement *child(int index) const;
    void clear();

    Q_INVOKABLE int pos() const;
    Q_INVOKABLE int count() const;

private:
    static void appendElement(QQmlListProperty<TreeElement> *property, TreeElement *value);
    static int countElement(QQmlListProperty<TreeElement> *property);
    static void clearElement(QQmlListProperty<TreeElement> *property);
    static TreeElement *atElement(QQmlListProperty<TreeElement> *property, int index);

    QList<TreeElement *> m_childs;
    TreeElement *m_parent;
};
#endif // TreeElement_H

// treeelement.cpp
#include "treeelement.h"

TreeElement::TreeElement(QObject *parent) :
    QObject(parent),
    m_parent(nullptr) {}

TreeElement *TreeElement::parentItem() const{
    return m_parent;
}


QQmlListProperty<TreeElement> TreeElement::items(){
    return  QQmlListProperty<TreeElement> (this,
                                           this,
                                           &TreeElement::appendElement,
                                           &TreeElement::countElement,
                                           &TreeElement::atElement,
                                           &TreeElement::clearElement);
}

TreeElement *TreeElement::child(int index) const{
    if(index < 0 || index >= m_childs.length())
        return nullptr;
    return m_childs.at(index);
}

void TreeElement::clear(){
    qDeleteAll(m_childs);
    m_childs.clear();
}

bool TreeElement::insertItem(TreeElement *item, int pos){
    if(pos > m_childs.count())
        return false;
    if(pos < 0)
        pos = m_childs.count();
    item->m_parent = this;
    item->setParent(this);
    m_childs.insert(pos, item);
    return true;
}

int TreeElement::pos() const{
    TreeElement *parent = parentItem();
    if(parent)
        return parent->m_childs.indexOf(const_cast<TreeElement *>(this));
    return 0;
}

int TreeElement::count() const{
    return m_childs.size();
}

void TreeElement::appendElement(QQmlListProperty<TreeElement> *property, TreeElement *value){
    TreeElement *parent = qobject_cast<TreeElement *>(property->object);
    parent->insertItem(value);
}

int TreeElement::countElement(QQmlListProperty<TreeElement> *property){
    TreeElement *parent = qobject_cast<TreeElement *>(property->object);
    return parent->count();
}

void TreeElement::clearElement(QQmlListProperty<TreeElement> *property){
    TreeElement *parent = qobject_cast<TreeElement *>(property->object);
    parent->clear();
}

TreeElement *TreeElement::atElement(QQmlListProperty<TreeElement> *property, int index){
    TreeElement *parent = qobject_cast<TreeElement *>(property->object);
    if(index < 0 || index >= parent->count())
        return nullptr;
    return parent->child(index);
}

TreeModel

// treemodel.h
#ifndef TreeModel_H
#define TreeModel_H

#include <QAbstractItemModel>
#include <QQmlListProperty>

class TreeElement;

class TreeModel : public QAbstractItemModel
{
    Q_OBJECT
public:
    Q_PROPERTY(QQmlListProperty<TreeElement> items READ items)
    Q_PROPERTY(QVariantList roles READ roles WRITE setRoles NOTIFY rolesChanged)
    Q_CLASSINFO("DefaultProperty", "items")

    TreeModel(QObject *parent = Q_NULLPTR);
    ~TreeModel() override;

    QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE;
    QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
    Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
    QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
    int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
    int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
    QQmlListProperty<TreeElement> items();

    QVariantList roles() const;
    void setRoles(const QVariantList &roles);

    Q_INVOKABLE QModelIndex indexFromElement(TreeElement *item);
    Q_INVOKABLE bool insertElement(TreeElement *item, const QModelIndex &parent = QModelIndex(), int pos = -1);

    TreeElement *elementFromIndex(const QModelIndex &index) const;

private:
    TreeElement *m_root;
    QHash<int, QByteArray> m_roles;

signals:
    void rolesChanged();
};

#endif // TreeModel_H

// treemodel.cpp
#include "treemodel.h"
#include "treeelement.h"

TreeModel::TreeModel(QObject *parent) :
    QAbstractItemModel(parent){
    m_root = new TreeElement;
}
TreeModel::~TreeModel(){
    delete m_root;
}

QHash<int, QByteArray> TreeModel::roleNames() const{
    return m_roles;
}

QVariant TreeModel::data(const QModelIndex &index, int role) const{
    if (!index.isValid())
        return QVariant();
    TreeElement *item = static_cast<TreeElement*>(index.internalPointer());
    QByteArray roleName = m_roles[role];
    QVariant name = item->property(roleName.data());
    return name;
}

Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const{
    if (!index.isValid())
        return nullptr;
    return QAbstractItemModel::flags(index);
}

QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const{
    if (!hasIndex(row, column, parent))
        return QModelIndex();
    TreeElement *parentItem = elementFromIndex(parent);
    TreeElement *childItem = parentItem->child(row);
    if (childItem)
        return createIndex(row, column, childItem);
    else
        return QModelIndex();
}

QModelIndex TreeModel::parent(const QModelIndex &index) const{
    if (!index.isValid())
        return QModelIndex();
    TreeElement *childItem = static_cast<TreeElement*>(index.internalPointer());
    TreeElement *parentItem = static_cast<TreeElement *>(childItem->parentItem());
    if (parentItem == m_root)
        return QModelIndex();
    return createIndex(parentItem->pos(), 0, parentItem);
}

int TreeModel::rowCount(const QModelIndex &parent) const{
    if (parent.column() > 0)
        return 0;
    TreeElement *parentItem = elementFromIndex(parent);
    return parentItem->count();
}

int TreeModel::columnCount(const QModelIndex &parent) const{
    Q_UNUSED(parent)
    return 1;
}

QQmlListProperty<TreeElement> TreeModel::items(){
    return m_root->items();
}

QVariantList TreeModel::roles() const{
    QVariantList list;
    QHashIterator<int, QByteArray> i(m_roles);
    while (i.hasNext()) {
        i.next();
        list.append(i.value());
    }

    return list;
}

void TreeModel::setRoles(const QVariantList &roles){
    static int nextRole = Qt::UserRole + 1;
    for(QVariant role : roles) {
        m_roles.insert(nextRole, role.toByteArray());
        nextRole ++;
    }
    emit rolesChanged();
}

QModelIndex TreeModel::indexFromElement(TreeElement *item){
    QVector<int> positions;
    QModelIndex result;
    if(item) {
        do{
            int pos = item->pos();
            positions.append(pos);
            item = item->parentItem();
        } while(item != nullptr);

        for (int i = positions.size() - 2; i >= 0 ; i--)
            result = index(positions[i], 0, result);
    }
    return result;
}


bool TreeModel::insertElement(TreeElement *item, const QModelIndex &parent, int pos){
    TreeElement *parentElement = elementFromIndex(parent);
    if(pos >= parentElement->count())
        return false;
    if(pos < 0)
        pos = parentElement->count();
    beginInsertRows(parent, pos, pos);
    bool retValue = parentElement->insertItem(item, pos);
    endInsertRows();
    return retValue;
}

TreeElement *TreeModel::elementFromIndex(const QModelIndex &index) const{
    if(index.isValid())
        return static_cast<TreeElement *>(index.internalPointer());
    return m_root;
}

main.cpp

#include "treemodel.h"
#include "treeelement.h"

#include <QGuiApplication>
#include <QQmlApplicationEngine>

static void registertypes(){
    qmlRegisterType<TreeElement>("foo", 1, 0, "TreeElement");
    qmlRegisterType<TreeModel>("foo", 1, 0, "TreeModel");
}

Q_COREAPP_STARTUP_FUNCTION(registertypes)

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);    
    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 1.4

import foo 1.0

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    TreeModel {
        id: treemodel
        roles: ["phrase"]
        TreeElement{
            property string phrase: "Hey"
            TreeElement{
                property string phrase: "What's"
                TreeElement{
                    property string phrase: "Up?"
                }
            }
        }
    }
    TreeView {
        anchors.fill: parent
        model: treemodel
        TableViewColumn {
            title: "Name"
            role: "phrase"
            width: 200
        }
    }
}

Выход:

enter image description here

Полный пример вы найдете здесь

1 голос
/ 23 мая 2019

Я создал разборный фрейм в QML (групповой блок с заголовком и содержимым).Если вы уверены, что никогда не измените структуру, вы можете использовать для своих целей:

Я упростил код, удалив ненужные части (анимации, декорации и т. Д.).Таким образом, приведенный ниже код может быть улучшен.Но я сохранил идею:

// CollapsibleGroupBox.qml
Item {
    property alias contentItem: content.contentItem;
    property string title: ""
    Item {
        id: titleBar
        anchors.top: parent.top
        anchors.left: parent.left
        anchors.right: parent.right
        height: 30
        Row {
            anchors.fill: parent
            CheckBox {
                Layout.alignment: Qt.AlignLeft
                id: expand
                checked: true;
            }
            Text {
                Layout.alignment: Qt.AlignLeft
                text: title
            }
        }
    }
    Pane {
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.top: titleBar.bottom
        anchors.bottom: parent.bottom
        topPadding: 0
        visible: expand.checked
        id: content
    }
}
// Main.qml
Item {
    height: 500
    width: 500
    CollapsibleGroupBox {
        anchors.fill: parent
        title: "Hey!"
        contentItem: CollapsibleGroupBox {
            title: "What's"
            contentItem: CollapsibleGroupBox {
                title: "up?"
            }
        }
    }
}

Вы получите:

enter image description here

Вы можете заменить флажок наMouseArea, а также

...