QML與C++集成<二>——<使用C++屬性及注冊QML類型>


前言

  在開始講之前首先講一個使用屬性(setContextProperty)注冊類型(qmlRegisterType)的區別,在這主要講一些我個人工作中的情況,其實二者都是將c++類暴露給QML的方法,只不過在使用時存在一些區別,根據使用方式不同我個人分為C++的形式和QML的定義形式。

1、C++定義方式(主要使用setContextProperty()函數)

  • a)、比如我們有一個功能單一的Configure類,我們需要把它暴露給QML,在使用之前必須要先創建類對象m_configuration,就是說類實例化一次,QML中可以直接使用這個類,注意功能單一的類只適合該方式;
  • b)、比如我們的業務比較復雜,我們有很多類,若要供QML調用,我們就要寫一個總的被調用類Complex(包含所有的業務類),然后實例化一次這個Complex,然后QML中直接使用實例化后的對象;

兩種業務方式的使用方式如下:

QQuickView viewer; viewer.rootContext()->setContextProperty("_configuration",&m_configuration);
 
        
_configuration便可直接在qml中使用,_configuration自然也是一個全局變量。

2、QML的方式,(主要使用qmlRegisterType()函數)

  該方式都是使用在業務復雜情況下,還是上面的例子,我們有一堆業務類,這個時候我們使用注冊的方式,用在QML中定義的方式去定義各個實例,也就不用再需要一個總類:

qmlRegisterType<Foo>("App", 1, 0, "Foo");
qmlRegisterType<Bar>("Bar", 1, 0, "Bar");//注冊不可實例化的類型

我們可以再QML中直接使用Foo去定義實例:

import App 1.0 Foo {
      bar.baz: "abc"
      Component.onCompleted: print(bar.baz)
  }

3、二者比較

  與C++方式相比,QML具有如下優勢:

  • 變量名前面可以加$(全局變量可用),從而方便區分全局變量和局部變量,這個在C++定義屬性的時候是不允許的;

  • 如果某個全局變量(一般是QML對象)構造很慢,可以通過QML中的Loader來很方便異步構造,從而加速程序啟動:

一、在QML中使用C++屬性

  QObject子類的所有屬性都能夠被QML訪問,QObject子類使用Q_PROPERTY宏定義一個屬性,該宏的作用是向Qt元對象系統注冊類的屬性,一個類的屬性就是類的數據成員,通常會有一個用於讀取的READ函數和一個可選的用於修改的WRITE函數。

該宏定義如下:

 一些常見的申明示例:

使用我們一般使用setContextProperty()函數,注意和注冊的區別,使用一版如下,m_configuration就是一個全局變量,便可在qml中直接使用

QQuickView viewer;
viewer.rootContext()->setContextProperty("_configuration",&m_configuration);

 1.1 使用函數和槽

  QML可以有條件地訪問QObject子類的函數:

  • 使用Q_INVOKABLE宏標記的public函數
Q_INVOKABLE bool postMessage(const QString &msg){
qDebug()<<"postMessage";
}
  • public槽函數
public slot:
    void refresh()
    {
        qDebug()<"refresh";  
    }

1.2 使用信號

  QML代碼可以使用QObject子類的任意public信號,QML引擎會為每一個來自QObject子類的信號自動創建一個信號處理器,其命名規則如為:on<Signal>,其中<Signal>為信號的名字,首字母要大寫,信號傳遞的參數通過其名字在信號處理器中使用。

注意:QML中的信號與前面提到的函數重載不同,如果C++類中具有參數列表不同的多個同名信號,但只有最后一個信號才能被QML訪問到。

二、注冊QML類型

   QObject子類可以注冊到QML類型系統中,以便在QML程序中作為一個數據類型使用,前面提到的所有操作數據的基礎就是先注冊Qml類型。可被注冊的類分為可實例化和不可實例化兩種,注冊可實例化的類意味着這個類定義為一個QML對象類型,QML對象類型通過這種注冊能夠獲取這種類型的元數據,以及相關屬性信號等操作,注冊不可實例化的C++類,意味着這種類型不可實例化,比如我們要把枚舉類型暴露給QML,但這個類型本身不需要被實例化。

2.1 注冊可實例化對象類型

  QObject子類都可以注冊為QML對象類型,注冊成功后這個類就可以在QML代碼中像其他類型一樣聲明和初始化,創建成功后就可以在QML中使用其屬性值、函數和信號等特征,注冊QObject子類,需要使用qmlRegisterType()函數,下面有兩個類Bar,Foo:

class Bar : public QObject
  {
      Q_OBJECT
      Q_PROPERTY(QString baz READ baz WRITE setBaz NOTIFY bazChanged)

  public:
      Bar() {}

      QString baz() const { return mBaz; }

      void setBaz(const QString &baz)
      {
          if (baz == mBaz)
              return;

          mBaz = baz;
          emit bazChanged();
      }

  signals:
      void bazChanged();

  private:
      QString mBaz;
  };

  class Foo : public QObject
  {
      Q_OBJECT
      Q_PROPERTY(Bar *bar READ bar CONSTANT FINAL)

  public:
      Foo() {}

      Bar *bar() { return &mBar; }

  private:
      Bar mBar;
  };

我們使用qmlRegisterType將其注冊到QML系統類型,該函數需要一個命名空間和版本號:

qmlRegisterType<Foo>("App", 1, 0, "Foo");
qmlRegisterType<Bar>();//注冊不可實例化的類型

在QML中使用:

import App 1.0

Foo {
      bar.baz: "abc"
      Component.onCompleted: print(bar.baz)
  }

2.2 注冊不可實例化對象類型

  有時需要注冊一個不可實例化的對象類型,比如一個C++類:

  • 是接口類型;
  • 一個基類,不需要通過QML代碼訪問;
  • 僅僅提供一些有用的枚舉;
  • 是一個單例,只能使用其唯一的實例,不應該從QML進行實例化。

  注冊方法:

  • 使用無參的qmlRegisterType()函數;
  • 使用qmlRegisterInterface()注冊指定QML類型名稱的Qt接口類型;
  • 使用qmlRegisterUncreatableType()函數
  • 使用qmlRegisterSingletonType()函數 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM