天天看點

QML資料模型



QML資料模型(Model)QML中的ListView,GridView和Repeater等元素需要資料模型來提供要顯示的資料.這些元素需要一個為模型中的每一項資料生成一個執行個體的代理元件(delegate component).模型可以是靜态的,也可對其動态修改,插入,删除,移動.

給代理提供的資料通過叫做角色的資料綁定到代理.下面的ListModel有兩個角色,type和age,ListView帶有一個代理,并綁定這些角色以顯示他們的值:

import QtQuick 1.0
 Item {
      width: 200; height: 250     ListModel {
          id: myModel
          ListElement { type: "Dog"; age: 8 }
          ListElement { type: "Cat"; age: 5 }
      }     Component {
          id: myDelegate
          Text { text: type + ", " + age }
      }     ListView {
          anchors.fill: parent
          model: myModel
          delegate: myDelegate
      }
  }      

如果模型屬性和代理的屬性有名稱沖突,可以在角色名稱前加上模型名稱.例如,如果Text元素也有type或age屬性,上面的text元素會顯示這些屬性值,而不是模型中的項目.這時,可以使用model.age和model.type,確定代理顯示模型中的資料項.

模型中的項目索引角色也可用在代理中.資料項從模型中删除後這個索引值被設定為-1.如果綁定這個索引角色,需要注意這個邏輯上可能的索引值-1,表示這個資料項還不可用.(通常這個資料項很快就會被删除,也可能通過在某些視圖設定delayRemove擴充屬性,由延時代理銷毀.)

對于沒有角色名稱的資料模型(如下面的QStringList)可以使用modelData角色來引用資料.隻有一個角色的模型也可使用modelData角色.這時modelData角色包含的資料與命名的角色相同.

QML提供了幾個内建的資料模型元素.此外,可在C++中建立模型,用于QML元素.

視圖引用模型中的資料并顯示.QML使用Positioner和Repeater項來定位排列模型中的資料項.

QML資料模型ListModel

ListModel是QML中簡單的層次元素.其中的角色由ListElement屬性指定.

ListModel {
      id: fruitModel     ListElement {
          name: "Apple"
          cost: 2.45
      }
      ListElement {
          name: "Orange"
          cost: 3.25
      }
      ListElement {
          name: "Banana"
          cost: 1.95
      }
  }      

上面的模型有兩個角色--name和cost.他們可以綁定到ListView的代理,如:

ListView {
      anchors.fill: parent
      model: fruitModel
      delegate: Row {
          Text { text: "Fruit: " + name }
          Text { text: "Cost: $" + cost }
      }
  }      

ListModel提供了可直接在​​​JavaScript​​中調用的方法.這時,第一次插入的項決定使用模型的視圖可用的角色.例如,如果建立了一個空ListModel,并在JavaScript中填充,第一次插入時指定的角色将顯示在視圖中:

ListModel { id: fruitModel }
      ...
  MouseArea {
      anchors.fill: parent
      onClicked: fruitModel.append({"cost": 5.95, "name":"Pizza"})
  }      

當點選MouseArea時,fruitModel中會産生兩個角色,cost和name.後面增加其他角色,使用模型的視圖也隻能使用這兩個.調用ListModel::clear()重置模型中的角色.

XmlListModel

XmlListModel用來從XML資料源構造模型.角色由XmlRole元素指定.

下面的模型有三個角色:title,link和description:

XmlListModel {
       id: feedModel
       source: "​​​http://rss.news.yahoo.com/rss/oceania​​​"
       query: "/rss/channel/item"
       XmlRole { name: "title"; query: "title/string()" }
       XmlRole { name: "link"; query: "link/string()" }
       XmlRole { name: "description"; query: "description/string()" }
  }      

RSS範例展示了如何使用XmlListModel顯示RSS源.

VisualItemModel

VisualItemModel允許QML元素作為模型.

這個模型中包含資料和代理;VisualItemModel的子項提供了代理的内容.這個模型不提供任何角色.

VisualItemModel {
      id: itemModel
      Rectangle { height: 30; width: 80; color: "red" }
      Rectangle { height: 30; width: 80; color: "green" }
      Rectangle { height: 30; width: 80; color: "blue" }
  } ListView {
      anchors.fill: parent
      model: itemModel
  }      

注意上例中不需要代理.模型中的資料項提供了可視化元素,将顯示到視圖中..

C++資料模型

C++中定義的模型可用于QML.可向QML元素暴露C++中已存的資料模型或複雜的資料集.

C++中可使用QStringList, QList<QObject*> 或QAbstractItemModel類定義模型.前面兩個可用于暴露簡單的資料集,QAbstractItemModel則可定義更複雜的模型.

基于QStringList的模型

可定義簡單的QStringList模型,使用modalData角色通路其中的内容.

下面的ListView中有一個代理使用modelData角色使用模型中的資料項:

ListView {
      width: 100; height: 100
      anchors.fill: parent     model: myModel
      delegate: Rectangle {
          height: 25
          width: 100
          Text { text: modelData }
      }
  }      

Qt應用程式可以加載這個QML文檔并設定一個叫做myModel的QStringList類型的值:

QStringList dataList;
      dataList.append("Item 1");
      dataList.append("Item 2");
      dataList.append("Item 3");
      dataList.append("Item 4");     QDeclarativeView view;
      QDeclarativeContext *ctxt = view.rootContext();
      ctxt->setContextProperty("myModel", QVariant::fromValue(dataList));      

完整範例見Qt的範例:examples/declarative/modelviews/stringlistmodel目錄.

注意:視圖無法知道QStringList中的内容變化.如果QStringList發生變化,需要再次調用QDeclarativeContext::setContextProperty()重置模型.

基于QObjectList的模型

QObject*清單也可作為模型.QList<QObject*>提供了一個對象清單的屬性.

下面的應用程式建立了一個DataObject對象,使用Q_PROPERTY聲明的命名屬性,當QList<DataObject>暴露給QML時,可按屬性名稱作為角色來通路:

class DataObject : public QObject
  {
      Q_OBJECT     Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
      Q_PROPERTY(QString color READ color WRITE setColor NOTIFY colorChanged)
      ...
  }; int main(int argc, char ** argv)
  {
      QApplication app(argc, argv);     QList<QObject*> dataList;
      dataList.append(new DataObject("Item 1", "red"));
      dataList.append(new DataObject("Item 2", "green"));
      dataList.append(new DataObject("Item 3", "blue"));
      dataList.append(new DataObject("Item 4", "yellow"));     QDeclarativeView view;
      QDeclarativeContext *ctxt = view.rootContext();
      ctxt->setContextProperty("myModel", QVariant::fromValue(dataList));      

     ...

QObject*可作為modelData的屬性來通路.為友善,對象的屬性也可直接用于代理上下文.這裡,view.qml在ListView代理中引用了資料模型屬性:

ListView {
      width: 100; height: 100
      anchors.fill: parent     model: myModel
      delegate: Rectangle {
          height: 25
          width: 100
          color: model.modelData.color
          Text { text: name }
      }
  }      

注意使用color屬性時使用了全名稱辨別.QML對象的屬性不能與模型對象屬性同名,可使用modelData對象來通路資料項.

完整範例見examples/declarative/modelviews/objectlistmodel.

注意:無法知道QList中的内容發生了變化.如果QList變化了,有必要調用QDeclarativeContext::setContextProperty()重置模型.

QAbstractItemModel

可使用QAbstractItemModel的子類定義模型.如果有一個複雜的模型無法用其他方式實作,就可以使用這種方式.QAbstractItemModel在發生變化時會自動通知QML視圖.

QAbstractItemModel子類向QML暴露的角色可使用QAbstractItemModel::setRoleNames()設定.預設的角色名稱由Qt設定:

Qt Role QML角色名稱 

Qt::DisplayRole 顯示 

Qt::DecorationRole 修飾

下面的應用程式有一個QAbstractItemModel子類模型叫做AnialModel,具有type和size角色.為在QML中通路這些屬性調用了QAbstractItemModel::setRoleNames():

class Animal
  {
  public:
      Animal(const QString &type, const QString &size);
      ...
  }; class AnimalModel : public QAbstractListModel
  {
      Q_OBJECT
  public:
      enum AnimalRoles {
          TypeRole = Qt::UserRole + 1,
          SizeRole
      };     AnimalModel(QObject *parent = 0);
      ...
  }; AnimalModel::AnimalModel(QObject *parent)
      : QAbstractListModel(parent)
  {
      QHash<int, QByteArray> roles;
      roles[TypeRole] = "type";
      roles[SizeRole] = "size";
      setRoleNames(roles);
  } int main(int argc, char ** argv)
  {
      QApplication app(argc, argv);     AnimalModel model;
      model.addAnimal(Animal("Wolf", "Medium"));
      model.addAnimal(Animal("Polar bear", "Large"));
      model.addAnimal(Animal("Quoll", "Small"));     QDeclarativeView view;
      QDeclarativeContext *ctxt = view.rootContext();
      ctxt->setContextProperty("myModel", &model);
      ...
 這個模型顯示在ListView中,代理可通路type和size角色: ListView {
      width: 200; height: 250
      anchors.fill: parent     model: myModel
      delegate: Text { text: "Animal: " + type + ", " + size }
  }      

模型更改後QML視圖自動更新.記住模型更改必須遵守标準規則,當模型修改後必須調用QAbstractItemModel::dataChanged(),QAbstractItemModel::beginInsertRows()等等,通知視圖模型被修改了.更多資訊見模型子類的參考.

完整範例見examples/declarative/modelviews/abstractitemmodel 目錄.

QAbstractItemModel表現為一個層次表,但現在QML隻提供了可顯示清單資料的視圖.為了顯示模型層次的子清單,VisualDataModel元素提供了幾個屬性和方法與QAbstractItemModel類型的模型共用:

hasModelChildren 确定節點是否有子節點. 

VisualDataModel::rootIndex 指定跟節點 

VisualDataModel::modelIndex() 傳回可給VisualDataModel::rootIndex指派的QModelIndex類型值

VisualDataModel::parentModelIndex() 傳回可給VisualDataModel::rootIndex指派的QModelIndex類型值

向QML暴露C++資料模型

上例中使用QDeclarativeContext::setContextProperty()設定可用于QML元件的模型.另一種方式是在C++插件中注冊一個C++模型類作為QML類型.這樣可以直接在QML中聲明模型元素:

class MyModelPlugin : public QDeclarativeExtensionPlugin
  {
  public:
      void registerTypes(const char *uri)
      {
          qmlRegisterType<MyModel>(uri, 1, 0,
                  "MyModel");
      }
  } Q_EXPORT_PLUGIN2(mymodelplugin, MyModelPlugin);
   MyModel {
      id: myModel
      ListElement { someProperty: "some value" }
  }
  ListView {
      width: 200; height: 250
      model: myModel
      delegate: Text { text: someProperty }
  }      

寫C++插件的更多資訊見Writing QML extensions with C++.

其他資料模型--整形

一個整數可訓示模型包含的元素個數.這時,模型沒有任何資料角色.

下面範例中建立一個有五個元素的ListView:

Item {
      width: 200; height: 250     Component {
          id: itemDelegate
          Text { text: "I am item number: " + index }
      }     ListView {
          anchors.fill: parent
          model: 5
          delegate: itemDelegate
      } }      

對象執行個體

一個對象執行個體可指定為模型.對象屬性可作為角色.

下例建立一個有一個項的清單,展示Text.text的顔色.注意這裡使用了全命名model.color避免命名沖突.

Rectangle {
      width: 200; height: 250     Text {
          id: myText
          text: "Hello"
          color: "#dd44ee"
      }     Component {
          id: myDelegate
          Text { text: model.color }
      }     ListView {
          anchors.fill: parent
          anchors.topMargin: 30
          model: myText
          delegate: myDelegate
      }
  }      

在代理中通路視圖和模型

可在代理中通路其所在的視圖,在ListView的代理中使用ListView.view屬性,或GridView中使用GridVie.view等.而且可使用ListView.view.model來通路模型的屬性.

這對于在多個視圖時使用同一個代理很有用,例如,想為每個視圖建立不同的修飾部分或其他特性,需要針對每個視圖設定不同的屬性.同樣,可能需要通路或顯示模型的屬性.

下面例子中,代理顯示模型的language屬性,一個字段的顔色依賴于fruit_color屬性.

Rectangle {
       width: 200; height: 200     ListModel {
          id: fruitModel
          property string language: "en"
          ListElement {
              name: "Apple"
              cost: 2.45
          }
          ListElement {
              name: "Orange"
              cost: 3.25
          }
          ListElement {
              name: "Banana"
              cost: 1.95
          }
      }     Component {
          id: fruitDelegate
          Row {
                  Text { text: " Fruit: " + name; color: ListView.view.fruit_color }
                  Text { text: " Cost: $" + cost }
                  Text { text: " Language: " + ListView.view.model.language }
          }
      }     ListView {
          property color fruit_color: "green"
          model: fruitModel
          delegate: fruitDelegate
          anchors.fill: parent
      }
  }      

繼續閱讀