web-dev-qa-db-ja.com

QMLでの双方向バインディングC ++モデル

QtQuickとQMLについてもっと知りたいと思っています。私の現在の目標は、C++モデルからビューにデータをバインドする方法を理解することです。これまでのところ、QMLでモデルを設定し、モデルからデータを取得することはできましたが、データを更新する方法がわかりません。

C++モデルの双方向バインディングを設定するにはどうすればよいですか?以下は私がこれまでに書いたコードです。

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.私はこれで巨大な初心者なので、コードにある他の問題(フォーマット、標準など)を自由に指摘してください。どういうわけか学ぶ必要があります笑

編集1

@derMの回答を読んだ後、コードを変更して目的を達成しました

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
}
12
amura.cxg

双方向バインディングは、通常、割り当てとして機能するため、QMLでは複雑な問題です。

したがって、プロパティをpropertyname: valuetobeboundtoでバインドし、後でpropertynameに何かを再度割り当てると、このバインドは失われます。

回避策として、2つの方法があります。 Binding -オブジェクトを使用するか、バインディングを使用せずに、すべてのproperty-change-signals(モデルが理想的に適切に出力する)を手動で処理します。

最初に、詳細な手順を見つけることができます ここ。 ここでは、各方向に1つのBinding-オブジェクトを使用します。良いことは、新しいBindingを割り当てることによって、それらのBindingsがオーバーライドされないことです。

考えてみましょう:

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を1回押すとすぐに、r1はクリックしても更新されません。 b2でもう。 r0の場合、更新は2つのBinding-オブジェクトによって行われるため、Bindingが失われることはありません。ただし、バインディングがどのように機能するかを確認できます。Buttonの状態が変更されるたびに、Bindingが更新されます。したがって、プレス[〜#〜]および[〜#〜]b2のリリースは信号を発し、それはによって処理されます最初のBindingと同じことがプレス[〜#〜]と[〜#〜]b3のリリースにも当てはまります。

双方向バインディングになりました。ここでは、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を更新する必要があるかどうかがチェックされますが、値はすでに正しいため、再帰は終了し、バインディングループは発生しません。

2番目のバインディングをに変更する

    Binding {
        target: count1
        property: 'count'
        value: count0.count + 1
    }

状況が大幅に変わります。count0.countを変更するたびに、count1.countを上げる必要があります。次に、最初のBindingcount0.countcount1.countと同じ値に設定しようとしますが、両方のBindingが満たされる方法はなく、他のBindingが行った後、変更を加える必要はありません。作業。その結果、バインディングループが発生します。幸い、これらはQMLで非常にうまく検出されるため、ロックが回避されます。

ここで、最後に注意すべきことが1つだけあります。このコンポーネントを検討してください-定義:

// 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'
        }
    }
}

ここでは、propertyname: valueToBeBoundTo-構文を使用して、color- propertyの内部バインディングがあります。つまり、内部バインディングは、color- propertyの外部割り当てによって上書きされる可能性があります。このバインディングをBinding- Objectに置き換えれば、問題ないはずです。

同じことが逆になります。colorは外部で何らかの値にバインドされ、次にシグナルを内部で処理して値を割り当てると、Binding- Objectによって作成されない場合、外部バインディングは失われます。

これは一般的な概要にすぎません。バインディングの動作を変更する可能性のある詳細があります。しかし、Two-Way-Bindingを作成する方法を示し、かなりの落とし穴について言及したと思います。

21
derM