QML中的双向绑定C ++模型 [英] Two way binding C++ model in QML

查看:119
本文介绍了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);
    }
}

main.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
        }
    }
}

main.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中是一件复杂的事情,因为它通常可以作为某些赋值使用。

Twoway binding is a complicated matter in QML, as it usually works as some assignment.

因此,如果您绑定属性使用属性名称:valuetobeboundto ,然后再为属性名称分配某些内容,此绑定将丢失。

So, if you bind a property with propertyname: valuetobeboundto and later assign something to propertyname again, this binding will be lost.

解决方法有两种:使用 Binding -对象或不使用绑定,而是手动处理所有属性更改信号(理想情况下,模型会正确发出)。

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 Bindings 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 -Objects进行,因此, Binding 不会丢失。但是,您可以看到绑定的工作原理:每当 Button 的状态更改时, Binding 就会变为更新。
因此,按 AND 键发布 b2 会触发信号,由第一个 Binding处理,发布 b3 的新闻 AND 也是如此。

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.

现在进入双向绑定

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 的需要,并且在另一个绑定起作用了。这将导致绑定循环。幸运的是,在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 的内部绑定-属性,方法是使用属性名称:valueToBeBoundTo -语法。这意味着,内部绑定可能会被 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.


这只是一个概述。有更多细节可能会改变Binding的行为。但是我想我已经展示了如何创建双向绑定并提到了很多陷阱,您可能会遇到。

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.

这篇关于QML中的双向绑定C ++模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆