2016-12-20 3 views
6

Ich versuche, mehr über QtQuick und QML zu lernen. Mein aktuelles Ziel ist es, zu verstehen, wie Daten von einem C++ - Modell an meine Sicht gebunden werden. Bisher war ich in der Lage, das Modell in meiner QML einzurichten und Daten aus dem Modell zu erhalten, aber ich kann nicht herausfinden, wie ich meine Daten aktualisieren soll.Zwei-Wege-Bindung C++ Modell in QML

Wie richte ich zwei Wege für meine C++ Bindung Modell? Unten ist der Code, den ich bisher geschrieben habe.

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. Ich bin ein riesiger Noob an diesem so fühlen sich frei, alle anderen Fragen, darauf hinzuweisen (Formatierung, Standards, etc.) ich in meinem Code haben, muss ich irgendwie lernen lol

Edit 1

Nach Lesen @ Derm Antwort änderte ich meinen Code 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 
} 

Antwort

12

Zweiweg-Bindung ist eine komplizierte Angelegenheit in QML, wie es funktioniert in der Regel als eine Zuordnung.

Also, wenn Sie eine Eigenschaft mit propertyname: valuetobeboundto binden und später wieder etwas zu propertyname zuweisen, wird diese Bindung verloren gehen.

Zur Umgehung des Problems gibt es zwei Möglichkeiten: Die Verwendung von Binding -Objekte oder nicht verbindlich verwenden, aber alle Eigenschaft Wechsel-Signale verarbeiten (das Modell emittiert ideal richtig) manuell einstellen.

Zum ersten finden Sie eine detaillierte Anweisung here. Hier verwenden sie das eine Binding -Objekt für jede Richtung. Die gute Sache ist, wird diese Binding s nicht durch Zuweisung eines neuen Binding außer Kraft gesetzt werden.

Bedenken Sie:

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

Zu Beginn wird der Wert von r1 auf den Zustand der b2 gebunden ist, aber sobald b3 wurde einmal gedrückt, r1 wird durch einen Klick auf b2 nicht mehr aktualisiert . Für r0 wird die Aktualisierung durch die beiden Binding -Objekte durchgeführt werden, und daher wird die Binding nicht verloren. Allerdings können Sie sehen, wie die Bindearbeiten: Wann immer der Zustand des Button ändert, wird die Binding aktualisiert werden. So die Presse UND die Freisetzung von b2 Signale ausgelöst wird, die durch die ersten Binding und das gleiche gilt für die Presse UND Relase von b3 behandelt werden.

Kommen jetzt zu der Zwei-Wege-Bindung. Hier ist es wichtig Binding-Loops zu vermeiden.

Während dieses Beispiel vollkommen in Ordnung ist. Der Wechsel von count0.count wird eine Änderung der count1.count auslösen. Nun wird geprüft, ob count0.count würde ein Update benötigen, aber der Wert ist bereits das Recht, so die Rekursion endet, und keine Bindung Schleife occures.

die zweite Bindung an

Ändern ändert
Binding { 
     target: count1 
     property: 'count' 
     value: count0.count + 1 
    } 

drastisch die Situation: Jetzt mit jedem Wechsel der count0.count, count1.count Bedarf erhöht werden. Die erste Binding versucht dann count0.count auf den gleichen Wert wie count1.count zu setzen, aber es gibt einfach keine Möglichkeit, dass sowohl Binding wird erfüllt, und keine Änderung vorgenommen werden muss, nachdem die andere Binding tat es funktioniert. Es wird zu einer Bindungsschleife führen. Zum Glück werden diese in QML ziemlich gut erkannt, so dass eine Sperre vermieden wird.

Jetzt nur noch eine letzte, was es gibt zu kümmern: Betrachten Sie diese 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' 
     } 
    } 
} 

Hier haben wir eine interne Bindung des color -property, durch die propertyname: valueToBeBoundTo -Syntax verwenden. Dies bedeutet, dass die interne Bindung möglicherweise durch eine externe Zuordnung der color -Eigenschaft überschrieben wird. Ersetzen Sie diese Bindung durch eine Binding -Objekt, und Sie sollten in Ordnung sein.

Das gleiche um den anderen Weg gehen würde: color extern auf einen Wert gebunden ist, und Sie dann ein Signal intern handhaben und einen Wert zuweisen, die externen Bindung würde verloren gehen, wenn sie nicht von einem Binding -Objekt erstellt .

Dies ist nur eine allgemeine Übersicht. Es gibt viel mehr Details, die das Verhalten von Binding verändern könnten. Aber ich glaube, ich habe gezeigt, wie Sie erstellen können ein Two-Way-Bindung und ganz einige Fallen erwähnt, könnten Sie stoßen.

+0

Dank @derM, die eine hervorragende Antwort war, ist es mir einen Platz zu starten gegeben. Vielen Dank! –

+1

Gern geschehen! – derM