Auto Binding 2 ways with Cpp Dynamic Property
-
Hi everyone !
I know how Qt binding work, but it not work for Dynamic Property.
Then I'm trying to implement 2 ways binding support Dynamic Property use simple function.The ideal is use eventFilter on DynamicPropertyChangedEvent to get notify when DynamicProperty changed then update destination property. (This is done as the code below)
Q_INVOKABLE void binding(QObject* qmlObj, QString qmlProp, QObject* cppObj, QString cppProp);
My class DynamicObject work as Context Object for an qml component
class DynamicObject : public QObject { Q_OBJECT private: QMap<QString, QVariant> bindingList; QMap<QString, QStringList> cppToQml; protected: bool eventFilter(QObject *obj, QEvent *event); public: Q_INVOKABLE void binding(QObject* qmlObj, QString qmlProp, QObject* cppObj, QString cppProp); Q_INVOKABLE QObject* thisContextObject(); } void DynamicObject::autoBinding(QObject* qmlObj, QString qmlProp, QObject* cppObj, QString cppProp) { //Set value from source (cpp) to qml qmlObj->setProperty(qPrintable(qmlProp), cppObj->property(qPrintable(cppProp))); // ==> Setup auto binding from source (cpp) to destination (qml) //Generate Unique Key for this binding QString uniqueKey = QString("%1%2%3%4").arg((size_t)qmlObj).arg(qmlProp).arg(size_t(cppObj)).arg(cppProp); qDebug() << "Binding Key: " + uniqueKey; if (!bindingList.contains(uniqueKey)) { //Add Binding Info into Binding List BindingInfo* bInfo = new BindingInfo(qmlObj, qmlProp, cppObj, cppProp); QVariant val = QVariant::fromValue(bInfo); bindingList.insert(uniqueKey, val); //Add Binding Map for Cpp Property to Qml Property QStringList qmlPropList; if (cppToQml.contains(cppProp)) { qmlPropList = cppToQml.value(cppProp); } qmlPropList.append(uniqueKey); cppToQml[cppProp] = qmlPropList; } // ==> Setup auto binding from destination (qml) to source (cpp) QVariant qmlVal = qmlObj->property(qPrintable(qmlProp)); qDebug() << qmlVal; if (qmlProp == "text") { //Try to connect qml signal textChanged with a lot in this object QObject::connect(qmlObj, SIGNAL(textChanged()), this, onTextChanged); //Try to forward qml object's events use event filter qmlObj->installEventFilter(this); } } bool DynamicObject::eventFilter(QObject *obj, QEvent *event) { qDebug() << "Event Captured: " << event->type() << " on Object (" << obj << ")"; if (event->type() == QEvent::KeyPress) { QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); qDebug("Key pressed: %d", keyEvent->key()); } if (event->type() == QEvent::KeyRelease) { QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); qDebug("Key released: %d", keyEvent->key()); } if (event->type() == QEvent::DynamicPropertyChange) { QDynamicPropertyChangeEvent *propEvent = static_cast<QDynamicPropertyChangeEvent *>(event); QString propName = propEvent->propertyName(); qDebug() << (size_t)obj << ":" << propName; } }
But it more difficult when I try to get notity when Qml Property changed
I'm tried eventFilter, but for each controls, I have to handle correct event (To much switch case)
Then I try to connect <property>Changed SIGNAL with corresponded slot (ex: textChanged => onTextChanged).
I got the pointer to qml Object & property name which I need to "listen" on, But I don't know how to cast QObject to Qml Type (for example: QObject to TextField)Any one have any ideal or solution for this?! Please help !!!
-
Hi,
Why do you need a big switch case ?
Since these are all dynamic properties, what do you need to cast the QObjects ?
If you are in such need for help, then you should maybe consider getting a commercial license which include support or hire someone to help you on this.
-
Dear @SGaist
"Why do you need a big switch case ?"
- As you can see in the DynamicObject::eventFilter. It already check if DynamicPropertyChanged Event fired or not.
I can get KeyPress, KeyRelease, but can't get DynamicPropertyChange. So, if I handle KeyRelease for TextField or SelectionChanged for ComboBox... etc... it will be a lot of case to switch (or if ... then)
"What do you need to cast the QObjects"
In Qml file, I'm setup autoBinding like this
Component.onCompleted: { viewModel = thisContextObject(); autoBinding(advSearch, "model", viewModel, "FilterList") autoBinding(txtName, "model", viewModel, "CompanyName") }
So I'm try to connect textChange signal of TextField (or other Property of other Qml Object) to related slot (in cpp autoBinding function). That why I need to cast QObject to TextField or something else.
- As you can see in the DynamicObject::eventFilter. It already check if DynamicPropertyChanged Event fired or not.
-
There's something I'm not sure I'm following properly. Can you explain a use case for dynamic properties in QML ?
-
@SGaist
Sorry for late responded. (I don't know why I can't get notification when mine topic got answer or comment.Let take an sample Code for the use case
//cpp QQmlContext* ctx = engine.rootContext(); ctx->setContextObject(mainViewModel); //QML TextField{ id : txtName } Component.onCompleted: { viewModel = thisContextObject(); autoBinding(txtName, "text", viewModel, "SelectedItem.Name"); }
As you can see in the Code above.
I setup an auto Binding between txtName.text <=> "SelectedItem.Name" ("SelectedItem" & "Name" are Dynamic Property in mainViewModel)
in Qt way (With normal Property declared by Q_PROPERTY), I have to setup a binding for each way like thisTextField{ id : txtName text : SelectedItem.Name } Binding { target: SelectedItem property: "Name" value: txtName.text }
What I want is "How to implement Qml to Cpp binding like what Binding Element does.
if I can have a look at Binding element source code, may be I can figure it out. -
You can look at the code without any problem. Just download the sources, either through the maintenance tool or clone it with git.