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
}
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 Binding
s 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 b2
aber 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.
Das funktioniert bei mir mit Qt Quick Controls 2:
TextField {
id: txt
text: testMessage.message
onTextChanged: testMesage.message = txt.text
}
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