QML实例化C++的类

1、QML调用(实例化)C++类

方法一:C++使用属性系统Q_PROPERTY

优点:

  • 可以方便的利用Qt的属性系统从QML中访问C++类中的属性。在QML程序中定义该C++类时会直接实例化该类。

缺点:

  • 无法在C++中对QML中实例化的类对象进行操作;
  • 只能访问使用属性系统Q_PROPERTY声明的属性;
  • 无法设置QML中访问指定的对象,即无法访问C++中的单例;

详细描述:

  1. 使用Qt Creator中的"Qt Quick Application"模板创建一个新的项目;

注意:取消选中New Project Wizard的Define Project Details部分中的With ui.qml文件选项。

  1. 添加C++类,命名为Call_Back,作为QML中使用的C++类,类的方法如下:

头文件

#ifndef CALL_BACK_H
#define CALL_BACK_H

#include <QObject>
#include <QString>

class Call_Back: public QObject
{
Q_OBJECT
//只读,只写,改变信号
Q_PROPERTY(QString userName READ userName WRITE setuserName NOTIFY userNameChanged)
public:
explicit Call_Back(QObject *parent = nullptr);
QString userName;
void setuserName(const QString &userName);
void callClassFunction();

signals:
void userNameChanged();

private:
QString m_userName;

};

#endif // CALL_BACK_H

本文福利,莬费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QSS,OpenCV,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓


源文件

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


// 构造函数
Call_Back::Call_Back(QObject *parent):
QObject(parent)
{
qDebug() << "-------Class construct-------";
m_userName = "hello world";
}

QString Call_Back::userName()
{
qDebug() << __FILE__ << __func__ << m_userName;
return m_userName;
}

void Call_Back::setuserName(const QString &userName)
{
qDebug() << __FILE__ << __func__ << userName;
if (userName == m_userName)
return;

m_userName = userName;
emit userNameChanged();
}

void Call_Back::callClassFunction()
{
qDebug() << __FILE__ << __func__ << __LINE__;
}

每次值改变时,该setUserName函数都会>发出userNameChanged信号。可以在QML中使用onUserNameChanged处理该信号。

  1. 在main.cpp中注册该方法到QML
    包含头文件Call_Back.h
    使用qmlRegisterType<BackEnd>("io.qt.examples", 1, 0, "Call_Back");注册方法;
  2. 将main.qml的内容替换如下:

main.qml

import QtQuick 2.6
import QtQuick.Controls 2.0
import io.qt.examples 1.0
//io.qt.examples 是使用qmlRegisterType进行注册的方法,名称必须和注册时一致
ApplicationWindow {
id: root
width: 300
height: 480
visible: true

BackEnd {
id: backend
}

Text {
id: txt1
x: 20
y: 20

text: backend.userName

}

TextField {
text: backend.userName
placeholderText: qsTr("User name")
anchors.centerIn: parent

onTextChanged:{
backend.userName = text
}
}
}

每当m_userName的值发生变化时,setUserName函数就会发出userNameChanged信号。可以使用onUserNameChanged处理程序从QML处理信号。

方法二:C++暴露(公开)方法到QML

  • QML可以通过C++代码中定义的功能轻松扩展。由于 QML 引擎与Qt 元对象系统的紧密集成,任何由QObject 派生类适当公开的功能都可以从 QML 代码访问。这使得C++数据和功能可以直接从QML访问,通常只需很少或不需要修改。
  • QML 引擎能够通过元对象系统自检QObject实例。这意味着任何 QML 代码都可以访问QObject 派生类实例的以下成员:

1、属性property
2、信号函数signals
3、public槽函数和使用 Q_INVOKABLE 声明的public函数允许元对象系统调用他们。
(此外,如果已使用Q_ENUMS声明枚举,则可以使用枚举。

属性:

  • 为了最大程度地与QML进行互操作,任何可写的属性都应该具有关联的NOTIFY信号,只要属性值发生变化就会发出该信号。例如:
    Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)这用来实现QML的属性绑定机制。
    信号:
  • 可以从QML代码访问QObject派生类型的任何公共信号。
  • QML引擎自动为从QML使用的QObject派生类型的任何信号创建信号处理程序。信号传递的所有参数都可以通过参数名称在信号处理程序中获得。同样可以在第一篇中找到demo。
    函数-方法:
    任何QObject派生类型的方法都可以从QML代码访问,只要它是:
  • 标有Q_INVOKABLE宏的公共方法
  • 一种public slots的方法
    QML支持调用重载的C ++函数。如果有多个具有相同名称但不同参数的C ++函数,则将根据提供的参数的数量和类型调用正确的函数。
    以下示例,点击窗口时会调用 Q_INVOKABLE 方法 Q_INVOKABLE void callClassFunction()和公有槽函数 — void slotSetAuthor()

message.h

#ifndef MESSAGE_H
#define MESSAGE_H

#include <QObject>
#include <QDebug>

class Message : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
public:
    void setAuthor(const QString &a) {
        qDebug() << __FILE__ << __func__ << __LINE__ << a;
        if (a != m_author) {
            m_author = a;
            emit authorChanged();
        }
    }
    QString author() const {
        qDebug() << __FILE__ << __func__ << __LINE__;
        return m_author;
    }
    // 定义QML访问函数 
    Q_INVOKABLE void callClassFunction(){
        qDebug() << __FILE__ << __func__ << __LINE__;
    }

public slots:
    void slotSetAuthor(){
        qDebug() << __FILE__ << __func__ << __LINE__;
        static int i = 0;
        ++i;
        QString str = QString("Tim%1").arg(i);
        setAuthor(str);
    }

signals:
    void authorChanged();
private:
    // 私有属性值
    QString m_author;
};
#endif // MESSAGE_H

qml文件

import QtQuick 2.5
import QtQuick.Window 2.2

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

    MouseArea {
        anchors.fill: parent
        onClicked: {
           // 调试信息
            console.log(qsTr('Clicked on background. Text: "' + msg.author + '"'))
            msg.author = msg.author + " hello world"
            msg.callClassFunction()
            msg.slotSetAuthor()
        }
    }


    Text {
        text: msg.author    // invokes Message::author() to get this value

        Component.onCompleted: {
            msg.author = "Jonah"  // invokes Message::setAuthor()
        }
    }
}

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <message.h>

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    Message msg;
    QQmlApplicationEngine engine;
    //用连接上下文函数使QML可以访问msg对象内的方法和属性
    //注意: 此处的“msg”必须小写字母开头,QML才能访问C++对象的函数与属性
    engine.rootContext()->setContextProperty("msg",&msg);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

2、C++访问QML属性与函数


所有的QML对象类型都是源自QObject类型,因此,QML引擎可以使用Qt元对象系统动态的实例化QML对象,并获取所创建对象的属性与方法。也就是说一旦创建了QML对象,就可以使用C++获取它的属性、函数与信号处理。

主要从以下几个方面介绍:

1、从C++加载QML对象,并读写属性
2、调用QML的函数
3、接收QML发出的信号

1、QML属性的读写

假设存在一个名为MyItem.qml的文件,内容如下:

import QtQuick 2.0

Item {
    width: 100; height: 100
}

加载QML文档的两种方法
在C++中,QML文档可以使用 QQmlComponent或 QQuickView来加载。加载方法如下:

// Using QQmlComponent
// QML引擎声明
QQmlEngine engine;
// QML组件声明,并使用引擎加载QML文件。
QQmlComponent component(&engine,
        QUrl::fromLocalFile("MyItem.qml"));
// 创建一个QObject实例
QObject *object = component.create();
...
delete object;

使用QQmlComponent需要调用QQmlComponent::create()创建一个QObject 实例。

// Using QQuickView
QQuickView view;
view.setSource(QUrl::fromLocalFile("MyItem.qml"));
view.show();
QObject *object = view.rootObject();

QQuickView会自动的创建一个QObject实例,可以通过view.rootObject()来获取。

获取QObject对象之后可以通过以下两种方法来设置属性:

object->setProperty("width", 500);           //法1
QQmlProperty(object, "width").write(500);    //法2

法2相对于法1,会移除上例中的width绑定属性;

除此之外,还可以将对象强制转换为实际类型,然后使用调用函数来设置属性。如下所示:

QQuickItem *item = qobject_cast<QQuickItem*>(object);
item->setWidth(500);

此时可以使用QMetaObject::invokeMethod()和QObject::connect()来调用item的行数和信号处理。详情见后文。

1.1 通过objectName来获取QML对象

假设MyItem.qml的内容如下,Item中存在一个objectName为“rect”的矩形

import QtQuick 2.0

Item {
    width: 100; height: 100

    Rectangle {
        anchors.fill: parent
        objectName: "rect"
    }
}

则 上述 矩形可以通过 如下方式获取:

QObject *rect = object->findChild<QObject*>("rect");
if (rect)
    rect->setProperty("color", "red");

如果存在多个objectName相同的对象,比如listView的Delegate中设置了一个特定的对象名称,则可以使用QObject::findChildren()来获取所有子对象。

警告: 尽管可以从C++中获取和操作QML对象,但是除非为了测试和原型设计,并不建议这样做。因为QML和C++集成的优势之一是能够在QML中实现与C++逻辑和后端分离的UI。

1.2 从C++访问QML对象类型的成员

在QML中使用 property 声明的属性可以在C++中被获取。假设MyItem.qml的内容如下:

// MyItem.qml
import QtQuick 2.0

Item {
    property int someNumber: 100
}

则someNumber属性值可以通过QQmlProperty,QObject::setProperty()和QObject::property()来访问。

QQmlEngine engine;
QQmlComponent component(&engine, "MyItem.qml");
QObject *object = component.create();
// 读取 属性值 并转换为int型
qDebug() << "Property value:" << QQmlProperty::read(object, "someNumber").toInt();
// 设置属性值
QQmlProperty::write(object, "someNumber", 5000);

qDebug() << "Property value:" << object->property("someNumber").toInt();
object->setProperty("someNumber", 100);

注意:你必须通过QObject::setProperty(), QQmlPropertyor QMetaProperty::write()这三种方法来设置QML的属性,才能够保证QML引擎对你的修改可知。

2. 调用QML函数

所有的QML函数都暴露在Qt元对象系统中,可以被C++使用QMetaObject::invokeMethod()来访问。向QML传递的函数参数和QML的返回值需要在C++中转换为QVariant值,因为这是QML函数参数和返回值的通用数据类型。例如:

// MyItem.qml
import QtQuick 2.0

Item {
    function myQmlFunction(msg) {
        console.log("Got message:", msg)
        return "some return value"
    }
}

C++代码如下:

// main.cpp
QQmlEngine engine;
QQmlComponent component(&engine, "MyItem.qml");
QObject *object = component.create();

QVariant returnedValue;
QVariant msg = "Hello from C++";
QMetaObject::invokeMethod(object, "myQmlFunction",
        Q_RETURN_ARG(QVariant, returnedValue),
        Q_ARG(QVariant, msg));

qDebug() << "QML function returned:" << returnedValue.toString();
delete object;

3.连接QML信号

所有的QML信号均可以在C++中被访问,可以像普通Qt的C++信号一样使用。
QML信号参数为:string

// MyItem.qml
import QtQuick 2.0

Item {
    id: item
    width: 100; height: 100

    signal qmlSignal(string msg)

    MouseArea {
        anchors.fill: parent
        onClicked: item.qmlSignal("Hello from QML")
    }
}

C++处理方式如下:

class MyClass : public QObject
{
    Q_OBJECT
public slots:
    void cppSlot(const QString &msg) {
        qDebug() << "Called the C++ slot with message:" << msg;
    }
};

int main(int argc, char *argv[]) {
    QGuiApplication app(argc, argv);

    QQuickView view(QUrl::fromLocalFile("MyItem.qml"));
    QObject *item = view.rootObject();

    MyClass myClass;
    QObject::connect(item, SIGNAL(qmlSignal(QString)),
                     &myClass, SLOT(cppSlot(QString)));

    view.show();
    return app.exec();
}

需要注意的是,当信号参数为QML对象时,参数类型应该为var,C++中槽函数的参数应该为QVariant。
QML文件如下:

import QtQuick 2.0

Item {
    id: item
    width: 100; height: 100

    signal qmlSignal(var anObject)

    MouseArea {
        anchors.fill: parent
        onClicked: item.qmlSignal(item)
    }
}

C++处理方式如下:

class MyClass : public QObject
{
    Q_OBJECT
public slots:
    void cppSlot(const QVariant &v) {
       qDebug() << "Called the C++ slot with value:" << v;

       QQuickItem *item = qobject_cast<QQuickItem*>(v.value<QObject*>());
       qDebug() << "Item dimensions:" << item->width()
                << item->height();
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    QQuickView view(QUrl::fromLocalFile("MyItem.qml"));
    QObject *item = view.rootObject();

    MyClass myClass;
    QObject::connect(item, SIGNAL(qmlSignal(QVariant)),
                     &myClass, SLOT(cppSlot(QVariant)));

    view.show();
    return app.exec();
}

3.嵌入C++对象到QML

  • 通过C++加载QML时,如果能嫁闺女C++对象嵌入到QML中可能会很用用。这使得可以在QML中调用嵌入对象的方法,或者将C++对象实例作为QML视图的数据模型。
  • 通过QQmlContext类可以将C++数据注入QML对象。此类(QQmlContext)将数据公开给QML对象的上下文,以便可以直接从QML代码的范围内引用数据。

1. 设置一个简单属性的上下文属性

例如,下面的QML代码中将访问一个当前域不存在的值currentDateTime

// MyItem.qml
import QtQuick 2.0

Text { text: currentDateTime }

currentDateTime可以通过C++代码使用QQmlContext::setContextProperty()直接嵌入到QML中。代码如下:

QQuickView view;
// 将属性currentDateTime设置链接属性,嵌入到QML中
view.rootContext()->setContextProperty("currentDateTime", QDateTime::currentDateTime());
view.setSource(QUrl::fromLocalFile("MyItem.qml"));
view.show();

注意: 由于QML计算的所有相关表达式都是在特定的上下文中计算的,因此如果更改了上下文,则所有绑定的属性都将重新计算。因此在除了初始化程序之外,应该小心使用上下文属性,这可能导致应用程序性能下降。

2.设置一个对象作为上下文属性

上下文属性可以包含QVariant或QObject *值。这意味着可以使用此方法注入自定义的C ++对象,并且可以在QML中直接修改和读取这些对象。修改上述实例,向QML中嵌入一个QObject实例,而不是一个QDateTime值。
C++:

class ApplicationData : public QObject
{
    Q_OBJECT
public:
    Q_INVOKABLE QDateTime getCurrentDateTime() const {
        return QDateTime::currentDateTime();
    }
};

int main(int argc, char *argv[]) {
    QGuiApplication app(argc, argv);

    QQuickView view;

    ApplicationData data;
    view.rootContext()->setContextProperty("applicationData", &data);

    view.setSource(QUrl::fromLocalFile("MyItem.qml"));
    view.show();

    return app.exec();
}

QML:

// MyItem.qml
import QtQuick 2.0

Text { text: applicationData.getCurrentDateTime() }

注意:从C++返回到QML的日期/时间值可以通过Qt.formatDateTime()和相关函数进行格式化。

2.1 QML处理C++对象的信号

如果QML需要接收上下文的信号,可以通过Connections类型来连接信号。如下:

Text {
    text: applicationData.getCurrentDateTime()

    Connections {
        target: applicationData
        onDataChanged: console.log("The application data changed!")
    }
}

上下文属性对与在QML视图中使用基于C++的数据模型非常有用。

文章转自博客园(Nlj):QML实例化C++的类

本文福利,莬费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QSS,OpenCV,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓

猜你喜欢

转载自blog.csdn.net/QtCompany/article/details/131994955