菜鸟笔记
提升您的技术认知

qml与c 交互-ag真人游戏

前言

文档如是说,qml旨在通过c 代码轻松扩展。qt qml模块中的类使qml对象能够从c 加载和操作,qml引擎与qt元对象系统集成的本质使得c 功能可以直接从qml调用。这允许开发混合应用程序,这些应用程序是通过混合使用qml,javascript和c 代码实现的。

qml is designed to be easily extensible through c code. the classes in the qt qml module enable qml objects to be loaded and manipulated from c , and the nature of qml engine's integration with qt's meta object system enables c functionality to be invoked directly from qml. this allows the development of hybrid applications which are implemented with a mixture of qml, javascript and c code.

除了从qml访问c 功能的能力之外,qt qml模块还提供了从c 代码执行反向和操作qml对象的方法。

下面会通过示例来讲解qml与c 的交互是如何实现的(内容有点长)。

文档如是说,使用c 代码中定义的功能可以轻松扩展qml。由于qml引擎与qt元对象系统的紧密集成,可以从qml代码访问由qobject派生的类适当公开的任何功能。这使得c 类的属性和方法可以直接从qml访问,通常很少或无需修改。

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

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

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

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

代码示例有四个文件,qtquick empty工程的两个加自定义的cpp类h和cpp文件,因为我把几种常用的方法都写出来了,所以看起来有点乱(完整代码链接见文末)。

#ifndef cppobject_h
#define cppobject_h
#include 
//派生自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 
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"<

为了测试方便,我给每个函数都加了一个打印语句,当调用sendsignal函数时将会emit两个信号,稍后会在qml中调用该函数。

#include 
#include 
#include 
#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("mycppobject",1,0,"cppobject");
    qqmlapplicationengine engine;
    //也可以注册为qml全局对象
    //engine.rootcontext()->setcontextproperty("cppobj",new cppobject(qapp));
    engine.load(q));
    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对象:by 龚建波1992")
    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="gongjianbo"  //修改属性会触发set函数,获取值会触发get函数
                cpp_obj.year=1992
                cpp_obj.sendsignal() //调用q_invokable宏标记的函数
            }else{
                console.log('----qml 点击右键:qml发射信号')
                root.qmlsignala()
                root.qmlsignalb('gongjianbo',1992)
            }
        }
    }
    //作为一个qml对象
    cppobject{
        id:cpp_obj
        //也可以像原生qml对象一样操作,增加属性之类的
        property int counts: 0
        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定义的类型一样进行操作,如信号槽关联、属性绑定等。

这个示例很简单,点击鼠标左键调用cppobj的sendsignal函数来发送信号,qml处理;点击鼠标右键qml发送信号,cppobj处理,下面是操作结果:

可以看到qml成功的访问了cppobj的属性和方法,并能进行信号槽的关联。

文档如是说,所有qml对象类型都是源自qobject类型,无论它们是由引擎内部实现还是第三方定义。这意味着qml引擎可以使用qt元对象系统动态实例化任何qml对象类型并检查创建的对象。

这对于从c 代码创建qml对象非常有用,无论是显示可以直观呈现的qml对象,还是将非可视qml对象数据集成到c 应用程序中。一旦创建了qml对象,就可以从c 中检查它,以便读取和写入属性,调用方法和接收信号通知。

可以使用qqmlcomponentqquickview来加载qml文档。qqmlcomponent将qml文档作为为一个c 对象加载,然后可以从c 代码进行修改。qquickview也可以这样做,但由于qquickview是一个基于qwindow的派生类,加载的对象也将可视化显示,qquickview通常用于将一个可视化的qml对象集成到应用程序的用户界面中。参见文档qt/qt5.9.7/docs/qt-5.9.7/qtqml/qtqml-cppintegration-interactqmlfromcpp.html

下面通过代码来演示(完整代码链接见文末)。

import qtquick 2.9
item{
    id: root
    width: 250
    height: 250
    //自定义属性  --cpp可以访问
    property string msg: "gongjianbo1992"
    //自定义信号  --可以触发cpp槽函数
    signal qmlsendmsg(string arg1,string arg2)
    rectangle {
        anchors.fill: parent
        color: "green"
        objectname: "rect" //用于cpp查找对象
    }
    mousearea {
        anchors.fill: parent
        onclicked: {
            console.log("qml 点击鼠标, 发送信号 qmlsendmsg")
            root.qmlsendmsg(root.msg,"myarg2")
        }
    }
    onheightchanged: console.log("qml height changed")
    onwidthchanged: console.log("qml width changed")
    //qml中的方法可以被cpp调用,也可以作为槽函数
    function qml_method(val_arg){
        console.log("qml method runing",val_arg,"return ok")
        return "ok"
    }
    //注意槽函数参数为var类型
    function qmlrecvmsg(arg1,arg2){
        console.log("qml slot runing",arg1,arg2)
    }
}

在qml中我定义了一些属性和方法等,用于测试。

#ifndef cppobject_h
#define cppobject_h
#include 
#include 
class cppobject : public qobject
{
    q_object
public:
    explicit cppobject(qobject *parent = q_nullptr)
        :qobject(parent){}
signals:
    //信号 --用来触发qml的函数
    //注意参数为var类型,对应qml中js函数的参数类型
    void cppsendmsg(const qvariant &arg1,const qvariant &arg2);
public slots:
    //槽函数 --用来接收qml的信号
    void cpprecvmsg(const qstring &arg1,const qstring &arg2){
        qdebug()<<"cppobject::cpprecvmsg"<

cpp中定义了一个槽函数,用来接收qml对象的信号。 

#include 
#include 
#include 
#include 
#include 
#include 
#include "cppobject.h"
int main(int argc, char *argv[])
{
    qcoreapplication::setattribute(qt::aa_enablehighdpiscaling);
    qguiapplication app(argc, argv);
    //可以用qqmlcomponent\qquickview\qquickwidget的c  代码加载qml文档
    //qquickview不能用window做根元素
    qquickview view(q);
    view.show();
    //获取到qml根对象的指针
    qobject *qmlobj=view.rootobject();
    /*文档如是说:
    应该始终使用qobject::setproperty()、qqmlproperty
    或qmetaproperty::write()来改变qml的属性值,以确保qml引擎感知属性的变化。*/
    //【1】
    //通过qobject设置属性值
    qdebug()<<"cpp set qml property height";
    //qmlobj->setproperty("height",300);
    qqmlproperty(qmlobj,"height").write(300);
    //通过qobject获取属性值
    qdebug()<<"cpp get qml property height"<property("height");
    //任何属性都可以通过c  访问
    qdebug()<<"cpp get qml property msg"<property("msg");
    //【2】
    qquickitem *item=qobject_cast(qmlobj);
    //通过qquickitem设置属性值
    qdebug()<<"cpp set qml property width";
    item->setwidth(300);
    //通过qquickitem获取属性值
    qdebug()<<"cpp get qml property width"<width();
    //【3】
    //通过objectname访问加载的qml对象
    //qobject::findchildren()可用于查找具有匹配objectname属性的子项
    qobject *qmlrect=qmlobj->findchild("rect");
    if(qmlrect){
        qdebug()<<"cpp get rect color"<property("color");
    }
    //【4】
    //调用qml方法
    qvariant val_return;  //返回值
    qvariant val_arg="gongjianbo";  //参数值
    //q_return_arg()和q_arg()参数必须制定为qvariant类型
    qmetaobject::invokemethod(qmlobj,
                              "qml_method",
                              q_return_arg(qvariant,val_return),
                              q_arg(qvariant,val_arg));
    qdebug()<<"qmetaobject::invokemethod result"<

然后就把文档中的东西测试了下,操作起来很简单。不过相对于qml中使用c 对象来说,感觉作用没那么大,因为一般把qml嵌入到widgets中才会做这些操作,但是混合两个框架很多坑。下面是我的测试输出结果:

 以上两种方式应该就是最简单的qml与c 交互应用了,对照文档或是博客敲一遍代码可以很容易地理解。

(完结)

代码完整链接(github)如下:

qml中创建cpp对象(cpp注册给qml):https://github.com/gongjianbo/mytestcode/tree/master/qml/qmlcallcpp2020

cpp中加载qml对象(cpp操作qml):https://github.com/gongjianbo/mytestcode/tree/master/qml/cppcallqml2020

代码的csdn下载链接:https://download.csdn.net/download/gongjianbo1992/12552274

 

网站地图