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
}
双方向バインディングは、通常、割り当てとして機能するため、QMLでは複雑な問題です。
したがって、プロパティをpropertyname: valuetobeboundto
でバインドし、後でpropertyname
に何かを再度割り当てると、このバインドは失われます。
回避策として、2つの方法があります。 Binding
-オブジェクトを使用するか、バインディングを使用せずに、すべてのproperty-change-signals(モデルが理想的に適切に出力する)を手動で処理します。
最初に、詳細な手順を見つけることができます ここ。 ここでは、各方向に1つのBinding
-オブジェクトを使用します。良いことは、新しいBinding
を割り当てることによって、それらのBinding
sがオーバーライドされないことです。
考えてみましょう:
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
を上げる必要があります。次に、最初のBinding
はcount0.count
をcount1.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を作成する方法を示し、かなりの落とし穴について言及したと思います。