百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术流 > 正文

QML实例化C++的类(qml编程实例)

citgpt 2024-07-12 09:51 8 浏览 0 评论


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

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

优点:

QML实例化C++的类(qml编程实例)

  • 可以方便的利用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开发必备技术栈学习路线和资料

源文件

#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文档可以使用
QQmlComponentQQuickView来加载。加载方法如下:

// 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属性值可以通过QQmlPropertyQObject::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):https://www.cnblogs.com/Nlj-Moon/p/16849680.html

Qt开发必备技术栈学习路线和资料

相关推荐

js中arguments详解

一、简介了解arguments这个对象之前先来认识一下javascript的一些功能:其实Javascript并没有重载函数的功能,但是Arguments对象能够模拟重载。Javascrip中每个函数...

firewall-cmd 常用命令

目录firewalldzone说明firewallzone内容说明firewall-cmd常用参数firewall-cmd常用命令常用命令 回到顶部firewalldzone...

epel-release 是什么

EPEL-release(ExtraPackagesforEnterpriseLinux)是一个软件仓库,它为企业级Linux发行版(如CentOS、RHEL等)提供额外的软件包。以下是关于E...

FullGC详解  什么是 JVM 的 GC
FullGC详解 什么是 JVM 的 GC

前言:背景:一、什么是JVM的GC?JVM(JavaVirtualMachine)。JVM是Java程序的虚拟机,是一种实现Java语言的解...

2024-10-26 08:50 citgpt

使用Spire.Doc组件利用模板导出Word文档
  • 使用Spire.Doc组件利用模板导出Word文档
  • 使用Spire.Doc组件利用模板导出Word文档
  • 使用Spire.Doc组件利用模板导出Word文档
  • 使用Spire.Doc组件利用模板导出Word文档
跨域(CrossOrigin)

1.介绍  1)跨域问题:跨域问题是在网络中,当一个网络的运行脚本(通常时JavaScript)试图访问另一个网络的资源时,如果这两个网络的端口、协议和域名不一致时就会出现跨域问题。    通俗讲...

微服务架构和分布式架构的区别

1、含义不同微服务架构:微服务架构风格是一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制(通常用HTTP资源API)。这些服务围绕业务能力构建并...

深入理解与应用CSS clip-path 属性
深入理解与应用CSS clip-path 属性

clip-pathclip-path是什么clip-path 是一个CSS属性,允许开发者创建一个剪切区域,从而决定元素的哪些部分可见,哪些部分会被隐...

2024-10-25 11:51 citgpt

HCNP Routing&Switching之OSPF LSA类型(二)
  • HCNP Routing&Switching之OSPF LSA类型(二)
  • HCNP Routing&Switching之OSPF LSA类型(二)
  • HCNP Routing&Switching之OSPF LSA类型(二)
  • HCNP Routing&Switching之OSPF LSA类型(二)
Redis和Memcached的区别详解
  • Redis和Memcached的区别详解
  • Redis和Memcached的区别详解
  • Redis和Memcached的区别详解
  • Redis和Memcached的区别详解
Request.ServerVariables 大全

Request.ServerVariables("Url")返回服务器地址Request.ServerVariables("Path_Info")客户端提供的路...

python操作Kafka

目录一、python操作kafka1.python使用kafka生产者2.python使用kafka消费者3.使用docker中的kafka二、python操作kafka细...

Runtime.getRuntime().exec详解

Runtime.getRuntime().exec详解概述Runtime.getRuntime().exec用于调用外部可执行程序或系统命令,并重定向外部程序的标准输入、标准输出和标准错误到缓冲池。...

promise.all详解 promise.all是干什么的
promise.all详解 promise.all是干什么的

promise.all详解promise.all中所有的请求成功了,走.then(),在.then()中能得到一个数组,数组中是每个请求resolve抛出的结果...

2024-10-24 16:21 citgpt

Content-Length和Transfer-Encoding详解
  • Content-Length和Transfer-Encoding详解
  • Content-Length和Transfer-Encoding详解
  • Content-Length和Transfer-Encoding详解
  • Content-Length和Transfer-Encoding详解

取消回复欢迎 发表评论: