C++与QML交互总结二

3 篇文章 0 订阅

目录

1.CPP调用QML

1.1 QMetaObject::invokeMethod调用

1.2 CPP中的信号绑定qml中的槽

2.QML调用CPP

2.1 QML单实例注册

2.2 将类对象注册到QML的上下文中

2.3 QML信号调用CPP槽

3.QML中注入一个cpp实例

3.1qmlRegisterType

3.2QML_ELEMENT

4.附加属性: QML_ATTACHED


以前写过一篇C++和QML交互的的文章(C++与QML交互总结_qml和c++交互_hsy12342611的博客-CSDN博客),很多网友都在看并提出了一些疑问,本篇结合网上的资料从另外一个角度再重新梳理一下C++与QML的交互。

1.CPP调用QML

1.1 QMetaObject::invokeMethod调用

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
// 元对象头文件
#include <QMetaObject>
#include <QDebug>


int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
    QGuiApplication app(argc, argv);

    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);

    //cpp调用qml指定对象的指定方法
    auto rootObj = engine.rootObjects();
    // rootObj.first()获取所有对象列表
    auto label = rootObj.first()->findChild<QObject*>("qml_label");

    // 通过元对象调用
    QVariant ret;
    QMetaObject::invokeMethod(label, "getText",
                              Q_RETURN_ARG(QVariant, ret),
                              Q_ARG(QVariant, " hello"));

    qDebug() << ret.toString();

    return app.exec();
}

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 as QtCtrl
import QtQuick.Layouts 1.0


Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("qml和cpp交互总结")

    Item {
        id: item
        anchors.fill: parent

        QtCtrl.Label {
            objectName: "qml_label"
            text: "QML Label"
            font.pixelSize: 25

            function getText(data) {
                return text + data
            }
        }
    }

}

1.2 CPP中的信号绑定qml中的槽

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
// 元对象头文件
#include <QMetaObject>
#include <QDebug>
#include <QQmlContext>
#include <QAbstractButton>
#include <QPushButton>
#include "person.h"


int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
    QGuiApplication app(argc, argv);

    Person person("张三", 18);

    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);

    //上下文: 将类对象注册到QML的上下文背景中
    auto ctext = engine.rootContext();
    ctext->setContextProperty("OtPerson", &person);

    // 先在上下文注入,再加载
    engine.load(url);

    //cpp获取qml中的指定对象
    auto rootObj = engine.rootObjects();
    // rootObj.first()获取所有对象列表
    auto button = rootObj.first()->findChild<QObject*>("qml_button");
    // 使用QT4的方式绑定信号和槽: qml的信号,cpp的槽
    QObject::connect(button, SIGNAL(clicked()), &person, SLOT(clickButton()));
    QObject::connect(button, SIGNAL(coutNum(int)), &person, SLOT(clickCal(int)));
    /*
    // 使用QT5的方式绑定信号和槽不可行,此处button is nullptr
    auto button = rootObj.first()->findChild<QPushButton*>("qml_button");
    if (!button) {
        qDebug() << "button is nullptr";
    }
    QObject::connect(button, &QAbstractButton::clicked, &person, &Person::clickButton);
    */


    return app.exec();
}

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 as QtCtrl
import QtQuick.Layouts 1.0
import QtQml 2.15


Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("qml和cpp交互总结")

    Item {
        id: item
        anchors.fill: parent

        QtCtrl.Button {
            objectName: "qml_button"
            text: "QML button"
            font.pixelSize: 25
            property int cal: 1
            // qml中自定义信号
            signal coutNum(int num)

            onClicked: {
                OtPerson.showInfo()
                if (0 == cal++ % 10) {
                    coutNum(cal)
                }
            }

            // cpp的信号绑定qml的槽
            Connections {
                target: OtPerson
                function onQmlCall() {
                    console.log("cpp call qml")
                }
            }

            Connections {
                target: OtPerson
                function onQmlCall(data) {
                    console.log("cpp call qml " + data)
                }
            }
        }
    }

}

2.QML调用CPP

2.1 QML单实例注册

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
// 元对象头文件
#include <QMetaObject>
#include <QDebug>
#include "person.h"


int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
    QGuiApplication app(argc, argv);

    Person person("张三", 18);
    // qml单实例注册
    qmlRegisterSingletonInstance("PersonMudle", 1, 0, "MyPerson", &person);

    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();
}

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 as QtCtrl
import QtQuick.Layouts 1.0
import PersonMudle 1.0


Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("qml和cpp交互总结")

    Item {
        id: item
        anchors.fill: parent

        QtCtrl.Button {
            objectName: "qml_button"
            text: "QML button"
            font.pixelSize: 25

            onClicked: {
                MyPerson.showInfo()
            }
        }
    }

}

2.2 将类对象注册到QML的上下文中

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
// 元对象头文件
#include <QMetaObject>
#include <QDebug>
#include <QQmlContext>
#include "person.h"


int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
    QGuiApplication app(argc, argv);

    Person person("张三", 18);

    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);

    //上下文: 将类对象注册到QML的上下文背景中
    auto ctext = engine.rootContext();
    ctext->setContextProperty("OtPerson", &person);

    return app.exec();
}

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 as QtCtrl
import QtQuick.Layouts 1.0


Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("qml和cpp交互总结")

    Item {
        id: item
        anchors.fill: parent

        QtCtrl.Button {
            objectName: "qml_button"
            text: "QML button"
            font.pixelSize: 25

            onClicked: {
                OtPerson.showInfo()
            }
        }
    }

}

2.3 QML信号调用CPP槽

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
// 元对象头文件
#include <QMetaObject>
#include <QDebug>
#include <QQmlContext>
#include <QAbstractButton>
#include <QPushButton>
#include "person.h"


int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
    QGuiApplication app(argc, argv);

    Person person("张三", 18);

    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);

    //上下文: 将类对象注册到QML的上下文背景中
    auto ctext = engine.rootContext();
    ctext->setContextProperty("OtPerson", &person);

    //cpp获取qml中的指定对象
    auto rootObj = engine.rootObjects();
    // rootObj.first()获取所有对象列表
    auto button = rootObj.first()->findChild<QObject*>("qml_button");
    // 使用QT4的方式绑定信号和槽: qml的信号,cpp的槽
    QObject::connect(button, SIGNAL(clicked()), &person, SLOT(clickButton()));
    QObject::connect(button, SIGNAL(coutNum(int)), &person, SLOT(clickCal(int)));
    /*
    // 使用QT5的方式绑定信号和槽不可行,此处button is nullptr
    auto button = rootObj.first()->findChild<QPushButton*>("qml_button");
    if (!button) {
        qDebug() << "button is nullptr";
    }
    QObject::connect(button, &QAbstractButton::clicked, &person, &Person::clieckButton);
    */


    return app.exec();
}

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 as QtCtrl
import QtQuick.Layouts 1.0


Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("qml和cpp交互总结")

    Item {
        id: item
        anchors.fill: parent

        QtCtrl.Button {
            objectName: "qml_button"
            text: "QML button"
            font.pixelSize: 25
            property int cal: 1
            // qml中自定义信号
            signal coutNum(int num)

            onClicked: {
                OtPerson.showInfo()
                if (0 == cal++ % 10) {
                    coutNum(cal)
                }
            }
        }
    }

}

3.QML中注入一个cpp实例

3.1qmlRegisterType

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
// 元对象头文件
#include <QMetaObject>
#include <QDebug>
#include <QQmlContext>
#include <QAbstractButton>
#include <QPushButton>
#include "person.h"
#include "tree.h"


int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
    QGuiApplication app(argc, argv);

    // 注册一个C++类型到qml中
    qmlRegisterType<Tree>("TreeMudle", 1, 0, "MyTree");

    Person person("张三", 18);

    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);

    //上下文: 将类对象注册到QML的上下文背景中
    auto ctext = engine.rootContext();
    ctext->setContextProperty("OtPerson", &person);

    // 先在上下文注入,再加载
    engine.load(url);

    //cpp获取qml中的指定对象
    auto rootObj = engine.rootObjects();
    // rootObj.first()获取所有对象列表
    auto button = rootObj.first()->findChild<QObject*>("qml_button");
    // 使用QT4的方式绑定信号和槽: qml的信号,cpp的槽
    QObject::connect(button, SIGNAL(clicked()), &person, SLOT(clickButton()));
    QObject::connect(button, SIGNAL(coutNum(int)), &person, SLOT(clickCal(int)));
    /*
    // 使用QT5的方式绑定信号和槽不可行,此处button is nullptr
    auto button = rootObj.first()->findChild<QPushButton*>("qml_button");
    if (!button) {
        qDebug() << "button is nullptr";
    }
    QObject::connect(button, &QAbstractButton::clicked, &person, &Person::clickButton);
    */


    return app.exec();
}

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 as QtCtrl
import QtQuick.Layouts 1.0
import QtQml 2.15
import TreeMudle 1.0


Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("qml和cpp交互总结")

    Item {
        id: item
        anchors.fill: parent

        QtCtrl.Button {
            objectName: "qml_button"
            text: tree.name + tree.age + tree.date//Qt.formatDate(tree.date, "yyyy-MM-dd hh:mm:ss")
            font.pixelSize: 25
            property int cal: 1
            // qml中自定义信号
            signal coutNum(int num)

            onClicked: {
                OtPerson.showInfo()
                if (0 == cal++ % 10) {
                    coutNum(cal)
                }
                tree.name = "李四"
            }

            // cpp的信号绑定qml的槽
            Connections {
                target: OtPerson
                function onQmlCall() {
                    console.log("cpp call qml")
                }
            }

            Connections {
                target: OtPerson
                function onQmlCall(data) {
                    console.log("cpp call qml " + data)
                }
            }
        }

        // 使用cpp注入的类型
        MyTree {
            id: tree
            name: 'My_Tree'
            age: 110
            date: new Date()

            onNameChanged: {
                console.log("changed name: " + name)

            }
        }
    }

}

3.2QML_ELEMENT

.pro

QT += quick

QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++17 qmltypes

QML_IMPORT_NAME = TreeMudle
QML_IMPORT_MAJOR_VERSION = 1

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
        main.cpp \
        person.cpp \
        tree.cpp

RESOURCES += qml.qrc

# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =

# Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH =

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

HEADERS += \
    person.h \
    tree.h

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
// 元对象头文件
#include <QMetaObject>
#include <QDebug>
#include <QQmlContext>
#include <QAbstractButton>
#include <QPushButton>
#include "person.h"
#include "tree.h"


int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
    QGuiApplication app(argc, argv);

    Person person("张三", 18);

    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);

    //上下文: 将类对象注册到QML的上下文背景中
    auto ctext = engine.rootContext();
    ctext->setContextProperty("OtPerson", &person);

    // 先在上下文注入,再加载
    engine.load(url);

    //cpp获取qml中的指定对象
    auto rootObj = engine.rootObjects();
    // rootObj.first()获取所有对象列表
    auto button = rootObj.first()->findChild<QObject*>("qml_button");
    // 使用QT4的方式绑定信号和槽: qml的信号,cpp的槽
    QObject::connect(button, SIGNAL(clicked()), &person, SLOT(clickButton()));
    QObject::connect(button, SIGNAL(coutNum(int)), &person, SLOT(clickCal(int)));
    /*
    // 使用QT5的方式绑定信号和槽不可行,此处button is nullptr
    auto button = rootObj.first()->findChild<QPushButton*>("qml_button");
    if (!button) {
        qDebug() << "button is nullptr";
    }
    QObject::connect(button, &QAbstractButton::clicked, &person, &Person::clickButton);
    */


    return app.exec();
}

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 as QtCtrl
import QtQuick.Layouts 1.0
import QtQml 2.15
import TreeMudle 1.0


Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("qml和cpp交互总结")

    Item {
        id: item
        anchors.fill: parent

        QtCtrl.Button {
            objectName: "qml_button"
            text: tree.name + tree.age + tree.date//Qt.formatDate(tree.date, "yyyy-MM-dd hh:mm:ss")
            font.pixelSize: 25
            property int cal: 1
            // qml中自定义信号
            signal coutNum(int num)

            onClicked: {
                OtPerson.showInfo()
                if (0 == cal++ % 10) {
                    coutNum(cal)
                }
                tree.name = "李四"
            }

            // cpp的信号绑定qml的槽
            Connections {
                target: OtPerson
                function onQmlCall() {
                    console.log("cpp call qml")
                }
            }

            Connections {
                target: OtPerson
                function onQmlCall(data) {
                    console.log("cpp call qml " + data)
                }
            }
        }

        // 使用cpp注入的类型
        Tree {
            id: tree
            name: 'My_Tree'
            age: 110
            date: new Date()

            onNameChanged: {
                console.log("changed name: " + name)

            }
        }
    }

}

person.h

#ifndef PERSON_H
#define PERSON_H

#include <QObject>
#include <QString>
#include "tree.h"

class Person : public QObject
{
    Q_OBJECT
    //类的附加属性
    QML_ATTACHED(Tree)

public:
    explicit Person(QObject *parent = nullptr);
    Person(QString name, int age);
    // 想要让QML调用函数,函数要加上宏Q_INVOKABLE
    Q_INVOKABLE void showInfo() const noexcept;

public slots:
    void clickButton() const noexcept;
    void clickCal(int data) const noexcept;

signals:
    void qmlCall() const;
    // cpp给qml传参数不是所有类型都可以,一般就字符串,json和基本类型可以
    void qmlCall(QString) const;

private:
    QString _name;
    int _age;

};

#endif // PERSON_H

person.cpp

#include "person.h"
#include <QDebug>

Person::Person(QObject *parent)
    : QObject{parent}
{

}

Person::Person(QString name, int age) {
    _name = name;
    _age = age;
}

void Person::showInfo() const noexcept {
    qDebug() << "name: " << _name << ", age: "<< _age;

    // cpp发送信号调用qml中的槽
    emit qmlCall();
    emit qmlCall("王五");
}

void Person::clickButton() const noexcept {
    qDebug() << __FUNCTION__ << " in qml: click button";
}

void Person::clickCal(int data) const noexcept {
    qDebug() << __FUNCTION__ << "  " << data;
}

tree.h

#ifndef TREE_H
#define TREE_H

#include <QObject>
#include <QDate>
#include <QtQml>

//使用QML_ELEMENT后,就会产生元数据类型:描述数据的数据

class Tree : public QObject
{
    Q_OBJECT
    // qml中使用cpp类的属性
    Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged)
    Q_PROPERTY(int age READ getAge WRITE setAge NOTIFY ageChanged)
    Q_PROPERTY(QDate date READ getDate WRITE setDate NOTIFY dateChanged)

    QML_ELEMENT

public:
    explicit Tree(QObject *parent = nullptr);
    Tree(QString nme, qint32 age, QDate date);

    void setName(QString name) noexcept;
    void setAge(qint32 age) noexcept;
    void setDate(QDate date) noexcept;

    QString getName() const noexcept;
    qint32 getAge() const noexcept;
    QDate getDate() const noexcept;

signals:
    void nameChanged(QString);
    void ageChanged();
    void dateChanged();

private:
    QString _name;
    qint32 _age;
    QDate _date;
};

#endif // TREE_H

tree.cpp

#include "tree.h"

Tree::Tree(QObject *parent)
    : QObject{parent}
{

}

Tree::Tree(QString name, qint32 age, QDate date) {
    _name = name;
    _age = age;
    _date = date;
}

void Tree::setName(QString name) noexcept {
    _name = name;
    emit nameChanged(name);
}

void Tree::setAge(qint32 age) noexcept {
    _age = age;
    emit ageChanged();
}

void Tree::setDate(QDate date) noexcept {
    _date = date;
    emit dateChanged();
}

QString Tree::getName() const noexcept {
    return _name;
}

qint32 Tree::getAge() const noexcept {
    return _age;
}

QDate Tree::getDate() const noexcept {
    return _date;
}

4.附加属性: QML_ATTACHED

使用附加属性:本质上会创建一个附加属性对象

效果如下:

tree.h

#ifndef TREE_H
#define TREE_H

#include <QObject>
#include <QDate>
#include <QtQml>

//使用QML_ELEMENT后,就会产生元数据类型:描述数据的数据

class Tree : public QObject
{
    Q_OBJECT
    // qml中使用cpp类的属性
    Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged)
    Q_PROPERTY(int age READ getAge WRITE setAge NOTIFY ageChanged)
    Q_PROPERTY(QDate date READ getDate WRITE setDate NOTIFY dateChanged)

    QML_ANONYMOUS

public:
    explicit Tree(QObject *parent = nullptr);
    Tree(QString nme, qint32 age, QDate date);

    void setName(QString name) noexcept;
    void setAge(qint32 age) noexcept;
    void setDate(QDate date) noexcept;

    QString getName() const noexcept;
    qint32 getAge() const noexcept;
    QDate getDate() const noexcept;

signals:
    void nameChanged(QString);
    void ageChanged();
    void dateChanged();

private:
    QString _name;
    qint32 _age;
    QDate _date;
};

#endif // TREE_H

tree.cpp

#include "tree.h"

Tree::Tree(QObject *parent)
    : QObject{parent}
{

}

Tree::Tree(QString name, qint32 age, QDate date) {
    _name = name;
    _age = age;
    _date = date;
}

void Tree::setName(QString name) noexcept {
    _name = name;
    emit nameChanged(name);
}

void Tree::setAge(qint32 age) noexcept {
    _age = age;
    emit ageChanged();
}

void Tree::setDate(QDate date) noexcept {
    _date = date;
    emit dateChanged();
}

QString Tree::getName() const noexcept {
    return _name;
}

qint32 Tree::getAge() const noexcept {
    return _age;
}

QDate Tree::getDate() const noexcept {
    return _date;
}

person.h

#ifndef PERSON_H
#define PERSON_H

#include <QObject>
#include <QString>
#include "tree.h"

class Person : public QObject
{
    Q_OBJECT
    //类的附加属性,将Tree中的属性附加到Person类中
    QML_ATTACHED(Tree)
    QML_ELEMENT

public:
    explicit Person(QObject *parent = nullptr);
    Person(QString name, int age);
    // 想要让QML调用函数,函数要加上宏Q_INVOKABLE
    Q_INVOKABLE void showInfo() const noexcept;

public slots:
    void clickButton() const noexcept;
    void clickCal(int data) const noexcept;

    static Tree* qmlAttachedProperties(QObject*);

signals:
    void qmlCall() const;
    // cpp给qml传参数不是所有类型都可以,一般就字符串,json和基本类型可以
    void qmlCall(QString) const;

private:
    QString _name;
    int _age;

};

#endif // PERSON_H

person.cpp

#include "person.h"
#include <QDebug>

Person::Person(QObject *parent)
    : QObject{parent}
{

}

Person::Person(QString name, int age) {
    _name = name;
    _age = age;
}

void Person::showInfo() const noexcept {
    qDebug() << "name: " << _name << ", age: "<< _age;

    // cpp发送信号调用qml中的槽
    emit qmlCall();
    emit qmlCall("王五");
}

void Person::clickButton() const noexcept {
    qDebug() << __FUNCTION__ << " in qml: click button";
}

void Person::clickCal(int data) const noexcept {
    qDebug() << __FUNCTION__ << "  " << data;
}

Tree* Person::qmlAttachedProperties(QObject* obj) {
    qDebug() << __FUNCTION__ << obj;
    return new Tree(obj);
}

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
// 元对象头文件
#include <QMetaObject>
#include <QDebug>
#include <QQmlContext>
#include <QAbstractButton>
#include <QPushButton>
#include "person.h"
#include "tree.h"


int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
    QGuiApplication app(argc, argv);

    Person person("张三", 18);

    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);

    //上下文: 将类对象注册到QML的上下文背景中
    auto ctext = engine.rootContext();
    ctext->setContextProperty("OtPerson", &person);

    // 先在上下文注入,再加载
    engine.load(url);

    //cpp获取qml中的指定对象
    auto rootObj = engine.rootObjects();
    // rootObj.first()获取所有对象列表
    auto button = rootObj.first()->findChild<QObject*>("qml_button");
    // 使用QT4的方式绑定信号和槽: qml的信号,cpp的槽
    QObject::connect(button, SIGNAL(clicked()), &person, SLOT(clickButton()));
    QObject::connect(button, SIGNAL(coutNum(int)), &person, SLOT(clickCal(int)));
    /*
    // 使用QT5的方式绑定信号和槽不可行,此处button is nullptr
    auto button = rootObj.first()->findChild<QPushButton*>("qml_button");
    if (!button) {
        qDebug() << "button is nullptr";
    }
    QObject::connect(button, &QAbstractButton::clicked, &person, &Person::clickButton);
    */


    return app.exec();
}

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 as QtCtrl
import QtQuick.Layouts 1.0
import QtQml 2.15
import PersonMudle 1.0


Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("qml和cpp交互总结")

    Item {
        id: item
        anchors.fill: parent

        QtCtrl.Button {
            objectName: "qml_button"
            text: Person.name + Person.age + Person.date
            font.pixelSize: 25
            property int cal: 1
            // qml中自定义信号
            signal coutNum(int num)

            onClicked: {
                OtPerson.showInfo()
                if (0 == cal++ % 10) {
                    coutNum(cal)
                }
                console.log(Person.name + " " + Person.age + " " + Person.date)
                console.log(this)
            }

            // cpp的信号绑定qml的槽
            Connections {
                target: OtPerson
                function onQmlCall() {
                    console.log("cpp call qml")
                }
            }

            Connections {
                target: OtPerson
                function onQmlCall(data) {
                    console.log("cpp call qml " + data)
                }
            }

            // 使用附加属性:本质上会创建一个附加属性对象
            Person.name: "赵六"
            Person.age: 25
            Person.date: new Date()
        }
    }

}

.pro

QT += quick

QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++17 qmltypes

QML_IMPORT_NAME = PersonMudle
QML_IMPORT_MAJOR_VERSION = 1

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
        main.cpp \
        person.cpp \
        tree.cpp

RESOURCES += qml.qrc

# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =

# Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH =

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

HEADERS += \
    person.h \
    tree.h

C++程序中使用QML QML API是分为三个主类——QDeclarativeEngine, QdeclarativeComponent 与 QDeclarativeContext。QDeclarativeEngine 提供QML运行的环境,QdeclarativeComponent 封装了QML Documents 与QDeclarativeContext允许程序导出数据到QML组件实例。 QML还包含了API的一个方便,通过QDeclarativeView 应用程序只需要简单嵌入QML组件到一个新的QGraphicsView就可以了。这有许多细节将在下面讨论。QDeclarativeView 主要是用于快速成型的应用程序里。 如果你是重新改进使用QMLQt应用程序,请参阅 整合QML到现有的Qt UI代码。 基本用法 每个应用程序至少需求一个QDeclarativeEngine。QDeclarativeEngine允许配置全局设置应用到所有的QML组件实例中,例如QNetworkAccessManager是用于网络通信以及永久储存的路径。如果应用程序需求在QML组件实例间需求不同的设置只需要多个QDeclarativeEngine。 使用QDeclarativeComponent类载入QML Documents。每个QDeclarativeComponent实例呈现单一QML文档。QDeclarativeComponent可以传递一个文档的地址或文档的原始文本内容。该文档的URL可以是本地文件系统的地址或通过QNetworkAccessManager支持的网络地址。 QML组件实例通过调用QDeclarativeComponent::create()模式来创建。在这里载入一个QML文档的示例并且从它这里创建一个对象。 QDeclarativeEngine *engine = new QDeclarativeEngine(parent); QDeclarativeComponent component(engine, QUrl::fromLocalFile(“main.qml”)); QObject *myObject = component.create(); 导出数据 QML组件是以QDeclarativeContext实例化的。context允许应用程序导出数据到该QML组件实例中。单个QDeclarativeContext 可用于一应用程序的所有实例对象或针对每个实例使用QDeclarativeContext 可以创建更为精确的控制导出数据。如果不传递一个context给QDeclarativeComponent::create()模式;那么将使用QDeclarativeEngine的root context。数据导出通过该root context对所有对象实例是有效的。 简单数据 为了导出数据到一个QML组件实例,应用程序设置Context属性;然后由QML属性绑定的名称与JavaScrip访问。下面的例子显示通过QGraphicsView如何导出一个背景颜色到QML文件中: //main.cpp #include <QApplication> #include <QDeclarativeView> #include <QDeclarativeContext> int main(int argc, char *argv[]) { QApplication app(argc, argv); QDeclarativeView view; QDeclarativeContext *context = view.rootContext(); context->setContextProperty(“backgroundColor”, QColor(Qt::yellow)); view.setSource(QUrl::fromLocalFile(“main.qml”)); view.show(); return app.exec(); } //main.qml import Qt 4.7 Rectangle { width: 300 height: 300 color: backgroundColor Text { anchors.centerIn: parent text: “Hello Yellow World!” } } 或者,如果你需要main.cpp不需要在QDeclarativeView显示创建的组件,你就需要使用QDeclarativeEngine::rootContext()替代创建QDeclarativeContext实例。 QDeclarativeEngine engine; QDeclarativeContext *windowContext = new QDeclarativeContext(engine.rootContext()); windowContext->setContextProperty(“backgroundColor”, QColor(Qt::yellow)); QDeclarativeComponent component(&engine, “main.qml”); QObject *window = component.create(windowContext); Context属性的操作像QML绑定的标准属性那样——在这个例子中的backgroundColor Context属性改变为红色;那么该组件对象实例将自动更新。注意:删除任意QDeclarativeContext的构造是创建者的事情。当window组件实例撤消时不再需要windowContext时,windowContext必须被消毁。最简单的方法是确保它设置window作为windowContext的父级。 QDeclarativeContexts 是树形结构——除了root context每个QDeclarativeContexts都有一个父级。子级QDeclarativeContexts有效的继承它们父级的context属性。这使应用程序分隔不同数据导出到不同的QML对象实例有更多自由性。如果QDeclarativeContext设置一context属性,同样它父级也被影响,新的context属性是父级的影子。如下例子中,background context属性是Context 1,也是root context里background context属性的影子。 结构化数据 context属性同样可用于输出结构化与写数据到QML对象。除了QVariant支持所有已经存在的类型外,QObject 派生类型可以分配给context属性。 QObject context属性允许数据结构化输出并允许QML来设置值。 下例创建CustomPalette对象并设置它作为palette context属性。 class CustomPalette : public QObject { Q_OBJECT Q_PROPERTY(QColor background READ background WRITE setBackground NOTIFY backgroundChanged) Q_PROPERTY(QColor text READ text WRITE setText NOTIFY textChanged) public: CustomPalette() : m_background(Qt::white), m_text(Qt::black) {} QColor background() const { return m_background; } void setBackground(const QColor &c) { if (c != m_background) { m_background = c; emit backgroundChanged(); } } QColor text() const { return m_text; } void setText(const QColor &c) { if (c != m_text) { m_text = c; emit textChanged(); } } signals: void textChanged(); void backgroundChanged(); private: QColor m_background; QColor m_text; }; int main(int argc, char *argv[]) { QApplication app(argc, argv); QDeclarativeView view; view.rootContext()->setContextProperty(“palette”, new CustomPalette); view.setSource(QUrl::fromLocalFile(“main.qml”)); view.show(); return app.exec(); } QML引用palette对象以及它的属性,为了设置背景与文本的颜色,这里是当单击窗口时,面板的文本颜色将改变成蓝色。 import Qt 4.7 Rectangle { width: 240 height: 320 color: palette.background Text { anchors.centerIn: parent color: palette.text text: “Click me to change color!” } MouseArea { anchors.fill: parent onClicked: { palette.text = “blue”; } } } 可以检测一个C++属性值——这种情况下的CustomPalette的文本属性改变,该属性必须有相应的NOTIFY信息。NOTIFY信号是属性值改变时将指定一个信号发射。 实现者应该注意的是,只有值改变时才发射信号,以防止发生死循环。访问一个绑定的属性,没有NOTIFY信号的话,将导致QML在运行时发出警告信息。 动态结构化数据 如果应用程序对结构化过于动态编译QObject类型;那么对动态结构化数据可在运行时使用QDeclarativePropertyMap 类构造。 从QML调用 C++ 通过public slots输出模式或Q_INVOKABLE标记模式使它可以调用QObject派生出的类型。 C++模式同样可以有参数并且可以返回值。QML支持如下类型: •bool •unsigned int, int •float, double, qreal •QString •QUrl •QColor •QDate,QTime,QDateTime •QPoint,QPointF •QSize,QSizeF •QRect,QRectF •QVariant 下面例子演示了,当MouseArea单击时控制“Stopwatch”对象的开关。 //main.cpp class Stopwatch : public QObject { Q_OBJECT public: Stopwatch(); Q_INVOKABLE bool isRunning() const; public slots: void start(); void stop(); private: bool m_running; }; int main(int argc, char *argv[]) { QApplication app(argc, argv); QDeclarativeView view; view.rootContext()->setContextProperty(“stopwatch”, new Stopwatch); view.setSource(QUrl::fromLocalFile(“main.qml”)); view.show(); return app.exec(); } //main.qml import Qt 4.7 Rectangle { width: 300 height: 300 MouseArea { anchors.fill: parent onClicked: { if (stopwatch.isRunning()) stopwatch.stop() else stopwatch.start(); } } } 值得注意的是,在这个特殊的例子里有更好的方法来达到同样的效果,在main.qml有”running”属性,这将会是一个非常优秀的QML代码: // main.qml import Qt 4.7 Rectangle { MouseArea { anchors.fill: parent onClicked: stopwatch.running = !stopwatch.running } } 当然,它同样可以调用 functions declared in QML from C++。 网络组件 如果URL传递给QDeclarativeComponent是一网络资源或者QML文档引用一网络资源,QDeclarativeComponent要先获取网络数据;然后才可以创建对象。在这种情况下QDeclarativeComponent将有Loading status。直到组件调用QDeclarativeComponent::create()之前,应用程序将一直等待。 下面的例子显示如何从一个网络资源载入QML文件。在创建QDeclarativeComponent之后,它测试组件是否加载。如果是,它连接QDeclarativeComponent::statusChanged()信号,否则直接调用continueLoading()。这个测试是必要的,甚至URL都可以是远程的,只是在这种情况下要防组件是被缓存的。 MyApplication::MyApplication() { // … component = new QDeclarativeComponent(engine, QUrl(“http://www.example.com/main.qml”)); if (component->isLoading()) QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), this, SLOT(continueLoading())); else continueLoading(); } void MyApplication::continueLoading() { if (component->isError()) { qWarning() << component->errors(); } else { QObject *myObject = component->create(); } } Qt资源 QML的内容可以使用qrc:URL方案从Qt 资源系统载入。例如: [project/example.qrc] <!DOCTYPE RCC> <RCC version=”1.0″> <qresource prefix=”/”> <file>main.qml</file> <file>images/background.png</file> </qresource> </RCC> [project/project.pro] QT += declarative SOURCES += main.cpp RESOURCES += example.qrc [project/main.cpp] int main(int argc, char *argv[]) { QApplication app(argc, argv); QDeclarativeView view; view.setSource(QUrl(“qrc:/main.qml”)); view.show(); return app.exec(); } [project/main.qml] import Qt 4.7 Image { source: “images/background.png” } 请注意,资源系统是不能从QML直接访问的。如果主QML文件被加载作为资源,所有的文件指定在QML中做为相对路径从资源系统载入。在QML层使用资源系统是完全透明的。这也意味着,如果主QML文件没有被加载作为资源,那么从QML不能访问资源系统。 1.这里主要是介绍,如何在c++调用QML中的函数和设置QML中的属性的问题 2.具体代码 // UICtest.qml import Qt 4.7 Rectangle { id: mainWidget; width: 640 height: 480 function callbyc(v) { mainWidget.color = v; return "finish"; } Rectangle{ id: secondRect; x: 100; y: 20; width: 400; height: 300; Rectangle{ x: 10; y: 20; width: 30; height: 40; color: "#FF035721" Text { objectName: "NeedFindObj"; anchors.fill: parent; text: ""; } } } } // main.cpp #include <QtGui/QApplication> #include <QtDeclarative/QDeclarativeView> #include <QtDeclarative/QDeclarativeEngine> #include <QtDeclarative/QDeclarativeComponent> #include <QtDeclarative/QDeclarativeContext> #include <QtDeclarative/QDeclarativeItem> #include <QMetaObject> int main(int argc, char *argv[]) { QApplication a(argc, argv); QDeclarativeView qmlView; qmlView.setSource(QUrl::fromLocalFile("../UICtest/UICtest.qml")); qmlView.show(); // 获取根节点,就是 QML中 id是mainWidget的节点 QDeclarativeItem *item = qobject_cast<QDeclarativeItem*>(qmlView.rootObject()); item->setProperty("color", QVariant("blue")); // 查找到我们需要的节点根均objectname NeedFindObj 来获得,并设置他的文本属性 QDeclarativeItem *item1 = item->findChild<QDeclarativeItem *>("NeedFindObj"); if (item1) { item1->setProperty("text", QVariant("OK")); } // 调用QML中的函数, 分别是 函数所在的对象, 函数名,返回值, 参数 QVariant returnVar; QVariant arg1 = "blue"; QMetaObject::invokeMethod(item, "callbyc", Q_RETURN_ARG(QVariant, returnVar),Q_ARG(QVariant, arg1)); qDebug(" %s",returnVar.toString().toLocal8Bit().data()); return a.exec(); } 说明: 这里的根节点是id为mainWidget的矩形元素,那么在C++中获取根节点后就可以,直接的设置他的属性了。其他属性也可以同样,调用指定节点内的函数是通过QMetaObject中的invokeMethod 来进行调用的。 最后所有关于QMLc++交互部分就基本写完,如果想要更多的东西,或者一些其他方法,强烈看看 http://doc.qt.nokia.com/4.7-snapshot/qtbinding.html,或者帮助文档,(究竟是不是我的文档里面没有还是怎么的)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值