如何集成QML与C++?


如何集成QML与C++?

Qt,QML,Qt Quick

本文是关于如何向Qml暴露C++ 对象和注册C++ 类 这一系列教程的第一篇文章。这一系列的教程名字就叫“ 如何集成C++ 和Qml ”。在Qt软件开发中,使用Qt 6这一新版本来恰当和轻松地实现这一关键机制,还不够清晰。特别是有不少朋友正从qmake转为CMake。因此,我们认为这恰好是个好机会,来说清楚Qml和C++ 集成的各种方式的细节。
接下来的博文会涵盖诸如模型和插件,但现在我们先聚焦于基础一些的,来说清楚如何从qml访问C++ 对象和如何向qml注册C++ 类。

Qt Creator 快捷键找到更多有用的快捷键

在完善语法格式后的类AppManager看起来象这样:

  1. #ifndef APPMANAGER_H 
  2. #define APPMANAGER_H 
  3.  
  4. #include  
  5.  
  6. class AppManager : public QObject 
  7. { 
  8. Q_OBJECT 
  9. Q_PROPERTY(bool isNightMode READ isNightMode WRITE setIsNightMode NOTIFY isNightModeChanged) 
  10.  
  11. public: 
  12. explicit AppManager(QObject *parent = nullptr); 
  13.  
  14. bool isNightMode() const; 
  15. void setIsNightMode(bool isNightMode); 
  16.  
  17. signals: 
  18. void isNightModeChanged(); 
  19.  
  20. private: 
  21. bool m_isNightMode = false; 
  22. }; 
  23.  
  24. #endif // APPMANAGER_H 

main.cpp文件(或其它可以访问Qml 引擎的地方)实例化类对象,并使用引擎顶级上下文的方法:QQmlContext::setContextProperty(const QString &name, QObject *value) 来将其暴露。需要为方法传入即将暴露给Qml的可访问的对象名,以及指向该对象的指针。main.cpp大概是这个样子:

  1. #include  
  2. #include  
  3. #include  
  4. #include  
  5.  
  6. int main(int argc, char *argv[]) 
  7. { 
  8. QGuiApplication app(argc, argv); 
  9.  
  10. QQmlApplicationEngine engine; 
  11.  
  12. // exposing C++ object to Qml 
  13. AppManager *appManager = new AppManager(&app); 
  14. engine.rootContext()->setContextProperty("appManager", appManager); 
  15.  
  16. const QUrl url(u"qrc:/testapp/main.qml"_qs); 
  17. QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, 
  18. &app, [url](QObject *obj, const QUrl &objUrl) { 
  19. if (!obj && url == objUrl) 
  20. QCoreApplication::exit(-1); 
  21. }, Qt::QueuedConnection); 
  22. engine.load(url); 
  23.  
  24. return app.exec(); 
  25. } 

大概就这样。你所需要做的就是调用setContextProperty() 方法。这样,就可以从Qml中访问C++ 对象了。 本例中,还会写一个简单的Qml 代码来根据appManager 的isNightMode属性值来更改应用的主题。应用会有一个按钮,以允许用户来更改属性值。

  1. import QtQuick 
  2. import QtQuick.Controls 
  3.  
  4. Window { 
  5. id: root 
  6.  
  7. readonly property color darkColor: "#218165" 
  8. readonly property color lightColor: "#EBEBEB" 
  9.  
  10. width: 280 
  11. height: 150 
  12. visible: true 
  13. title: qsTr("Expose C++ object test") 
  14.  
  15. color: root.lightColor 
  16.  
  17. Column { 
  18. anchors.centerIn: parent 
  19. spacing: 20 
  20.  
  21. Text { 
  22. id: resultText 
  23. color: root.darkColor 
  24. } 
  25.  
  26. Button { 
  27. anchors.horizontalCenter: parent.horizontalCenter 
  28.  
  29. text: qsTr("Start operation") 
  30. palette.buttonText: root.darkColor 
  31.  
  32. onClicked: { 
  33. appManager.performOperation() 
  34. } 
  35. } 
  36. } 
  37.  
  38. Connections { 
  39. target: appManager 
  40.  
  41. function onOperationFinished(result) { 
  42. resultText.text = "Operation result: " + result 
  43. } 
  44. } 
  45. } 

如你所见,C++ 对象的属性既可读又可写。因为宏Q_PROPERTY里的isNightModeChanged() 信号,文本内容及其颜色会自动调整。当程序运行时,效果如下:

qt_add_qml_method()就是解决方案。当使用Qt Creator’s New Project向导来创建新的工程时, 在默认的CMakeLists.txt文件中会被用到,它看上去是这样:

  1. qt_add_qml_module(testapp 
  2. URI testapp 
  3. VERSION 1.0 
  4. QML_FILES main.qml 
  5. ) 

为了向Qml注册C++ 类,需要通过这个CMake方法,指定添加到Qml模块的C++ 源文件列表。向下面的例子这样设置SOURCES参数。

  1. qt_add_qml_module(testapp 
  2. URI testapp 
  3. VERSION 1.0 
  4. QML_FILES main.qml 
  5. SOURCES AppManager.h AppManager.cpp 
  6. ) 

在Qml文件中导入模块后,就可以象实例化其它Qml类型那样实例化AppManager类了。

  1. import QtQuick 
  2. import QtQuick.Controls 
  3. import testapp // own module 
  4.  
  5. Window { 
  6. id: root 
  7.  
  8. readonly property color darkColor: "#218165" 
  9. readonly property color lightColor: "#EBEBEB" 
  10.  
  11. width: 280 
  12. height: 150 
  13. visible: true 
  14. title: qsTr("Expose C++ object test") 
  15.  
  16. color: appManager.isNightMode ? root.darkColor : root.lightColor 
  17.  
  18. Column { 
  19. anchors.centerIn: parent 
  20. spacing: 20 
  21.  
  22. Text { 
  23. color: appManager.isNightMode ? root.lightColor : root.darkColor 
  24. text: qsTr("Is night mode on? - ") + appManager.isNightMode 
  25. } 
  26.  
  27. Button { 
  28. anchors.horizontalCenter: parent.horizontalCenter 
  29.  
  30. text: qsTr("Change mode") 
  31. palette.buttonText: appManager.isNightMode ? root.lightColor : root.darkColor 
  32.  
  33. // change isNightMode on clicked 
  34. onClicked: { 
  35. appManager.isNightMode = !appManager.isNightMode 
  36. } 
  37. } 
  38. } 
  39.  
  40. AppManager { 
  41. id: appManager 
  42. } 
  43. } 
Qt官方关于这个话题的参考

How to integrate Qml and C++ ?