Hallo, ich bringe mir immer noch Qt Qml alleine bei und knappere schon
länger an TwoWay Bindings, deshalb konnte ich mich über dieses
veröffentlichtes Video von der Firma KDAB freuen.
https://www.kdab.com/kdab-qt-world-summit-2019/#component
QML Component Design: the two-way binding problem
Die PowerPoints wurden auch veröffentlicht, aber das Beispiel konnte ich
im Internet nicht finden, deshalb hab ich einige Dinge aus der PPT
extrahiert.
Das Interface wurde wie folgt dargestellt:
1 | #ifndef BOOLEANVALUE_H
|
2 | #define BOOLEANVALUE_H
|
3 | #include <QObject>
|
4 | #include <QDebug>
|
5 | #include <QTimer>
|
6 |
|
7 |
|
8 | class BooleanValue : public QObject
|
9 | {
|
10 | Q_OBJECT
|
11 | Q_PROPERTY(bool isOn READ isOn WRITE setOn NOTIFY isOnChanged)
|
12 |
|
13 | public:
|
14 | explicit BooleanValue(QObject *parent = nullptr);
|
15 | explicit BooleanValue(bool initValue, QObject *parent = nullptr);
|
16 |
|
17 | bool isOn() const;
|
18 | Q_SLOT void setOn(bool isOn); // Be sure to make it a slot or Q_INVOKABLE
|
19 |
|
20 | //convenience API is now possible
|
21 | Q_INVOKABLE void toggle();
|
22 |
|
23 |
|
24 | signals:
|
25 | void isOnChanged(bool isOn);
|
26 |
|
27 | // New for my personal test currently uncomment
|
28 | private slots:
|
29 | //void timeout();
|
30 |
|
31 | private:
|
32 | bool m_isOn = false;
|
33 |
|
34 | // New for my personal test currently uncomment
|
35 | // QTimer *m_timer;
|
36 |
|
37 |
|
38 | };
|
39 |
|
40 | #endif // BOOLEANVALUE_H
|
Die Implementierung konnte ich nicht finden, deshalb hab ich es
implementiert nach besten Gewissen.
1 | #include "booleanvalue.h"
|
2 |
|
3 | BooleanValue::BooleanValue(QObject *parent) : QObject(parent)
|
4 | {
|
5 | // m_timer = new QTimer(this);
|
6 | // m_timer->setSingleShot(true);
|
7 | // m_timer->setInterval(100);
|
8 | // connect(m_timer,&QTimer::timeout,this,&BooleanValue::timeout);
|
9 | }
|
10 |
|
11 | BooleanValue::BooleanValue(bool initValue, QObject *parent) : QObject(parent)
|
12 | {
|
13 | m_isOn = initValue;
|
14 | }
|
15 |
|
16 | bool BooleanValue::isOn() const
|
17 | {
|
18 | return m_isOn;
|
19 | }
|
20 |
|
21 | void BooleanValue::setOn(bool isOn)
|
22 | {
|
23 | if(m_isOn == isOn)
|
24 | return;
|
25 |
|
26 | m_isOn = isOn;
|
27 | emit isOnChanged(m_isOn);
|
28 |
|
29 | }
|
30 |
|
31 |
|
32 | void BooleanValue::toggle()
|
33 | {
|
34 | // New for my personal test currently uncomment
|
35 | // m_timer->start();
|
36 | m_isOn = !m_isOn;
|
37 | emit isOnChanged(m_isOn);
|
38 | qDebug() << "m_isOn Changed";
|
39 | }
|
40 |
|
41 | // New for my personal test currently uncomment
|
42 | //void BooleanValue::timeout()
|
43 | //{
|
44 | // m_isOn = !m_isOn;
|
45 | // emit isOnChanged(m_isOn);
|
46 | // qDebug() << "m_isOn Changed";
|
47 | //}
|
Die Type ist dann in der main.c registiert.
1 | qmlRegisterType<BooleanValue>("BooleanValue", 1, 0, "BooleanValue");
|
main.qml
1 | import QtQuick 2.12
|
2 | import QtQuick.Window 2.12
|
3 |
|
4 | Window {
|
5 | visible: true
|
6 | width: 640
|
7 | height: 480
|
8 | title: qsTr("Hello World")
|
9 |
|
10 | CheckBox{
|
11 |
|
12 | id: someController
|
13 | text: "checkbox"
|
14 | }
|
15 | Rectangle{
|
16 | color: someController.checked ? "red" : "blue"
|
17 | width: 100
|
18 | height: 100
|
19 | anchors.centerIn: parent
|
20 | }
|
21 | }
|
CheckBox.qml
1 | import QtQuick 2.0
|
2 | import BooleanValue 1.0
|
3 | import QtQuick.Controls 2.12
|
4 | import QtQuick.Layouts 1.12
|
5 |
|
6 | Item {
|
7 | id: root
|
8 |
|
9 | property alias checked: internal.isOn
|
10 | property alias text: label.text
|
11 |
|
12 | implicitHeight: checkbox.height
|
13 | implicitWidth: checkbox.width + checkbox.implicitWidth + 4
|
14 | //ui related code
|
15 |
|
16 |
|
17 |
|
18 |
|
19 | Rectangle {
|
20 | id: checkbox
|
21 | implicitWidth: 48
|
22 | implicitHeight: 48
|
23 | Rectangle{
|
24 | color: root.checked ? "black" : "white"
|
25 | anchors{
|
26 | fill: parent
|
27 | margins: 8
|
28 | }
|
29 | border {
|
30 | color: "black"
|
31 | width: 1
|
32 | }
|
33 | }
|
34 |
|
35 | Text{
|
36 | id: label
|
37 | anchors{
|
38 | left: checkbox.right
|
39 | verticalCenter: checkbox.verticalCenter
|
40 | margins: 4
|
41 | }
|
42 | font.pointSize: 16
|
43 |
|
44 |
|
45 | }
|
46 |
|
47 | BooleanValue {
|
48 | id: internal
|
49 | }
|
50 |
|
51 | MouseArea {
|
52 | anchors.fill: parent
|
53 | onClicked: {
|
54 | internal.toggle(); // works, using convenience function on BooleanValue
|
55 | // internal.setOn(!internal.isOn) // works too
|
56 | // internal.isOn = !internal.isOn // Wrong: breaks the binding
|
57 | }
|
58 | }
|
59 |
|
60 | }
|
61 | }
|
Die Zeile würde das TwoWay Binding brechen, soweit so gut.
1 | // internal.isOn = !internal.isOn // Wrong: breaks the
|
Wieso wird über das Q_PROPERTY Marco ein Setter angegeben?
1 | Q_PROPERTY(bool isOn READ isOn WRITE setOn NOTIFY isOnChanged)
|
Wieso nicht nur ein Readonly Property und eine Funktion Q_INVOKABLE oder
Slot zum setzen?
1 | Q_PROPERTY(bool isOn READ isOn NOTIFY isOnChanged)
|
Somit kommt doch keiner mehr auf die Idee
1 | internal.isOn = !internal.isOn // Wrong: breaks the binding
|
sondern benutzt immer den Slot in Qml Code zuschreiben
1 | internal.setOn(!internal.isOn)
|