QML与C++的交互

来源:互联网 发布:final cutpro mac 编辑:程序博客网 时间:2024/05/17 04:57

原文地址 http://brionas.github.io/2014/08/15/How-to-integrate-qml-with-C++/


介绍

QML 是一种基于Javascript的描述性脚本语言。它的功能跟xrc文件类似,主要用来描述应用程序主界面。 所不同的是,它可以很方便的跟C++进行交互。qml 跟C++ 的交互方式主要有以下几种:

  • 可以直接在C++应用程序中加载qml文件,拿到界面各元素的指针,修改界面属性。这种方式跟xrc文件类似
  • 可以将C++对象expose 到qml中,然后在qml文件中访问该对象的属性或调用对象的方法
  • 可以自定义C++类,把该类注册给qml类型系统,然后可以像其他内置类型那样在qml中使用

如果充分利用qml的这些特性将qml与C++结合在一起使用可以给我们带来很多帮助,尤其对于提高代码质量改善现有设计。 具体表现在:

  • 界面与逻辑完美分离:用QML来定义界面,用C++来实现界面的响应逻辑。
  • 通常的做法是,当用户在界面上操作的时候,我们从qml文件里面调用C++的响应函数。
  • 应用MVC方便:用QML来描绘界面(View),用C++代码来实现Model 和 Controller。
  • 自定义控件容易:可以用C++来自定义自己的QML类型, 然后将它应用于我们的应用程序中。
  • 可以在程序运行时访问或修改QML对象的属性进而改变界面的显示

在C++代码中访问或修改QML对象

加载qml文件应用程序中

我们通常都是使用qml定义自己的界面, 然后在C++应用程序中加载qml文件创建用户界面。 我们可以用QQmlComponent或者QQuickView来加载一个QML文件,加载成功之后就可以将qml文件对应的界面显示出来 下面的例子演示了怎样通过qml文件创建一个用户界面程序:

  1. int main(int argc, char **argv)
  2. {
  3. Application app(argc, argv);
  4. QQuickView view(QUrl::fromlocalFile("main.qml"));// load the qml file
  5. view.show();
  6. return app.exec();
  7. }

访问QML中的子对象

加载QML文件之后,它会返回一个C++对象的指针,我们可以通过这个指针来修改QML的各种属性值。 具体的说就是可以通过QObject::setProperty函数来修改QML中控件的各种属性进而改变界面的显示效果。 下面的例子演示了怎样在C++代码中修改QML中的各种属性值来改变界面:

  1. // Cpp code
  2. QQuickView view;
  3. view.setSource(QUrl::fromLocalFile("main.qml"));
  4. QObject *object = view.rootObject();
  5. object->setProperty("hight", 500);
  6. object->setProperty("width", 500);
  7. QObject *rect = object->findChild<QObject*>("rect");
  8. if (rect) btn->setProperty("color", "blue");
  9. view.show();
  10. // main.qml
  11. Item {
  12. width: 200; height:200
  13. Rectangle {
  14. color: "green"
  15. width: 100; height: 100
  16. objectName: "rect"
  17. Rectangle {
  18. color: "blue"
  19. x: 100; y: 100; width: 100; height: 100
  20. }
  21. }
  22. }

实际上我们可以根据QML中每个控件的objectName(QML中每个控件都可以有一个唯一的标识类似XRC中的name) 通过函数findChild来拿到QML中各子控件的对象指针进而访问或修改控件,但是我们不建议这样做。因为使用QML的初衷 是将用户接口UI的实现与C++逻辑分离开来,如果将界面跟逻辑混杂在一起,失去了使用QML的意义了。所以C++的实现 应该尽可能少的知道QML用户界面的实现以及QML对象树的结构(QML中的各个控件是以树状结构组织起来的,每个控件 都可以包含子控件,每个子控件又可以有各自的子控件)。

在QML中访问C++中的对象并调用C++中的函数

当加载QML到C++应用程序中的时候,如果能够直接将C++中的数据嵌入到QML中将会非常的有用。 比如,我们可以在QML文件中通过被插入的C++对象来调用C++中的方法。或者用一个C++的对象 作为QML界面的数据模型。实际上,任何继承自QObject的对象都可以在运行时expose到QML中 供QML 代码调用,包括对象各种属性,方法,信号以及槽函数等。C++中的数据和函数不需要做 任何的修改就可以被QML代码直接访问。Qt 中提供了一系列的方法把C++中的数据注入到QML中使用。

用户想要在QML中访问C++ 中对象,方法如下:

  • 被访问的对象的必须继承自QObject
  • 在类的定义中添加Q_PROPERTY宏,它的作用是将类的某成员变量expose给QML使用。
  • 它同时定义了变量的读函数,写函数以及变量值改变时发出的信号
  • 在类的定义中添加Q_INVOKABLE宏,它的作用是将类的某成员函数expose给QML使用。槽函数不用添加Q_INVOKABLE宏,默认就可以在QML中调用
  • 在C++代码中,通过函数QQmlContext::setContextProperty()将对象注入QML

下面的例子演示了怎样把一个C++ 中的对象暴露给qml然后在qml通过该对象中调用C++中的方法。 在下面的qml文件中,cppobj是我们注入的C++对象。我们通过该对象访问C++中的数据调用C++中的函数

  1. // main.qml
  2. Item {
  3. id: buttonrow
  4. width: 250; height: 50
  5. RowLayout {
  6. TextField {
  7. id: txtvalue1
  8. text: cppobj.value1 // get the value by MyCppObject::value1
  9. }
  10. TextField {
  11. id: txtvalue2
  12. text: cppobj.value2 // the value by MyCppObject::value2
  13. }
  14. Button {
  15. id:button1
  16. text: cppobj.value1 + cppobj.value2
  17. onClicked: {
  18. cppobj.resetAllValues(); // call the Cpp function to reset values, then the TextField value will be update automatolly
  19. }
  20. }
  21. }
  22. }

为了能够让上述qml代码工作, 我们需要在C++代码中实现MyCppObject类并把它暴露给QML,对应的C++代码如下:

  1. // cpp code
  2. int main(int argc, char* argv[])
  3. {
  4. QApplication app(argc, argv);
  5. QQmlEngine engine;
  6. MyCppObject cppobj("value1", "value2");
  7. // export the cppobj to qml and in the qml file, we can access the object by the name "cppobj"
  8. engine.rootContext()->setContextProperty("cppobj", &cppobj);
  9. QQuickView view(&engine, NULL);
  10. view.setSource(QUrl::fromLocalFile("main.qml"));
  11. view.show();
  12. return app.exec();
  13. }
  14. // MyCppObject implement
  15. class MyCppObject : public QObject
  16. {
  17. Q_OBJECT
  18. Q_PROPERTY(QString value1 READ value1 WRITE setValue1 NOTIFY value1Changed )
  19. Q_PROPERTY(QString value2 READ value2 WRITE setValue2 NOTIFY value2Changed )
  20. public:
  21. MyCppObject(const QString& value1, const QString& value2): QObject(), m_value1(value1), m_value2(value2) { }
  22. Q_INVOKABLE void resetAllValues();
  23. void setValue1(const QString& v);
  24. QString value1() const;// { return m_value1; }
  25. void setValue2(const QString& v);
  26. QString value2() const;// {return m_value2; }
  27. signals:
  28. void value1Changed(const QString&);
  29. void value2Changed(const QString&);
  30. private:
  31. QString m_value1;
  32. QString m_value2;
  33. };
  34. void MyCppObject::resetAllValues()
  35. {
  36. m_value1 = "";
  37. m_value2 = "";
  38. emit value1Changed(m_value1);
  39. emit value2Changed(m_value2);
  40. }
  41. void MyCppObject::setValue1(const QString& v)
  42. {
  43. if (m_value1 != v)
  44. {
  45. m_value1 = v;
  46. emit value1Changed(m_value1);
  47. }
  48. }
  49. QString MyCppObject::value1() const { return m_value1; }
  50. void MyCppObject::setValue2(const QString& v)
  51. {
  52. if (m_value2 != v)
  53. {
  54. m_value2 = v;
  55. emit value2Changed(m_value2);
  56. }
  57. }
  58. QString MyCppObject::value2() const {return m_value2; }

把C++类导入QML中使用

上面讲的是怎样把一个C++对象注入到qml中使用,其实我们也可以直接在qml文件中使用C++中定义的类型。 比如,用C++自定义一个类型,然后把自定义的类型注册给QML类型系统。这样自定义类型就可以像其它内置类型那样在qml文件中被使用。 通过这种方法,我们可以用C++ 来拓展现有的QML 类型系统,自定义自己的类型并把它整合到已有的QML代码里面。

用户想要自定义自己的类型,方法如下:

  • 在C++中,实现一个直接或者间接派生自QObject 类
  • 在C++中,通过函数qmlRegisterType将该自定义类型注册到QML 类型系统中
  • 在QML中,导入含有自定义类型的C++模块
  • 在QML中,像其他内置类型那样使用自定义的类型

下面的例子,在C++中自定义一个MyTimer 类型再把MyTimer类型注册给QML类型系统。然后可以像其他内置类型那样在qml文件中使用MyTimer类。 使用的时候,我们只需要导入包含MyTimer的对应的模块名称及其版本号”import CustomComponents 1.0”。模块的名称及版本号是在C++代码中我们注册MyTimer类给QML类型系统是指定的。

  1. import QtQuick 2.0
  2. import CustomComponents 1.0 // 这里指定MyTimer所属的模块名称及版本号
  3. Item {
  4. width: 200; height:200
  5. Rectangle {
  6. id: rect1
  7. color: "green"
  8. width: 100; height: 100
  9. Rectangle {
  10. id: rect2
  11. color: "blue"
  12. x: 100; y: 100; width: 100; height: 100
  13. MyTimer { // 这里MyTimer是在C++中定义的一个类
  14. id: timer
  15. interval: 1000 // MyTimer类有m_interval这个属性
  16. onTimeout: {
  17. rect1.color = "blue"
  18. rect2.color = "green"
  19. }
  20. }
  21. }
  22. }
  23. }

为了使上述的qml 代码工作, 我们需要在C++中定义MyTimer类,并把MyTimer注册个QML类型系统同时指定MyTimer所属的模块及版本号。 下面的C++代码将MyTimer 类注册给QML类型系统

  1. int main(int argc, char *argv[])
  2. {
  3. QApplication app(argc, argv);
  4. // 这里指定MyTimer所属的模块名称及版本号,必须与qml中保持一致
  5. qmlRegisterType<MyTimer>("CustomComponents", 1, 0, "MyTimer");
  6. QQuickView view;
  7. view.setSource(QUrl::fromLocalFile("main.qml"));
  8. view.show();
  9. return app.exec();
  10. }
  11. // MyTimer.h
  12. class MyTimer : public QObject
  13. {
  14. Q_OBJECT
  15. Q_PROPERTY(int interval READ interval WRITE setInterval NOTIFY intervalChanged)
  16. public:
  17. MyTimer(QObject * p = NULL);
  18. void setInterval(int msec);
  19. int interval();
  20. signals:
  21. void intervalChanged();
  22. void timeout();
  23. void activeChanged();
  24. public slots:
  25. void start();
  26. void stop();
  27. private:
  28. QTimer* m_timer;
  29. int m_interval;
  30. };

总结

以上简单介绍了qml与C++的交互的方式,主要包括以下几种:

  • 在C++中,加载qml文件
  • 在qml中,访问C++对象并调用C++方法
  • 在qml中,使用导入的C++ 类型

关于QML 与C++的交互,如果想进一步了解的话可以参照Qt官方文档。


0 0