QML TreeView 的可重用编辑器委托
Reuseable EditorDelegate for QML TreeView
我正在尝试弄清楚如何为 QML TreeView
组件创建可重用的 EditorDelegate
。
我能够为角色 end
的一个专栏创建一个可行的编辑委托。但是我的TreeView
有3列,分别是name
、start
和end
。
我试图简单地设置 styleData.value=textEdit.text
而不是 modelEnd=textEdit.text
,但看起来 styleData.value
是只读的 属性。
如何使我的 EditorDelegate
可重复用于所有列?
EditorDelegate.qml
导入 QtQuick 2.0
Rectangle {
anchors.fill: parent
Text {
anchors.fill: parent
id: textDisplay
visible: true
text: styleData.value
}
TextInput {
anchors.fill: parent
id: textEdit
text: styleData.value
visible: false
onVisibleChanged: {
focus: parent
}
onAccepted: {
model.end=textEdit.text; // Can be model.name, model.start, model.<role>???
textEdit.visible=false
textDisplay.visible=true
}
onFocusChanged: {
if (!focus) {
textEdit.visible=false
styleData.value=textEdit.text
textDisplay.visible=true
}
}
}
MouseArea {
id: mouseArea
acceptedButtons: Qt.AllButtons
anchors.fill: parent
onDoubleClicked: {
if (mouse.button & Qt.LeftButton) {
textDisplay.visible=false
textEdit.visible=true
textEdit.forceActiveFocus()
}
}
}
}
用法应该是这样的:
import QtQuick 2.9
import QtQuick.Window 2.3
import QtQuick.Controls 1.4
Window {
visible: true
TreeView {
id: treeView
anchors.fill: parent
TableViewColumn {
title: "Value"
role: "name"
delegate: EditorDelegate { }
}
TableViewColumn {
title: "Start"
id: start
role: "start"
delegate: EditorDelegate { }
}
TableViewColumn {
title: "End"
id: end
role: "end"
delegate: EditorDelegate { }
}
model: itemModel
}
}
这里我还有一个问题,因为EditorDelegate
无法打开和折叠树节点。但这是一个完全不同的故事。
这个想法是一个组件清楚地建立了入口和出口,在你的情况下,组件与视图非常相关,所以它很难重用。更好的设计是只暴露一个属性,它用模型的数据更新,并在它发生变化时通知它。
在委托中,我更喜欢在必要时使用 Loader 来显示编辑器。
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QStandardItemModel>
#include <QDebug>
enum CustomRoles {
NameRole = Qt::UserRole + 1000,
StartRole,
EndRole
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QStandardItemModel model;
QObject::connect(&model, &QStandardItemModel::itemChanged, [](QStandardItem *item){
qDebug()<< item->data(StartRole);
});
QHash<int, QByteArray> roles;
roles[NameRole] = "name";
roles[StartRole] = "start";
roles[EndRole] = "end";
model.setItemRoleNames(roles);
for(int i=0; i<4; ++i){
QStandardItem *parent_item = new QStandardItem();
model.invisibleRootItem()->appendRow(parent_item);
parent_item->setData(QString("name-%1").arg(i), NameRole);
parent_item->setData(QString("start-%1").arg(i), StartRole);
parent_item->setData(QString("end-%1").arg(i), EndRole);
for (int j=0; j<5; ++j) {
QStandardItem *child_item = new QStandardItem();
parent_item->appendRow(child_item);
child_item->setData(QString("name-%1-%2").arg(i).arg(j), NameRole);
child_item->setData(QString("start-%1-%2").arg(i).arg(j), StartRole);
child_item->setData(QString("end-%1-%2").arg(i).arg(j), EndRole);
}
}
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("itemModel", &model);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
TreeView {
id: treeView
anchors.fill: parent
TableViewColumn {
title: "Value"
role: "name"
delegate: EditorDelegate{
text: styleData.value
onTextChanged: model.name = text
}
}
TableViewColumn {
title: "Start"
id: start
role: "start"
delegate: EditorDelegate{
text: styleData.value
onTextChanged: model.start = text
}
}
TableViewColumn {
title: "End"
id: end
role: "end"
delegate: EditorDelegate{
text: styleData.value
onTextChanged: model.end = text
}
}
model: itemModel
}
}
EditorDelegate.qml
import QtQuick 2.0
Rectangle {
id: root
property string text
property bool mode: false
Component{
id: component_display
Text{}
}
Component{
id: component_edit
TextInput{}
}
Loader{
id: loader
anchors.fill: parent
sourceComponent: mode ? component_edit: component_display
onSourceComponentChanged: {
loader.item.text = root.text
if(sourceComponent === component_edit){
loader.item.editingFinished.connect(onEditingFinished)
loader.item.forceActiveFocus()
}
}
function onEditingFinished(){
text = loader.item.text
mode = false
}
MouseArea{
anchors.fill: parent
onDoubleClicked: {
if (mouse.button & Qt.LeftButton)
mode = true
}
}
}
}
我正在尝试弄清楚如何为 QML TreeView
组件创建可重用的 EditorDelegate
。
我能够为角色 end
的一个专栏创建一个可行的编辑委托。但是我的TreeView
有3列,分别是name
、start
和end
。
我试图简单地设置 styleData.value=textEdit.text
而不是 modelEnd=textEdit.text
,但看起来 styleData.value
是只读的 属性。
如何使我的 EditorDelegate
可重复用于所有列?
EditorDelegate.qml 导入 QtQuick 2.0
Rectangle {
anchors.fill: parent
Text {
anchors.fill: parent
id: textDisplay
visible: true
text: styleData.value
}
TextInput {
anchors.fill: parent
id: textEdit
text: styleData.value
visible: false
onVisibleChanged: {
focus: parent
}
onAccepted: {
model.end=textEdit.text; // Can be model.name, model.start, model.<role>???
textEdit.visible=false
textDisplay.visible=true
}
onFocusChanged: {
if (!focus) {
textEdit.visible=false
styleData.value=textEdit.text
textDisplay.visible=true
}
}
}
MouseArea {
id: mouseArea
acceptedButtons: Qt.AllButtons
anchors.fill: parent
onDoubleClicked: {
if (mouse.button & Qt.LeftButton) {
textDisplay.visible=false
textEdit.visible=true
textEdit.forceActiveFocus()
}
}
}
}
用法应该是这样的:
import QtQuick 2.9
import QtQuick.Window 2.3
import QtQuick.Controls 1.4
Window {
visible: true
TreeView {
id: treeView
anchors.fill: parent
TableViewColumn {
title: "Value"
role: "name"
delegate: EditorDelegate { }
}
TableViewColumn {
title: "Start"
id: start
role: "start"
delegate: EditorDelegate { }
}
TableViewColumn {
title: "End"
id: end
role: "end"
delegate: EditorDelegate { }
}
model: itemModel
}
}
这里我还有一个问题,因为EditorDelegate
无法打开和折叠树节点。但这是一个完全不同的故事。
这个想法是一个组件清楚地建立了入口和出口,在你的情况下,组件与视图非常相关,所以它很难重用。更好的设计是只暴露一个属性,它用模型的数据更新,并在它发生变化时通知它。
在委托中,我更喜欢在必要时使用 Loader 来显示编辑器。
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QStandardItemModel>
#include <QDebug>
enum CustomRoles {
NameRole = Qt::UserRole + 1000,
StartRole,
EndRole
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QStandardItemModel model;
QObject::connect(&model, &QStandardItemModel::itemChanged, [](QStandardItem *item){
qDebug()<< item->data(StartRole);
});
QHash<int, QByteArray> roles;
roles[NameRole] = "name";
roles[StartRole] = "start";
roles[EndRole] = "end";
model.setItemRoleNames(roles);
for(int i=0; i<4; ++i){
QStandardItem *parent_item = new QStandardItem();
model.invisibleRootItem()->appendRow(parent_item);
parent_item->setData(QString("name-%1").arg(i), NameRole);
parent_item->setData(QString("start-%1").arg(i), StartRole);
parent_item->setData(QString("end-%1").arg(i), EndRole);
for (int j=0; j<5; ++j) {
QStandardItem *child_item = new QStandardItem();
parent_item->appendRow(child_item);
child_item->setData(QString("name-%1-%2").arg(i).arg(j), NameRole);
child_item->setData(QString("start-%1-%2").arg(i).arg(j), StartRole);
child_item->setData(QString("end-%1-%2").arg(i).arg(j), EndRole);
}
}
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("itemModel", &model);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
TreeView {
id: treeView
anchors.fill: parent
TableViewColumn {
title: "Value"
role: "name"
delegate: EditorDelegate{
text: styleData.value
onTextChanged: model.name = text
}
}
TableViewColumn {
title: "Start"
id: start
role: "start"
delegate: EditorDelegate{
text: styleData.value
onTextChanged: model.start = text
}
}
TableViewColumn {
title: "End"
id: end
role: "end"
delegate: EditorDelegate{
text: styleData.value
onTextChanged: model.end = text
}
}
model: itemModel
}
}
EditorDelegate.qml
import QtQuick 2.0
Rectangle {
id: root
property string text
property bool mode: false
Component{
id: component_display
Text{}
}
Component{
id: component_edit
TextInput{}
}
Loader{
id: loader
anchors.fill: parent
sourceComponent: mode ? component_edit: component_display
onSourceComponentChanged: {
loader.item.text = root.text
if(sourceComponent === component_edit){
loader.item.editingFinished.connect(onEditingFinished)
loader.item.forceActiveFocus()
}
}
function onEditingFinished(){
text = loader.item.text
mode = false
}
MouseArea{
anchors.fill: parent
onDoubleClicked: {
if (mouse.button & Qt.LeftButton)
mode = true
}
}
}
}