QML 中的两种方式绑定 C++ 模型
我正在尝试了解有关 QtQuick 和 QML 的更多信息.我目前的目标是了解如何将数据从 C++ 模型绑定到我的视图.到目前为止,我已经能够在我的 QML 中设置模型并从模型中获取数据,但我不知道如何更新我的数据.
I'm trying to learn more about QtQuick and QML. My current goal is to understand how to bind data from a C++ model to my view. So far I've been able to setup the model in my QML and get data from the model but I can't figure out how to update my data.
如何为我的 C++ 模型设置双向绑定?下面是我目前写的代码.
How do I setup two way binding for my C++ model? Below is the code I've written so far.
message.h
class Message : public QObject
{
Q_OBJECT
Q_PROPERTY(QString author READ getAuthor WRITE setAuthor NOTIFY authorChanged)
Q_PROPERTY(QString message READ getMessage WRITE setMessage NOTIFY messageChanged)
Q_SIGNALS:
void authorChanged(QString author);
void messageChanged(QString message);
public:
Message(QObject *parent = 0);
QString getAuthor();
void setAuthor(QString author);
QString getMessage();
void setMessage(QString message);
private:
QString _author;
QString _message;
};
message.cpp
#include "message.h"
Message::Message(QObject *parent) : QObject(parent)
{
}
QString Message::getAuthor()
{
return _author;
}
void Message::setAuthor(QString author)
{
if(author != _author)
{
_author = author;
emit authorChanged(author);
}
}
QString Message::getMessage()
{
return _message;
}
void Message::setMessage(QString message)
{
if(message != _message)
{
_message = message;
emit messageChanged(message);
}
}
ma??in.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
import com.butts.messaging 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: "Test"
Message {
id: testMessage
author: "Batman"
message: "Hello World!"
}
Flow {
TextField {
text: testMessage.message
}
Label {
text: testMessage.message
}
}
}
ma??in.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "message.h"
int main(int argc, char *argv[])
{
qmlRegisterType<Message>("com.butts.messaging", 1, 0, "Message");
//Message msg = Message();
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
return app.exec();
}
附:我在这方面是个大菜鸟,所以请随时指出我的代码中存在的任何其他问题(格式、标准等),我需要以某种方式学习大声笑
P.S. I'm a giant noob at this so feel free to point out any other issues (formatting, standards, etc.) I have in my code, I need to learn somehow lol
编辑 1
阅读@derM 的回答后,我更改了代码以实现我想要的效果
After reading @derM's answer I changed my code to achieve what I wanted
TextField {
id: editor
//Binding model -> view
text: testMessage.message
//Binding model <- view
Binding {
target: testMessage
property: "message"
value: editor.text
}
}
Label {
id: display
//Binding model -> view
text: testMessage.message
}
推荐答案
双向绑定在 QML 中是一件复杂的事情,因为它通常作为一些 assignment 工作.
Twoway binding is a complicated matter in QML, as it usually works as some assignment.
所以,如果你用 propertyname: valuetobeboundto
绑定一个属性,然后再给 propertyname
赋值,这个绑定就会丢失.
So, if you bind a property with propertyname: valuetobeboundto
and later assign something to propertyname
again, this binding will be lost.
作为一种解决方法,有两种方法: 使用 绑定
-对象或不使用绑定,而是手动处理所有属性更改信号(理想情况下,您的模型会正确发出).
As a workaround there are two ways: The use of Binding
-Objects or to not use binding, but handle all the property-change-signals (which your model ideally properly emits) manually.
首先,您可以在这里找到详细说明.在这里,他们为每个方向使用一个 Binding
-Object.好消息是,那些 Binding
不会被新的 Binding
赋值覆盖.
For the first, you can find a detailed instruction here.
Here they use the one Binding
-Object for each direction. The good thing is, those Binding
s will not be overridden, by assignment of a new Binding
.
考虑:
Row {
spacing: 2
Rectangle {
id: r0
width: 50
height: 30
}
Rectangle {
id: r1
width: 50
height: 30
color: b2.pressed ? 'red' : 'blue'
}
Button {
id: b2
}
Button {
id: b3
onPressed: r1.color = 'black'
onReleased: r1.color = 'green'
}
Binding {
target: r0
property: 'color'
value: b2.pressed ? 'red' : 'blue'
}
Binding {
target: r0
property: 'color'
value: (b3.pressed ? 'black' : 'green')
}
}
一开始r1
的值是绑定到b2
的状态的,但是一旦b3
被按下一次,r1
将不再通过单击 b2
来更新.对于 r0
,更新将由两个 Binding
-Object 完成,因此 Binding
不会丢失.但是,您可以看到绑定是如何工作的:当 Button
的状态发生变化时,Binding
将被更新.所以按下 AND 释放 b2
将触发信号,这将由第一个 Binding
处理,按下 也是如此>AND b3
的关系.
At the beginning the value of r1
is bound to the state of b2
, but as soon as b3
has been pressed once, r1
won't be updated by a click on b2
anymore. For r0
the updating will be done by the two Binding
-Objects, and therefore the Binding
won't be lost. However, you can see, how the binding works: When ever the state of the Button
changes, the Binding
will be updated.
So the press AND the release of b2
will fire signals, that will be handled by the first Binding
and the same goes for the press AND relase of b3
.
现在介绍双向绑定.在这里避免 Binding-Loops 很重要.
Now coming to the two-way binding. Here it is important to avoid Binding-Loops.
Row {
Button {
id: count0
property int count: 0
onClicked: count += 1
text: count
}
Button {
id: count1
property int count: 0
onClicked: count += 1
text: count
}
Binding {
target: count0
property: 'count'
value: count1.count
}
Binding {
target: count1
property: 'count'
value: count0.count
}
}
虽然这个例子很好.count0.count
的变化会触发count1.count
的变化.现在检查,如果 count0.count
需要更新,但值已经是正确的,所以递归结束,没有绑定循环发生.
While this example is perfectly fine. The changing of count0.count
will trigger a change of count1.count
. Now it is checked, if count0.count
would need an update, but the value is already the right, so the recursion ends, and no binding-loop occures.
将第二个绑定更改为
Binding {
target: count1
property: 'count'
value: count0.count + 1
}
极大地改变了情况:现在随着 count0.count
的每次更改,count1.count
都需要提高.然后,第一个 Binding
尝试将 count0.count
设置为与 count1.count
相同的值,但是两者都没有办法 Binding
就满足了,不需要做任何改动,其他的Binding
完成后就可以了.它将导致绑定循环.幸运的是,这些在 QML 中被很好地检测到,因此避免了锁定.
drastically changes the situation: Now with each change of count0.count
, count1.count
needs to be raised. The first Binding
then tries to set count0.count
to the same value as count1.count
but there is just no way that both Binding
will be satisfied, and no change is needed to be done, after the other Binding
did it's work. It will result in a binding-loop. Luckily those are detected pretty fine in QML, so a lock is avoided.
现在只有最后一件事需要处理:考虑这个组件定义:
Now there is only one last thing to take care of: Consider this Component-Definition:
// TestObj.qml
Item {
width: 150
height: 40
property alias color: rect.color
Row {
spacing: 10
Rectangle {
id: rect
width: 40
height: 40
radius: 20
color: butt.pressed ? 'green' : 'red'
}
Button {
id: butt
text: 'toggle'
}
}
}
这里我们有一个 color
-property 的内部绑定,通过使用 propertyname: valueToBeBoundTo
-Syntax.这意味着,内部绑定可能会被 color
属性的任何外部分配覆盖.用 Binding
-Object 替换这个绑定,应该没问题.
Here we have an internal binding of the color
-property, by using the propertyname: valueToBeBoundTo
-Syntax. This means, the internal binding might be overwritten by any external assignemtn of the color
-property.
Replace this binding by a Binding
-Object, and you should be fine.
反之亦然:color
外部绑定到某个值,然后您在内部处理信号并为其分配值,如果没有,外部绑定将丢失由 Binding
-Object.
The same would go the other way around: color
is externally bound to some value, and then you handle a signal internally and assign a value to it, the external binding would be lost, if not created by a Binding
-Object.
这只是一般概述.还有更多细节可能会改变绑定的行为.但我想我已经展示了如何创建双向绑定,并提到了一些你可能会遇到的陷阱.
This is only a general overview. There are way more details that might alter the behavior of Binding. But I think I have shown, how you can create a Two-Way-Binding and mentioned quite some pitfalls, you might encounter.
相关文章