QML调用C++的三种方法

62 篇文章 103 订阅
15 篇文章 145 订阅

1.注册法

由于QML引擎与Qt元对象系统的紧密集成,可以从QML代码访问由QObject派生的类适当公开的任何功能。这使得C ++类的属性和方法可以直接从QML访问,通常很少或无需修改。

QML引擎能够通过元对象系统内省QObject实例。这意味着,任何QML代码都可以访问QObject派生类实例的以下成员:

  • 属性(使用Q_PROPERTY注册的属性)
  • 方法(需注册为public slots或是标记为Q_INVOKABLE)
  • 信号

(此外,如果已使用Q_ENUMS声明枚举,则可以使用枚举。)

通常,无论是否已向QML类型系统注册了QObject派生类,都可以从QML访问它们。但是,如果QML引擎要访问其他类型信息(例如,如果要将类本身用作方法参数或属性,或者要将其中一个枚举类型用于以这种方式使用),那么该类可能需要注册。

#ifndef CPPOBJECT_H
#define CPPOBJECT_H
 
#include <QObject>
 
//派生自QObject
//使用qmlRegisterType注册到QML中
class CppObject : public QObject
{
    Q_OBJECT
    //注册属性,使之可以在QML中访问--具体语法百度Q_PROPERTY
    Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged)
    Q_PROPERTY(int year READ getYear WRITE setYear NOTIFY yearChanged)
 
public:
    explicit CppObject(QObject *parent = nullptr);
    //通过Q_INVOKABLE宏标记的public函数可以在QML中访问
    Q_INVOKABLE void sendSignal();//功能为发送信号
 
    //给类属性添加访问方法--myName
    void setName(const QString &name);
    QString getName() const;
    //给类属性添加访问方法--myYear
    void setYear(int year);
    int getYear() const;
 
signals:
    //信号可以在QML中访问
    void cppSignalA();//一个无参信号
    void cppSignalB(const QString &str,int value);//一个带参数信号
    void nameChanged(const QString name);
    void yearChanged(int year);
 
public slots:
    //public槽函数可以在QML中访问
    void cppSlotA();//一个无参槽函数
    void cppSlotB(const QString &str,int value);//一个带参数槽函数
 
private:
    //类的属性
    QString myName;
    int myYear;
};
 
#endif // CPPOBJECT_H

在头文件中,我定义了信号和public槽函数,以及Q_INVOKABLE宏标记的public函数,还通过Q_PROPERTY注册了两个属性,这些方法和属性之后都可以在QML中进行访问。

#include "CppObject.h"
 
#include <QDebug>
 
CppObject::CppObject(QObject *parent)
    : QObject(parent),
      myName("none"),
      myYear(0)
{
 
}
 
void CppObject::sendSignal()
{
    //测试用,调用该函数后发送信号
    qDebug()<<"CppObject::sendSignal";
    emit cppSignalA();
    emit cppSignalB(myName,myYear);
}
 
void CppObject::setName(const QString &name)
{
    qDebug()<<"CppObject::setName"<<name;
    if(myName!=name){
        qDebug()<<"emit nameChanged";
        myName=name;
        emit nameChanged(name);
    }
}
 
QString CppObject::getName() const
{
    qDebug()<<"CppObject::getName";
    return myName;
}
 
void CppObject::setYear(int year)
{
    qDebug()<<"CppObject::setYear"<<year;
    if(year!=myYear){
        qDebug()<<"emit yearChanged";
        myYear=year;
        emit yearChanged(myYear);
    }
}
 
int CppObject::getYear() const
{
    qDebug()<<"CppObject::getYear";
    return myYear;
}
 
void CppObject::cppSlotA()
{
    qDebug()<<"CppObject::cppSlotA";
}
 
void CppObject::cppSlotB(const QString &str, int value)
{
    qDebug()<<"CppObject::cppSlotB"<<str<<value;
}

在QML项目中添加类CppObject,并在QQmlApplicationEngine之前注册

函数原型
template<typename T>
int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName); 

单例 
int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, QJSValue(* ) ( QQmlEngine *, QJSEngine * ) callback)
int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, QObject *(* ) ( QQmlEngine *, QJSEngine * ) callback)
int qmlRegisterSingletonType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName)
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "CppObject.h"
 
int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
 
    QGuiApplication app(argc, argv);
 
    //qmlRegisterType注册C++类型至QML
    //arg1:import时模块名
    //arg2:主版本号
    //arg3:次版本号
    //arg4:QML类型名
    qmlRegisterType<CppObject>("MyCppObject",1,0,"CppObject");
 
    QQmlApplicationEngine engine;
 
    //也可以注册为qml全局对象
    //engine.rootContext()->setContextProperty("cppObj",new CppObject(qApp));
 
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;
 
    return app.exec();
}

通过使用qmlRegisterType,将刚才定义的QObject派生类注册到QML中(Qt5.15增加了新的注册方式)。

import QtQuick 2.9
import QtQuick.Window 2.9
//引入我们注册的模块
import MyCppObject 1.0
 
Window {
    id: root
    visible: true
    width: 500
    height: 300
    title: qsTr("QML调用Cpp对象")
    color:"green"
 
    signal qmlSignalA
    signal qmlSignalB(string str,int value)
 
    //鼠标点击区域
    MouseArea{
        anchors.fill: parent
        acceptedButtons: Qt.LeftButton | Qt.RightButton
        //测试时点击左键或右键
        onClicked: {
            if(mouse.button===Qt.LeftButton){
                console.log('----qml 点击左键:Cpp发射信号')
                cpp_obj.name="CSDN"  //修改属性会触发set函数,获取值会触发get函数
                cpp_obj.year=2020
                cpp_obj.sendSignal() //调用Q_INVOKABLE宏标记的函数
            }else{
                console.log('----qml 点击右键:QML发射信号')
                root.qmlSignalA()
                root.qmlSignalB('CSDN',2020)
            }
        }
    }
 
    //作为一个QML对象
    CppObject{
        id:cpp_obj
        //也可以像原生QML对象一样操作,增加属性之类的
        property int counts: 0
 		//值改变信号对应的槽函数,on +  *  方式信号槽,自动连接
        onYearChanged: {
            counts++
            console.log('qml onYearChanged',counts)
        }
        onCountsChanged: {
            console.log('qml onCountsChanged',counts)
        }
    }
 
    //组件加载完成执行
    Component.onCompleted: {
        //关联信号与信号处理函数的方式同QML中的类型
        //Cpp对象的信号关联到Qml
        //cpp_obj.onCppSignalA.connect(function(){console.log('qml signalA process')})
        cpp_obj.onCppSignalA.connect(()=>console.log('qml signalA process')) //js的lambda
        cpp_obj.onCppSignalB.connect(processB)
        //Qml对象的信号关联到Cpp
        root.onQmlSignalA.connect(cpp_obj.cppSlotA)
        root.onQmlSignalB.connect(cpp_obj.cppSlotB)
    }
 
    //定义的函数可以作为槽函数
    function processB(str,value){
        console.log('qml function processB',str,value)
    }
}

注册之后就能直接在QML中使用刚才定义的C++类型了,并且可以像QML定义的类型一样进行操作,如信号槽关联、属性绑定等

2.插件法

在Qt新项目向导下创建项目Library->Qt Quick 2 Extension Plugin
输入你的项目名称(此例为QMLPluginTemp)后, 创建项目会自动生成两个.cpp两个.h和一个qmldir文件
修改qmldir文件如下,module项名称就是后面导入的模块名称,和变量命名一样,不要有.或者其他特殊符号;plugin项就是新建插件的项目名称

qmldir 文件

module MyPlugin

plugin QMLPluginTemp
#ifndef MYITEM_H
#define MYITEM_H

#include <QQuickItem>

class MyItem : public QQuickItem
{
    Q_OBJECT
    Q_DISABLE_COPY(MyItem)

public:
    MyItem(QQuickItem *parent = 0);
    ~MyItem();    

     Q_INVOKABLE void read();

};

#endif // MYITEM_H

#include "myitem.h"
#include <QDebug>
MyItem::MyItem(QQuickItem *parent):
    QQuickItem(parent)
{
    // By default, QQuickItem does not draw anything. If you subclass
    // QQuickItem to create a visual item, you will need to uncomment the
    // following line and re-implement updatePaintNode()

    // setFlag(ItemHasContents, true);
}

MyItem::~MyItem()
{
}


void MyItem::read()
{

    qWarning() << "qml cpp plugin ";
}

#pragma once

#include <QQmlExtensionPlugin>

class QMLPluginTempPlugin: public QQmlExtensionPlugin
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")

public:
    void registerTypes(const char *uri);
};

#include "qmlplugintemp_plugin.h"
#include "myitem.h"

#include <qqml.h>

void QMLPluginTempPlugin::registerTypes(const char *uri)
{
    // @uri com.mycompany.qmlcomponents
    qmlRegisterType<MyItem>(uri, 1, 0, "MyItem");
}

构建项目后,在生成目录下找到qmldir文件和QMLPluginTemp.dll或QMLPluginTempd.dll(根据生成类型,debug还是release),放到一个新建文件夹MyPlugin(qmldir文件中的模块名)中,再将MyPlugin文件夹放入新建文件夹DLL中。
main函数中指定DLL文件夹路径

#include <QGuiApplication>
#include <QQmlApplicationEngine>

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

    QQmlApplicationEngine engine;
    engine.addImportPath("D:/QML/DLL/");
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

随后就可以在QML文件中使用插件
如果不指定,在Qt安装路径如D:\ProgramFiles\Qt\Qt580\5.8\mingw53_32\qml目录新建文件夹MyPlugin,把QMLPluginTemp.dllQMLPluginTempd.dllqmldir复制到MyPlugin文件夹内。

import QtQuick 2.6
import QtQuick.Window 2.2
import MyPlugin 1.0
Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    MainForm {
        anchors.fill: parent
        mouseArea.onClicked: {
            cppPlugin.read()
            console.log(qsTr('Clicked on background. Text: "' + textEdit.text + '"'))
        	}

    				}

    MyItem{
        id:cppPlugin
    		  }
}

3.暴露法

使用 setContextProperty,是将对象或数据暴露给 QML,一般默认就是全局单例。 函数原型

    void QQmlContext::setContextProperty(const QString &name, QObject *value)
    void QQmlContext::setContextProperty(const QString &name, const QVariant &value)
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
 
    QQmlApplicationEngine engine;
    engine.rootContext( )->setContextProperty(
                "Greeting",
                QObject::tr( "Hello QML from C++" ) );//变量,qml中直接就可用此属性的值
 
    engine.rootContext( )->setContextProperty(
                "CPlusPlusClass",
               new CPlusPlusClass() );//对象,此处new的时候创建对象了
    //setContextProperty,在qml中是全局的
    engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
 
    return app.exec();
}
Window {
    title: qsTr("Hello World")
    width: 640
    height: 480
    visible: true
    Text {
        id: text
        text: Greeting //直接就可以使用此属性
        anchors.centerIn: parent
    }
    Button {
 
        anchors.top: text.bottom
        text: "CPlusPlusClass.method()"
        MouseArea {
 
            anchors.fill: parent
            onClicked: {
                CPlusPlusClass.method()//调用类中的方法
            }
        }
    }
}

引用链接
QML与C++交互

  • 9
    点赞
  • 87
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt QML是一种基于JavaScript的声明式语言,用于快速构建跨平台的用户界面。它有时需要与C++代码交互,因为C++可以提供更高性能和更底层的功能。 要在Qt QML调用C函数,需要进行以下步骤: 1. 创建一个继承自QObject的C++类,并在其中定义所需的函数。这些函数需要使用Q_INVOKABLE宏进行标记,以便在QML调用。 ```cpp // MyFunctions.h #include <QObject> class MyFunctions: public QObject { Q_OBJECT public: explicit MyFunctions(QObject *parent = nullptr); Q_INVOKABLE void myFunction(); }; ``` 2. 在QML文件中导入C++类,并使用其实例调用函数。 ```qml import MyFunctions 1.0 Window { // ... Button { text: "调用C函数" onClicked: { MyFunctions.myFunction(); } } // ... } ``` 3. 在C++代码中将该类注册到QML引擎中。 ```cpp // main.cpp #include <QGuiApplication> #include <QQmlApplicationEngine> #include "MyFunctions.h" int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); qmlRegisterType<MyFunctions>("MyFunctions", 1, 0, "MyFunctions"); QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); } ``` 通过以上步骤,就可以在Qt QML中成功调用C函数了。在按钮点击事件中调用C++类的函数,可以在C++代码中执行所需的操作,并将结果返回到QML界面中进行展示。这种方式可以实现Qt QML框架与C++高性能功能的结合,使得开发者能够更好地发挥Qt的优秀特性和灵活性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值