Using QML Bindings in C++ Applications(part 2)
2014-07-25 13:08
316 查看
Exchanging data between QML and C++QML and C++ objects cancommunicate withone another through signals, slots and property modifications.For a C++object, any data that is exposed to Qt's Meta-Object System - that is,properties,signals, slots and Q_INVOKABLE methods - becomeavailable toQML. On the QML side, all QML object data is automatically madeavailable to themeta-object system and can be accessed from C++.Calling functionsQML functions can be calledfrom C++ andvice-versa.All QML functions are exposedto themeta-object system and can be called usingQMetaObject::invokeMethod().Here is a C++application that uses this to call a QML function:1. //MyItem.qml2. import QtQuick 1.03. 4. Item {5. functionmyQmlFunction(msg) {6. console.log("Gotmessage:", msg)7. return "somereturnvalue"8. } 9. }1. //main.cpp2. QDeclarativeEngine engine;3. QDeclarativeComponent component(&engine,"MyItem.qml");4. QObject *object = component.create();5. 6. QVariant returnedValue;7. QVariant msg = "Hello fromC++";8. QMetaObject::invokeMethod(object,"myQmlFunction",9. Q_RETURN_ARG(QVariant, returnedValue),10. Q_ARG(QVariant, msg));11. 12. qDebug() << "QMLfunctionreturned:" <<returnedValue.toString();13.delete object;Notice the Q_RETURN_ARG() and Q_ARG() argumentsfor QMetaObject::invokeMethod()must be specifiedasQVariant types, as this is thegenericdata type used for QML functions and return values.To call a C++ function fromQML, thefunction must be either a Qt slot, or a function marked with theQ_INVOKABLE macro, to be available toQML.In the following example, the QML code invokes methods on the myObject object,which has beenset using QDeclarativeContext::setContextProperty():1. //MyItem.qml2. import QtQuick 1.03. 4. Item {5. width: 100; height: 1006. 7. MouseArea {8. anchors.fill: parent9. onClicked: {10. myObject.cppMethod("Hello from QML")11. myObject.cppSlot(12345)12. }13. } 14. } 1. class MyClass : public QObject2. {3. Q_OBJECT4. public:5. Q_INVOKABLE void cppMethod(const QString&msg) {6. qDebug() << "Calledthe C++method with"<< msg;7. }8. 9. public slots:10. void cppSlot(int number) {11. qDebug() << "Calledthe C++slot with" <<number;12. }13. };14. 15. int main(int argc, char *argv[]) {16. QApplication app(argc, argv);17. 18. QDeclarativeView view;19. MyClass myClass;20. view.rootContext()->setContextProperty("myObject", &myClass);21. 22. view.setSource(QUrl::fromLocalFile("MyItem.qml"));23. view.show();24. 25. return app.exec(); 26. }QML supports the calling ofoverloaded C++functions. If there are multiple C++ functions with the same namebut differentarguments, the correct function will be called according to thenumber and thetypes of arguments that are provided.Receiving signalsAll QML signals areautomatically availableto C++, and can be connected to using QObject::connect()like any ordinary Qt C++signal. In return, any C++ signal can be received by aQML object usingsignal handlers.Here is a QML component with asignal named qmlSignal. This signal isconnected to a C++ object's slot using QObject::connect(),so that the cppSlot() methodis called wheneverthe qmlSignal is emitted:1. //MyItem.qml2. import QtQuick 1.03. 4. Item {5. id: item6. width: 100; height: 1007. 8. signal qmlSignal(stringmsg)9. 10. MouseArea {11. anchors.fill: parent12. onClicked:item.qmlSignal("Hello from QML")13. } 14. } 1. class MyClass : public QObject2. {3. Q_OBJECT4. public slots:5. void cppSlot(const QString &msg) {6. qDebug() << "Calledthe C++slot with message:" << msg;7. }8. };9. 10. int main(int argc, char *argv[]) {11. QApplication app(argc, argv);12. 13. QDeclarativeView view(QUrl::fromLocalFile("MyItem.qml"));14. QObject *item = view.rootObject();15. 16. MyClass myClass;17. QObject::connect(item,SIGNAL(qmlSignal(QString)),18. &myClass,SLOT(cppSlot(QString)));19. 20. view.show();21. return app.exec(); 22. } To connect to Qt C++ signalsfrom withinQML, use a signal handler with the on<SignalName> syntax.If the C++ objectis directly creatable from within QML (see Defining new QML elements above) then thesignalhandler can be defined within the object declaration. In the followingexample,the QML code creates a ImageViewer object,and the imageChanged and loadingError signalsof the C++ objectare connected to through onImagedChanged and onLoadingError signalhandlers in QML:1. class ImageViewer : publicQDeclarativeItem2. {3. Q_OBJECT4. Q_PROPERTY(QUrl imageREAD image WRITE setImage NOTIFYimageChanged)5. public:6. ...7. signals:8. void imageChanged();9. void loadingError(const QString&errorMsg); 10. }; 1. ImageViewer {2. onImageChanged:console.log("Imagechanged!")3. onLoadingError:console.log("Imagefailed toload:",errorMsg) 4. }(Note that if a signal has beendeclared asthe NOTIFY signal for a property, QML allows it to be received withan on<Property>Changed handler even ifthesignal's name does not follow the<Property>Changed namingconvention. In theabove example, if the "imageChanged" signal wasnamed"imageModified" instead, the onImageChanged signalhandler wouldstill be called.)If, however, the object withthe signal isnot created from within the QML code, and the QML item only has areference tothe created object - for example, if the object was set usingQDeclarativeContext::setContextProperty()-then the Connections element can be usedinstead tocreate the signal handler:
1. ImageViewer viewer;2. 3. QDeclarativeView view;4. view.rootContext()->setContextProperty("imageViewer", &viewer);5. 6. view.setSource(QUrl::fromLocalFile("MyItem.qml")); 7. view.show(); 1. //MyItem.qml2. import QtQuick 1.03. 4. Item {5. Connections {6. target: imageViewer7. onImageChanged:console.log("Imagehas changed!")8. } 9. }C++ signals can use enum valuesasparameters provided that the enum is declared in the class that is emittingthesignal, and that the enum is registered using Q_ENUMS. See Using enumerations of acustomtype below for details.Modifying propertiesAny properties declared in a QML objectareautomatically accessible from C++. Given a QML item like this:1. //MyItem.qml2. import QtQuick 1.03. 4. Item {5. property int someNumber: 1006. }The value of the someNumber property can be set andreadusing QDeclarativeProperty,orQObject::setProperty()and QObject::property():1. QDeclarativeEngine engine;2. QDeclarativeComponentcomponent(&engine, "MyItem.qml");3. QObject *object = component.create();4. 5. qDebug() << "Propertyvalue:" << QDeclarativeProperty::read(object, "someNumber").toInt();6. QDeclarativeProperty::write(object, "someNumber", 5000);7. 8. qDebug() << "Propertyvalue:" << object->property("someNumber").toInt();9. object->setProperty("someNumber", 100);You should always use QObject::setProperty(), QDeclarativeProperty or QMetaProperty::write()tochange a QML property value, to ensure the QML engine is made aware oftheproperty change. For example, say you have a custom element PushButton with a buttonText property that internallyreflects thevalue of a m_buttonText membervariable.Modifying the member variable directly like this is not a good idea:1. //BAD!2. QDeclarativeComponent component(engine, "MyButton.qml");3. PushButton *button = qobject_cast<PushButton*>(component.create());4. button->m_buttonText = "Clickme";Since the value is changed directly,thisbypasses Qt's meta-object system andthe QML engine is notmade aware of the property change. This means propertybindings to buttonText would not beupdated, andany onButtonTextChanged handlers would notbecalled.Any Qt properties -that is, those declaredwith the Q_PROPERTY() macro-are accessible from QML. Here is a modified version of the earlier example onthis page; here, the ApplicationData class has abackgroundColor property. Thispropertycan be written to and read from QML:1. class ApplicationData : publicQObject2. {3. Q_OBJECT4. Q_PROPERTY(QColorbackgroundColor5. READbackgroundColor6. WRITE setBackgroundColor7. NOTIFY backgroundColorChanged)8. 9. public:10. void setBackgroundColor(constQColor &c) {11. if (c != m_color) {12. m_color = c;13. emitbackgroundColorChanged();14. }15. }16. 17. QColor backgroundColor() const {18. return m_color;19. }20. 21. signals:22. void backgroundColorChanged();23. 24. private:25. QColor m_color; 26. }; 1. //MyItem.qml2. import QtQuick 1.03. 4. Rectangle {5. width: 100; height: 1006. color: applicationData.backgroundColor7. 8. MouseArea {9. anchors.fill: parent10. onClicked:applicationData.backgroundColor = "red"11. } 12. }Notice the backgroundColorChanged signal is declaredas theNOTIFY signal for the backgroundColorproperty. If a Qtproperty does not have anassociated NOTIFY signal, the property cannot be usedforProperty Binding inQML, as the QML enginewould not be notified when the value changes. If you areusing custom types inQML, make sure their properties have NOTIFY signals sothat they can be used inproperty bindings.See Tutorial: Writing QMLextensionswith C++ for further details and examples on usingQt propertieswith QML.
1. ImageViewer viewer;2. 3. QDeclarativeView view;4. view.rootContext()->setContextProperty("imageViewer", &viewer);5. 6. view.setSource(QUrl::fromLocalFile("MyItem.qml")); 7. view.show(); 1. //MyItem.qml2. import QtQuick 1.03. 4. Item {5. Connections {6. target: imageViewer7. onImageChanged:console.log("Imagehas changed!")8. } 9. }C++ signals can use enum valuesasparameters provided that the enum is declared in the class that is emittingthesignal, and that the enum is registered using Q_ENUMS. See Using enumerations of acustomtype below for details.Modifying propertiesAny properties declared in a QML objectareautomatically accessible from C++. Given a QML item like this:1. //MyItem.qml2. import QtQuick 1.03. 4. Item {5. property int someNumber: 1006. }The value of the someNumber property can be set andreadusing QDeclarativeProperty,orQObject::setProperty()and QObject::property():1. QDeclarativeEngine engine;2. QDeclarativeComponentcomponent(&engine, "MyItem.qml");3. QObject *object = component.create();4. 5. qDebug() << "Propertyvalue:" << QDeclarativeProperty::read(object, "someNumber").toInt();6. QDeclarativeProperty::write(object, "someNumber", 5000);7. 8. qDebug() << "Propertyvalue:" << object->property("someNumber").toInt();9. object->setProperty("someNumber", 100);You should always use QObject::setProperty(), QDeclarativeProperty or QMetaProperty::write()tochange a QML property value, to ensure the QML engine is made aware oftheproperty change. For example, say you have a custom element PushButton with a buttonText property that internallyreflects thevalue of a m_buttonText membervariable.Modifying the member variable directly like this is not a good idea:1. //BAD!2. QDeclarativeComponent component(engine, "MyButton.qml");3. PushButton *button = qobject_cast<PushButton*>(component.create());4. button->m_buttonText = "Clickme";Since the value is changed directly,thisbypasses Qt's meta-object system andthe QML engine is notmade aware of the property change. This means propertybindings to buttonText would not beupdated, andany onButtonTextChanged handlers would notbecalled.Any Qt properties -that is, those declaredwith the Q_PROPERTY() macro-are accessible from QML. Here is a modified version of the earlier example onthis page; here, the ApplicationData class has abackgroundColor property. Thispropertycan be written to and read from QML:1. class ApplicationData : publicQObject2. {3. Q_OBJECT4. Q_PROPERTY(QColorbackgroundColor5. READbackgroundColor6. WRITE setBackgroundColor7. NOTIFY backgroundColorChanged)8. 9. public:10. void setBackgroundColor(constQColor &c) {11. if (c != m_color) {12. m_color = c;13. emitbackgroundColorChanged();14. }15. }16. 17. QColor backgroundColor() const {18. return m_color;19. }20. 21. signals:22. void backgroundColorChanged();23. 24. private:25. QColor m_color; 26. }; 1. //MyItem.qml2. import QtQuick 1.03. 4. Rectangle {5. width: 100; height: 1006. color: applicationData.backgroundColor7. 8. MouseArea {9. anchors.fill: parent10. onClicked:applicationData.backgroundColor = "red"11. } 12. }Notice the backgroundColorChanged signal is declaredas theNOTIFY signal for the backgroundColorproperty. If a Qtproperty does not have anassociated NOTIFY signal, the property cannot be usedforProperty Binding inQML, as the QML enginewould not be notified when the value changes. If you areusing custom types inQML, make sure their properties have NOTIFY signals sothat they can be used inproperty bindings.See Tutorial: Writing QMLextensionswith C++ for further details and examples on usingQt propertieswith QML.
相关文章推荐
- Using QML Bindings in C++ Applications(part 1)
- Using QML Bindings in C++ Applications(part 3)
- Using XML in Delphi applications. Part I.
- using qml in C++ aplications
- Financial Applications using Excel Add-in Development in C/C++
- Top 5 common mistakes to cause memory leaks in Symbian C++ applications
- How to create MFC applications that do not have a menu bar in Visual C++(MFC单文档和多文档程序中去掉菜单栏)(转)
- Using Windows XP Visual Styles and PrintWindow in Windows Applications
- Writing Web Services Client Applications using Visual C++
- Using Forms Authentication in ASP.NET - Part 1
- Oracle Database 10g XML & SQL: Design, Build & Manage XML Applications in Java, C, C++ & PL/SQL
- Using managed reference types in C++
- Matlab to C++ using boost.ublas and boost.bindings
- 【译】TetroGL: An OpenGL Game Tutorial in C++ for Win32 Platforms - Part 2 (下)
- Set Desktop Wallpaper using non-bitmap in Win32 C++
- Using JavaScript in Page Flow and Portal Applications
- 【译】TetroGL: An OpenGL Game Tutorial in C++ for Win32 Platforms - Part 1
- Using Visual C++ DLLs in a C++Builder Project
- An easy instance of using the template class in C++
- How to get the password text in a text with password property from another process using C++ - 用C++如何从不同进程获取密码框文本