C++与QML通信

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

上一篇:轻量级ORM框架 第二篇:Dapper


下一篇:pandas行列转换的4大技巧