Zwei-Wege-bindendes C++-Modell in QML

Lesezeit: 5 Minuten

Benutzer-Avatar
amura.cxg

Ich versuche, mehr über QtQuick und QML zu erfahren. Mein aktuelles Ziel ist es zu verstehen, wie Daten aus einem C++-Modell an meine Ansicht gebunden werden. Bisher konnte ich das Modell in meinem QML einrichten und Daten aus dem Modell abrufen, aber ich kann nicht herausfinden, wie ich meine Daten aktualisieren kann.

Wie richte ich eine Zwei-Wege-Bindung für mein C++-Modell ein? Unten ist der Code, den ich bisher geschrieben habe.

Nachricht.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;
};

Nachricht.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();
}

PS Ich bin ein riesiger Noob in diesem Bereich, also fühlen Sie sich frei, auf andere Probleme (Formatierung, Standards usw.) hinzuweisen, die ich in meinem Code habe, ich muss irgendwie lernen, lol

Bearbeiten 1

Nachdem ich die Antwort von @derM gelesen hatte, änderte ich meinen Code, um das zu erreichen, was ich wollte

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
}

  • Zu diesem Thema gab es einen Vortrag auf QtWS 2019: youtu.be/G6W6MSzM7rs kdab.com/wp-content/uploads/stories/…

    – Zweiundvierzig

    25. Februar 2021 um 6:18 Uhr

  • Die Antwort von derM gibt einen hervorragenden Einblick in die Eigenschaftsbindung und ihre Fallstricke. Die Antwort von user240515 schlägt jedoch eine viel prägnantere Lösung vor.

    – m7913d

    1. Dezember 2021 um 18:34 Uhr


  • @amura.cxg Erwägen Sie, Ihre verwendete Lösung in eine separate Antwort zu verschieben, anstatt sie in Ihrer Frage zu posten (wie von der SO-Richtlinie gefordert), andernfalls scheint die Antwort von derM die einzig richtige zu sein.

    – m7913d

    1. Dezember 2021 um 18:36 Uhr

Die Zweiwegebindung ist in QML eine komplizierte Angelegenheit, da sie normalerweise als einige funktioniert Abtretung.

Also, wenn Sie eine Eigenschaft mit binden propertyname: valuetobeboundto und später etwas zuweisen propertyname Auch hier geht diese Bindung verloren.

Als Problemumgehung gibt es zwei Möglichkeiten: Die Verwendung von Binding-Objekte oder keine Bindung verwenden, sondern alle Eigenschaftsänderungssignale (die Ihr Modell idealerweise ordnungsgemäß aussendet) manuell handhaben.

Zum einen finden Sie eine ausführliche Anleitung hier.
Hier benutzen sie den einen Binding-Objekt für jede Richtung. Das Gute ist, die Bindings werden nicht durch Zuweisung eines neuen überschrieben Binding.

In Betracht ziehen:

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

Am Anfang der Wert von r1 ist an den Zustand gebunden b2aber sobald b3 wurde einmal gedrückt, r1 wird nicht durch einen Klick auf aktualisiert b2 mehr. Zum r0 die Aktualisierung wird von den beiden durchgeführt Binding-Objekte, und damit die Binding wird nicht verloren gehen. Sie können jedoch sehen, wie die Bindung funktioniert: Wann immer der Stand der Button Änderungen, die Binding wird Aktualisiert. Also die Presse UND die Freisetzung von b2 wird Signale abfeuern, die von der ersten behandelt werden Binding und das gleiche gilt für die Presse UND Freigabe von b3.

Kommen wir nun zum Zwei-Wege-Bindung. Hier ist es wichtig Binding-Loops zu vermeiden.

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

Während dieses Beispiel vollkommen in Ordnung ist. Das Wechseln von count0.count wird eine Änderung von auslösen count1.count. Nun wird geprüft, ob count0.count müsste aktualisiert werden, aber der Wert ist bereits richtig, sodass die Rekursion endet und keine Bindungsschleife auftritt.

Ändern der zweiten Bindung zu

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

drastisch ändert sich die Situation: Jetzt mit jeder Änderung von count0.count, count1.count muss angehoben werden. Der Erste Binding versucht dann einzustellen count0.count auf den gleichen Wert wie count1.count aber es gibt einfach keine Möglichkeit, dass beides Binding zufrieden sein, und es ist keine Änderung erforderlich, nach der anderen Binding hat es funktioniert. Dies führt zu einer Bindungsschleife. Glücklicherweise werden diese in QML ziemlich gut erkannt, sodass eine Sperre vermieden wird.

Jetzt gibt es nur noch eine letzte Sache zu erledigen: Betrachten Sie diese Komponenten-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'
        }
    }
}

Hier haben wir eine interne Bindung der color-Eigenschaft, indem Sie die propertyname: valueToBeBoundTo-Syntax. Das bedeutet, dass die interne Bindung möglicherweise durch eine externe Zuweisung des überschrieben wird color-Eigentum. Ersetzen Sie diese Bindung durch a Binding-Objekt, und es sollte dir gut gehen.

Andersherum würde es genauso gehen: color extern an einen Wert gebunden ist, und Sie dann ein Signal intern behandeln und ihm einen Wert zuweisen, würde die externe Bindung verloren gehen, wenn sie nicht durch a erstellt wird Binding-Objekt.

Dies ist nur ein allgemeiner Überblick. Es gibt noch viel mehr Details, die das Verhalten von Binding verändern könnten. Aber ich denke, ich habe gezeigt, wie man eine erstellen kann Zwei-Wege-Bindung und erwähnte einige Fallstricke, auf die Sie stoßen könnten.

Benutzer-Avatar
Benutzer240515

Das funktioniert bei mir mit Qt Quick Controls 2:

TextField {
    id: txt

    text: testMessage.message

    onTextChanged: testMesage.message = txt.text
}

1018800cookie-checkZwei-Wege-bindendes C++-Modell in QML

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy