简体   繁体   English

如何使用基于字段值选择的异构委托制作 QtQuick TableView / TreeView

[英]how to make QtQuick TableView / TreeView with heterogeneous delegate chosen based on field value

How to make a TableView or TreeView with a cell delegate chosen according to the value of another cell?如何使用根据另一个单元格的值选择的单元格委托制作 TableView 或 TreeView?

The idea is to make a property editor similar to this:我们的想法是制作一个类似于此的属性编辑器:

截屏

I tried various of the approaches listed here: https://doc.qt.io/qt-5/qml-qt-labs-qmlmodels-tablemodel.html我尝试了此处列出的各种方法: https://doc.qt.io/qt-5/qml-qt-labs-qmlmodels-tablemodel.ZFC35FDC70D5FC69A73E7DB1063A822C

However DelegateChooser can only choose based on column or based on roleValue.但是DelegateChooser只能选择基于列或基于角色值。 None of those would work for the above usecase.这些都不适用于上述用例。

The model could be something like this: model 可能是这样的:

model: TableModel {
    TableModelColumn { display: "name" }
    TableModelColumn { display: "value" }
    rows: [
        {
            name: "Name",
            type: "string",
            value: "Alfred"
        },
        {
            name: "Amount",
            type: "float",
            value: 3.75
        },
        {
            name: "Enabled",
            type: "bool",
            value: true
        },
        {
            name: "Count",
            type: "int",
            value: 2
        },
        {
            name: "Color",
            type: "color",
            value: "#3300ff"
        }
    ]
}

to show a 2-column table view, where the delegate in the second column is chosen according to the value of type .显示一个 2 列表视图,其中第二列中的委托是根据type的值选择的。

Even selecting on the name role (which is a suboptimal solution, because there will be many properties of each type, and each DelegateChoice should match multiple names) does not work:即使选择名称角色(这是一个次优的解决方案,因为每种类型会有很多属性,并且每个DelegateChoice应该匹配多个名称)也不起作用:

delegate: DelegateChooser {
    role: "name"
    DelegateChoice {
        roleValue: "Enabled"
        delegate: CheckBox {
            checked: model.display
            onToggled: model.display = checked
        }
    }
    DelegateChoice {
        roleValue: "Count"
        delegate: SpinBox {
            value: model.display
            onValueModified: model.display = value
        }
    }
    DelegateChoice {
        delegate: TextField {
            text: model.display
            selectByMouse: true
            implicitWidth: 140
            onAccepted: model.display = text
        }
    }
}

As it is said in TableModel documentation :正如 TableModel 文档中所说:

As model manipulation in Qt is done via row and column indices, and because object keys are unordered, each column must be specified via TableModelColumn.由于 Qt 中的 model 操作是通过行和列索引完成的,并且由于 object 键是无序的,因此必须通过 TableModelColumn 指定每一列。 This allows mapping Qt's built-in roles to any property in each row object...这允许将 Qt 的内置角色映射到每一行 object 中的任何属性...

So, I've got working solution using built-in roles:所以,我有使用内置角色的工作解决方案:

import QtQuick 2.14
import QtQuick.Window 2.14
import QtQuick.Controls 2.14
import Qt.labs.qmlmodels 1.0

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Properties table")

    TableView {
        anchors.fill: parent

        model: TableModel {
            TableModelColumn {
                display: "name"
                decoration: function() { return "";}
            }
            TableModelColumn {
                display: "value"
                decoration: "type"
            }
            rows: [
                {
                    name: "Name",
                    type: "string",
                    value: "Alfred"
                },
                {
                    name: "Enabled",
                    type: "bool",
                    value: true
                },
                {
                    name: "Count",
                    type: "int",
                    value: 2
                }
            ]
        }

        delegate: DelegateChooser {
            role: "decoration"
            DelegateChoice {
                roleValue: "string"
                delegate: TextField {
                    text: model.display
                    selectByMouse: true
                }
            }
            DelegateChoice {
                roleValue: "int"
                delegate: SpinBox {
                    value: model.display
                }
            }
            DelegateChoice {
                roleValue: "bool"
                delegate: CheckBox {
                    checked: model.display
                }
            }
            DelegateChoice {
                delegate: Rectangle {
                    color: "beige"
                    implicitWidth: textLabel.width + 10
                    implicitHeight: textLabel.height
                    Text {
                        id: textLabel
                        anchors.centerIn: parent
                        text: model.display
                    }
                }
            }
        }
    }
}

However, I think a better solution would be define a custom PropertiesTableModel inherited from QAbstractTableModel:但是,我认为更好的解决方案是定义从 QAbstractTableModel 继承的自定义 PropertiesTableModel:

properties_table_model.hpp: properties_table_model.hpp:

#pragma once

#include <QAbstractTableModel>

class PropertiesTableModel : public QAbstractTableModel
{    
    Q_OBJECT

public:
    enum PropertyType {
        String,
        Integer,
        Boolean
    };
    Q_ENUM(PropertyType)

    struct Property {
        QString name;
        QVariant value;
        PropertyType type;
    };

    enum CustomRoles {
        NameRole = Qt::UserRole + 1,
        ValueRole,
        TypeRole
    };

    PropertiesTableModel(QObject *parent = nullptr) {
        m_properties.append({"String prop", "StringProperty", PropertyType::String});
        m_properties.append({"Int prop", 55, PropertyType::Integer});
        m_properties.append({"Bool prop", true, PropertyType::Boolean});
    }

    int rowCount(const QModelIndex & = QModelIndex()) const override
    {
        return m_properties.size();
    }

    int columnCount(const QModelIndex & = QModelIndex()) const override
    {
        return 2;
    }

    QVariant data(const QModelIndex &index, int role) const override
    {
        auto& property = m_properties.at(index.row());
        switch (role) {
            case CustomRoles::NameRole:
                return property.name;
            case CustomRoles::TypeRole:
                if (index.column() > 0)
                    return property.type;
                else
                    return -1;
            case CustomRoles::ValueRole:
                return property.value;
            default:
                break;
        }

        return QVariant();
    }

    QHash<int, QByteArray> roleNames() const override
    {
        QHash<int, QByteArray> roles;
        roles[NameRole] = "name";
        roles[ValueRole] = "value";
        roles[TypeRole] = "type";
        return roles;
    }
private:
    QVector<Property> m_properties;
};

, and use it like this: ,并像这样使用它:

import QtQuick 2.14
import QtQuick.Window 2.14
import QtQuick.Controls 2.14
import Qt.labs.qmlmodels 1.0

import MyLib 1.0

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Properties table")

    TableView {
        anchors.fill: parent

        model: PropertiesModel {}

        delegate: DelegateChooser {
            role: "type"
            DelegateChoice {
                roleValue: PropertiesModel.String
                delegate: TextField {
                    text: model.value
                    selectByMouse: true
                }
            }
            DelegateChoice {
                roleValue: PropertiesModel.Integer
                delegate: SpinBox {
                    value: model.value
                }
            }
            DelegateChoice {
                roleValue: PropertiesModel.Boolean
                delegate: CheckBox {
                    checked: model.value
                }
            }
            DelegateChoice {
                delegate: Rectangle {
                    color: "beige"
                    implicitWidth: textLabel.width + 10
                    implicitHeight: textLabel.height
                    Text {
                        id: textLabel
                        anchors.centerIn: parent
                        text: model.name
                    }
                }
            }
        }
    }
}

PS. PS。 remember to register it with:记得注册:

qmlRegisterType<PropertiesTableModel>("MyLib", 1, 0, "PropertiesModel");

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM