Qt Quick入门教程(11) : qml C++交互介绍

在Qt界面开发时,用信号槽可以很容易实现各个窗口控件之间的交互,qml也是可以的,qml和C++可以相互调用,可以在qml代码中调用C++的类对象,也可以用C++类来获取qml的控件对象,下面分别介绍这两这种用法,需要源码的可以翻到最后直接下载。

一、qml调用C++

Qt 提供了两种在 QML 环境中使用 C++对象的方式∶

方式1:在C+中实现一个类,注册为 QML 环境的一个类型,在 QML 环境中使用该类型创建对象。

方式2:在 C++中构造一个对象,将这个对象设置为 QML 的上下文属性,在QML 环境中直接使用该属性。

不管哪种方式,对要导出的 C++类都有要求,不是一个类的所有方法、变量都可以在 QML 语境中使用,定义可以导出的 C++类
前提条件
要想将一个类或对象导出到 QML 中,下列的前提条件必须满足∶

(1)从 QObject 或 QObject 的派生类继承,并使用Q_OBJECT宏,这和使用信号与槽的前提条件一样的,这两个条件是为了让一个类能够进入Qt强大的元对象系统(meta-object system)中,只有使用元对象系统,一个类的某些方法或属性才可能通过字符串形式的名字来调用,才可以在 QML 中被访问。

(2)成员函数想在qml中被调用,则需要在声明前加上Q_INVOKABLE

(3)槽函数可以用类对象在qml代码中直接调用

(4)C++的成员属性可以用Q_PROPERTY宏设置

(5)枚举体需要用Q_ENUMS导出

下面介绍方式1, 例如下面这个类

#ifndef TESTBOX_H
#define TESTBOX_H

#includeclass TestBox : public QObject
{
    Q_OBJECT
    Q_ENUMS(ColorType)
    Q_PROPERTY(int mValue READ getValue WRITE setValue)

public:
    explicit TestBox(QObject *parent = nullptr);

    enum ColorType
    {
        Red,
        Green,
        Blue
    };

    // 成员函数想在qml中被调用,则需要在声明前加上Q_INVOKABLE
    Q_INVOKABLE int fun1();

    int getValue()
    {
        return m_value;
    }

    Q_INVOKABLE void setValue(int value)
    {
        m_value = value;
    }

signals:
    void sig_Value();

public slots:
    void on_Get();

private:
    int m_value = 0;
};

#endif // TESTBOX_H

类定义好后,在main.cpp中注册

#include#include#include "testbox.h"

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    qmlRegisterType("cpp.qt.TestBox", 1, 0, "TestBox");

    QQmlApplicationEngine engine;
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}

调用qmlRegisterType注册类TestBox,版本是1.0, 这个版本号可以自己定义。

最后在qml代码中调用

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
import cpp.qt.TestBox 1.0

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    TestBox{
        id:tb
        mValue:123  //可以在这里给对象赋值
    }

    Button{
        id:btn1
        text:"getValue"
        anchors.left: parent.left
        anchors.leftMargin: 40
        anchors.top: parent.top
        anchors.topMargin: 60

        onClicked: {
            tb.on_Get()  //调用槽函数
        }
    }

    Button{
        id:btn2
        text:"setValue"
        anchors.left: btn1.left
        anchors.top: btn1.bottom
        anchors.topMargin: 20

        onClicked: {
            tb.setValue(3456)
        }
    }

    Button{
        id:btn3
        text:"getenum"
        anchors.left: btn2.left
        anchors.top: btn2.bottom
        anchors.topMargin: 10

        onClicked: {
            valueTextFeild.text = TestBox.Blue  //调用枚举
        }
    }

    Button{
        id:btn4
        text:"invoke fun"
        anchors.left: btn3.left
        anchors.top: btn3.bottom
        anchors.topMargin: 10

        onClicked: {
            valueTextFeild.text = tb.fun1()  //调用普通成员函数
        }
    }

    TextField
    {
        id:valueTextFeild
        anchors.left: btn1.right
        anchors.leftMargin: 15
        anchors.top: btn1.top
    }

    //链接信号槽
    Connections{
        target: tb

        onSig_Value:{
            valueTextFeild.text = tb.mValue  //获取成员属性值
        }
    }
}

运行界面如下:
Qt Quick入门教程(11) : qml C++交互介绍

方式2:

在main.cpp中直接创建C++类对象,例如下面的代码,

 engine.rootContext()->setContextProperty("tb", new TestBox);

new对象 tb, 然后在qml中就可以直接使用tb了。

代码如下:

#include#include#include "testbox.h"
#includeint main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    //qmlRegisterType("cpp.qt.TestBox", 1, 0, "TestBox");

    QQmlApplicationEngine engine;

    engine.rootContext()->setContextProperty("tb", new TestBox);

    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}

这种方式有弊端,枚举值如法直接在qml中获得,可以用数字代替。

二、C++调用qml

在C++代码中获取qml控件时,需要用到objectName

Text{
        objectName: "textLabel"
        text:"Hello World"
        anchors.centerIn: parent
        font.pixelSize: 26
    }

    Button{
        objectName: "quitBtn"
        anchors.right: parent.right
        anchors.rightMargin: 10
        anchors.bottom: parent.bottom
        anchors.bottomMargin: 10
        text:qsTr("退出")
    }

上面的控件在声明时,都设置了属性objectName, qt程序在初始化时有个对象树,在对象树中根据objectName调用findChild可以获取到控件对象指针

//C++获取qml的控件
QObject* pQuitBtn = pRoot->findChild("quitBtn");
if(pQuitBtn)
{
    QObject::connect(pQuitBtn, SIGNAL(clicked()), &app, SLOT(quit()));
}

QObject *pText = pRoot->findChild("textLabel");
if(pText)
{
    bool bRet = QMetaObject::invokeMethod(pText, "setText", Q_ARG(QString, "AAAA"));
    qDebug() << "bRet = " << bRet; //调用失败,没有该方法

    pText->setProperty("color", QColor::fromRgb(255,0,0));
}

三、源码下载

以上具体功能,可以看我的代码,链接如下:
https://gitee.com/qwerwo/qml_code

3个工程
Qt Quick入门教程(11) : qml C++交互介绍

上一篇:Qml之自定义表格


下一篇:qml 锚点