如何使用基于字段值选择的异构委托制作 QtQuick TableView / TreeView
how to make QtQuick TableView / TreeView with heterogeneous delegate chosen based on field value
如何制作一个 TableView 或 TreeView,并根据另一个单元格的值选择单元格委托?
想法是制作一个类似于这样的 属性 编辑器:
我尝试了此处列出的各种方法:https://doc.qt.io/qt-5/qml-qt-labs-qmlmodels-tablemodel.html
但是DelegateChooser
只能选择基于列或者基于角色值。 None 个适用于上述用例。
模型可能是这样的:
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"
}
]
}
显示 2 列 table 视图,其中第二列中的委托是根据 type.
的值选择的
甚至选择 name 角色(这是一个次优的解决方案,因为每种类型会有很多属性,每个 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
}
}
}
正如TableModel中所说documentation:
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. This allows mapping Qt's built-in roles to any property in each row object...
所以,我有使用内置角色的工作解决方案:
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
}
}
}
}
}
}
但是,我认为更好的解决方案是定义一个继承自 QAbstractTableModel 的自定义 PropertiesTableModel:
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;
};
,并像这样使用它:
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。记得注册:
qmlRegisterType<PropertiesTableModel>("MyLib", 1, 0, "PropertiesModel");
如何制作一个 TableView 或 TreeView,并根据另一个单元格的值选择单元格委托?
想法是制作一个类似于这样的 属性 编辑器:
我尝试了此处列出的各种方法:https://doc.qt.io/qt-5/qml-qt-labs-qmlmodels-tablemodel.html
但是DelegateChooser
只能选择基于列或者基于角色值。 None 个适用于上述用例。
模型可能是这样的:
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"
}
]
}
显示 2 列 table 视图,其中第二列中的委托是根据 type.
的值选择的甚至选择 name 角色(这是一个次优的解决方案,因为每种类型会有很多属性,每个 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
}
}
}
正如TableModel中所说documentation:
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. This allows mapping Qt's built-in roles to any property in each row object...
所以,我有使用内置角色的工作解决方案:
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
}
}
}
}
}
}
但是,我认为更好的解决方案是定义一个继承自 QAbstractTableModel 的自定义 PropertiesTableModel:
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;
};
,并像这样使用它:
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。记得注册:
qmlRegisterType<PropertiesTableModel>("MyLib", 1, 0, "PropertiesModel");