C++与QML的集成

来源:互联网 发布:金川集团网络学校 编辑:程序博客网 时间:2024/05/20 22:29

在利用Cascades进行开发的时候,我们可以用纯C++,也可以混合使用QMLC++,不过,我认为混合的效率会更高:一方面,QML可以直接预览界面效果;另一方面,混合可以提高并行开发度(界面设计和逻辑代码可以由不同人员同时开发)。

而在混合使用时,遇到的几个问题无非就是:

怎样把QML文件加载到C++中,把它的内容显示出来。

C++QML之间值的传递是怎样实现的。

针对这两点,同时加上对官网给出的教程,我也和大家分享一下我的理解。




C++中加载QML


我们先新建一个名为test的标准空白项目(默认的功能是在屏幕上显示”helloworld”):

File->New->Blackberry Project->Cascades->Next->Standard empty project(空白项目)->输入项目名称test->Finish

项目建立后,我们查看Tset.cpp(在最新的NDK会变成applicationui.cpp

    QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(this);    // create root object for the UI    AbstractPane *root = qml->createRootObject<AbstractPane>();    // set created root object as a scene    app->setScene(root);

从这里可以看出,加载QML文件有三个步骤

1、创建一个QmlDocument对象,指定你的QML文件路径(注意路径格式)。

2、使用对象的createRootObject()方法创建QML文件内容的根节点。

3、通过setScene()方法把该根节点显示出来。

通过这三个步骤,就能把QML的内容完全显示到屏幕上了。




修改QML的属性


通过setProperty()方法能够在C++中修改QML的一些属性,但是有三个前提:

1、使用这个方法的对象必须属于QObject

2、QML中组件必须有一个id

3、在QML中为属性设置一个别名

下面这个例子,通过setProperty()idlabelLabel组件的text属性改为”new text”

Page {

//idlabel的组件的ltext属性设置一个别名labelText

  property alias labelText: label.text  

Container {                    Label {            id: label            text: "Label"        }                                   }}

  //在C++中使用setProperty()方法  //把id为label的组件的text改为”New text”  root->setProperty("labelText", "New text");



访问子对象


QML 文件是树形结构的,所以我们可以利用QObject::findChild()来寻找它的子对象。前提是QML组件有一个objectName

下面这个例子,通过findChild()找到objectName”button”的按钮,并用setProperty()来改变按钮的文字。

QObject *newButton = root->findChild<QObject*>("button");newButton->setProperty("text", "New button text");





暴露C++的值给QML


使用QDeclarativePropertyMap这个类能够很方便地将对象的值传递到QML当中。

下面这个例子说明了怎样把C++中的名字和号码传递到QML当中:

QmlDocument *qml = QmlDocument::create("asset:///main.qml");  QDeclarativePropertyMap* propertyMap = new QDeclarativePropertyMap;propertyMap->insert("name", QVariant(QString("Wes Barichak")));propertyMap->insert("phone", QVariant(QString("519-555-0199")));  qml->setContextProperty("propertyMap", propertyMap);

定义好后,我们就可以直接使用了。

Label {    text: "User name: " + propertyMap.name}Label {    text: "Phone number: " + propertyMap.phone}





暴露C++的对象给QML


    为了能够把C++的对象传递给QML,我们需要先在头文件中调用Qt的几个重要的宏。

  

  Q_OBJECT 使用信号槽机制所必须的宏。

  Q_PROPERTY 暴露类属性给QML,它可以定义从属性中读取的和写进属性中的值,                    同时还可以定义当属性值改变是所触发的信号。

  Q_INVOKABLE 暴露成员方法,这样能在QML中调用它们。

  

下面是一个头文件,这个类有一个属性value,它有三个方法,分别为:value(),setValue()reset()通过在reset()方法前加上Q_INVOKABLE,使它能在QML中调用。同时,头文件也定义了当值改变时所触发的信号valueChanged

#ifndef MYCPPCLASS_H_  #define MYCPPCLASS_H_      #include <QObject>  #include <QMetaType>      class MyCppClass : public QObject {      Q_OBJECT      Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)      public:      MyCppClass();      virtual ~MyCppClass();          Q_INVOKABLE void reset();          int value();      void setValue(int i);      signals:      void valueChanged(int);      private:      int m_iValue;  };      #endif



接着,在源文件中实现头文件中的定义

  #include "MyCppClass.h"      MyCppClass::MyCppClass()  {      m_iValue = 0;  }      MyCppClass::~MyCppClass()  {  }      int MyCppClass::value()  {      return m_iValue;  }      void MyCppClass::setValue(int i)  {      m_iValue = i;      emit valueChanged(m_iValue);  }      void MyCppClass::reset()  {      m_iValue = 0;      emit valueChanged(m_iValue);  }

  为了把MyCppClass暴露给QML,我们先创建一个实例,再用setContextProperty()暴露出来。

  MyCppClass *cppObject = new MyCppClass();  qml->setContextProperty("cppObject", cppObject);

  

        这时候,已经能在QML使用对象cppObject了。由于我们在头文件里使用了那3个宏,所以现在我们能够使用cppObject的属性value和方法reset()

  Label {    text: "Value of cppObject: " +  cppObject.value  }



Q_PROPERTY中,我们还定义了一个信号valueChanged,所以我们可以在QML中写一个槽,连接到这个信号,通过这个办法来改变value的值。

下面这个例子,按钮每按一次,value的值加1。这个按钮自定义了一个槽来连接valueChanged()这个信号,当槽被调用时,改变按钮上的文字。而reset按钮这调用方法reset()来重置value的值。

  Button {      id: increaseButton      text: "Increase the value"      onClicked: {          cppObject.valueChanged.connect                   (increaseButton.onCppValueChanged);          cppObject.value = cppObject.value + 1;      }      function onCppValueChanged (val) {          increaseButton.text = "Set value to " + (val + 1);      }  }  Button {      id: resetButton      text: "Reset the value"      onClicked: {          cppObject.reset ()      }  }  Label {      id: valueLabel      text: "The value is " + cppObject.value  }




QML中使用C++的类


我们也可以直接在QML中使用C++中已经存在的类。实现的方法有两个:

1使用UIObject::attachedObjects把类(必须继承自QObject)附加到已经存在的QML组件当中。


例如,在QML中使用QTimer

  首先,我们要在C++中对这个类进行注册:

qmlRegisterType<QTimer>("my.library", 1, 0, "QTimer");

  

        需要注意的是这个语句必须在加载QML文件的语句之前,也就是说你必须先注册,再加载。如下:

  

qmlRegisterType<QTimer>("my.library", 1, 0, "QTimer");QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(&app);

  注册后,我们就能在QML中创建QTimer对象。下面这个例子中,我们把QTimer对象附加到一个Label上。当页面被创建时,计时器就会被触发。计时完成后,Label的文字便会被替换。

  import bb.cascades 1.0  import my.library 1.0      Page {      Label {          id: timerTestLabel          text: "Hello world"                          attachedObjects: [              QTimer {                  id: timer                  interval: 1000                  onTimeout :{                      timerTestLabel.text = "Timer triggered"                        }              }          ]      }                  onCreationCompleted: {          timer.start();      }  }

  

2使用CustomControl来拓展类,然后在QML中直接使用。


  使用CustomControl,我们能够直接在QML中使用C++的类,不必把它附加到任何QML的组件当中。例子如下:我们新建一个继承CustomControl的类,叫作TextControl,它有text属性,setText()方法,还有一个当文字改变时被触发的信号。

  

#include <QObject>  #include <QString>  #include <bb/cascades/CustomControl>      class TextControl : public bb::cascades::CustomControl  {      Q_OBJECT      Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)      public:      QString text();      void setText(QString text);      signals:      void textChanged();  };


  然后,我们在C++中注册,这样才能在QML中使用。

qmlRegisterType<TextControl>("my.library", 1, 0, "TextControl");


此语句同样要放在加载QML文件的语句之前。

注册后,我们同样需要在QML中引入,接着就像其它组件一样使用了。

  import bb.cascades 1.0  import my.library 1.0      Page {      TextControl {          text: "Custom text control"      }  }

  




QML中注入C++对象


我们可以在C++中创建对象然后把它动态地注入到QML当中。

    首先我们为QML的容器定义一个objectName,例如:

Page {        Container {            objectName: "rootContainer"        }    }

接着,我们把QML文件加载到C++中,并使用QML文件中的根创建了一个Page对象。再用findChild()来寻找对objectName为”rootContainer”的Container

    

QmlDocument *qml = QmlDocument::create("main.qml");     // Sets the context property that we want to use from within QML.    // Functions exposed via Q_INVOKABLE will be found with this    // property name and the name of the function.     qml->setContextProperty("injection", this);     // Creates the root using the page node     Page *appPage = qml->createRootNode<Page>();     // Retrieves the root container from the page     Container *container = appPage->findChild<Container*>("rootContainer");

    现在,我们就能够在rootContainer中加入我们需要动态增加的内容了。

    container->add(Container::create()        .background(image)      .preferredSize(200,200));

    通过这个方法,我们也能够在C++中利用objectName标签来动态地删除、替换、或者插入QML组件。


  好了,关于C++与QML的对象和值的传递我暂时也只了解这么多,欢迎大家一起讨论。

  文章参考地址:黑莓官网教程