QML数据模型

来源:互联网 发布:淘宝扫码付款被骗 编辑:程序博客网 时间:2024/06/08 17:11


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
     }
 }
其他重要的情形是当有些情况下(如鼠标点击)代理需要更新模型中的数据.这时可以在模型中定义一个函数,如:

         setData(int row, const QString & field_name, QVariant new_value),
...在代理中调用这个函数:

         ListView.view.model.setData(index, field, value)
...假设field保存了需要更新的字段名称,value中保存了新值.

0 0