C++ 与QML可以理解为两个平行的世界,由于很多Qt的模块无法直接在QML中使用,因此在业务开发的过程中,对于C++ 与QML的分工往往是使用QML来构建界面,使用C++ 来实现非界面的业务逻辑开发,所以我们会频繁使用到C++ 与QML的交互,因此对我在开发过程中经常使用的交互方法进行一个梳理。
对所有的交互方式给出三类总结:
1.推荐使用的原有框架:Qt Quick有一套Model-View-Delegate框架,在这套框架里面完成了C++ 与QML的通信,这个方式在另一篇文章“Model-View-Delegate编程框架”中有讨论,这里就不展开细说了
2.QML使用C++ 类(属于Qt元对象系统)与对象
Qt提供了两种在QML环境中使用C++ 对象的方式:
一是将一个C++类注册为QML环境的一个类型,在QML中使用该类型创建对象,看实例:
首先是创建一个属于Qt元对象系统的类
class TestClass: public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QString testStr READ getTestStr WRITE setTestStr NOTIFY
testStrChanged)
public:
TestClass(QQuickItem *parent = nullptr);
QString getTestStr() {return m_test;}
signals:
void testStrChanged(QString testStr);
public slots:
void setTestStr (const QString &testStr);
private:
QString m_testStr;
};
然后是将这个类注册为QML环境的一个类型
qmlRegisterType<TestClass>("TestModule", 1, 0, "TestClass");//这个方法就是用来将C++类型注册道QML系统中的
this->setSource(QUrl(qrc:/qml/main.qml));//this是QQuickView的派生类
接下来你就可以在你的QML代码里使用这个类型创建你需要的对象了
import TestModule 1.0
TestClass{
id:root
width: 50
height: 50
}
二是在C++中构造一个对象,将这个对象设置为QML的上下文属性
首先创建一个属于Qt元对象系统的类
class TestClass: public QObject
{
Q_OBJECT
Q_PROPERTY(QString testStr READ getTestStr WRITE setTestStr NOTIFY
testStrChanged)
public:
TestClass(QQuickItem *parent = nullptr);
QString getTestStr() {return m_test;}
signals:
void testStrChanged(QString testStr);
public slots:
void setTestStr (const QString &testStr);
private:
QString m_testStr;
};
QScopedPointer<TestClass> m_testClass{ nullptr };
m_testClass.reset(TestClass);
this->rootContext()->setContextProperty("MyTestClass",m_testClass.data());//
为此上下文设置属性(QQmlEngine提供了用于实例化QML组件的环境)
this->setSource(QUrl(qrc:/qml/main.qml));//this是QQuickView的派生类
在QML环境中使用在上下文中设置的属性
Text{
id:testText
text:MyTestClass.testStr
}
3.C++使用QML对象
通过元对象系统,我们可以找到QML环境中的某个对象,紧接着就可以通过元对象系统来访问它的属性、信号、槽等。
看下面这个实例:
auto childComponent= this->rootObject()->findChild<QQuickItem *>("childName");
this依然是我创建的一个QQuickView的派生类,如果你的QQuickView设置QML资源并实例化成功了,你可以通过QML 的root item来找到任何你想要的子组件通过子组件的objectName属性
Item{
id:root
width:50
height:50
objectName: "childName"
signal testSignal()
function testFunction(text){
console.log(test))
}
}
当拿到我们子组件以后,我们可以直接调用QML声明的函数,也可以关联信号槽等
if (childComponent) {
connect(childComponent, SIGNAL(testSignal()), this, SLOT(onTestSignal()));
QMetaObject::invokeMethod(childComponent, "testFunction"
, Q_ARG(QVariant, "test string"));
}
参考资料
【1】Qt Quick核心编程 第十一章 C++与QML混合编程
【2】Qt Assistant QQuickView
【2】Qt Assistant QQmlEngine
【3】Qt Assistant QQmlContext