一般人不知道怎么去过滤ListView里面的数据,下面是一个转载的文章:http://imaginativethinking.ca/use-qt-quicks-delegatemodelgroup/

How to use Qt Quick’s DelegateModelGroup

By: Brad van der Laan, C.Tech.

Jul

07

2014In this tutorial I’m going to show you how to use Qt Quick’s DelegateModelGroup to re-organize a list of data in QML.

In QtQuick you have a View and a Data Model where the Data Model holds the data that the View will present to the user. Your Data Model may hold data that needs to be presented in a number of different ways. The different presentation views may only need to show a sub set of that data; In order to achieve this we need to present each View with a sub-set of the contents of the Data Model. We can do this via a Proxy Model such as the DelegateModel with the use of a DelegateModelGroup.

In QML there is a component called a DelegateModel which is essentially a proxy model allowing you to sort or filter the contents of the Data Model before passing it along to the View. The DelegateModel has a property called rootIndex which you can use in order to pick which branch of the Data Model (assuming its a Tree) to use in the View. There is also a component called a DelegateModelGroup which can be used by a DelegateModel to apply filtering and/or sorting to the data being retrieved from the Data Model.

Lets get started with some code shall we.

First create a new Qt Quick UI project (*.qmlproject), remember this is a type of project that has only QML (no C++). Call your new project DelegateModel_Sample1.

In the DelegateModel_Sample1.qml file that gets auto generated per the Qt Quick UI project template add the following ListModel to act as our Data Model.

view plaincopy to clipboardprint?

ListModel {

id: myModel

ListElement { role_display: "One"; role_value: 0; }

ListElement { role_display: "One"; role_value: 2; }

ListElement { role_display: "One"; role_value: 3; }

ListElement { role_display: "One"; role_value: 4; }

ListElement { role_details: "Two"; role_value: 5; }

ListElement { role_details: "Three"; role_value: 6; }

ListElement { role_details: "Four"; role_value: 7; }

ListElement { role_details: "Five"; role_value: 8; }

ListElement { role_details: "Six"; role_value: 9; }

ListElement { role_keyID: "Seven"; role_value: 10; }

ListElement { role_keyID: "Eight"; role_value: 11; }

ListElement { role_keyID: "hello"; role_value: 12; }

}

ListModel {

id: myModel

ListElement { role_display: "One"; role_value: 0; }

ListElement { role_display: "One"; role_value: 2; }

ListElement { role_display: "One"; role_value: 3; }

ListElement { role_display: "One"; role_value: 4; }

ListElement { role_details: "Two"; role_value: 5; }

ListElement { role_details: "Three"; role_value: 6; }

ListElement { role_details: "Four"; role_value: 7; }

ListElement { role_details: "Five"; role_value: 8; }

ListElement { role_details: "Six"; role_value: 9; }

ListElement { role_keyID: "Seven"; role_value: 10; }

ListElement { role_keyID: "Eight"; role_value: 11; }

ListElement { role_keyID: "hello"; role_value: 12; }

}

Notice that I’ve named the roles role_ I use this convention so as to make it clear in the QML that I’m referring to a role identifier vs. an id of another component. In practice I’ve run into issues where a component in QML was given the same id as a defined role. This causes conflicts and confusion as its not clear what was being referred to, a QML Component or data from a model. This is typically more of an issue when your model is in C++ as the role identifier isn’t defined in the QML script. To solve this I personally adopted a naming convention of prefixing role identifiers with role_ to make them unique and easily identified in the QML script.

Now we have a Data Model that has twelve (12) entries. Lets say we want to present only the entries that have the role_display role data. To do this we need to filter out all the other entries so that we’re left with only the four (4) entries who have the appropriate data. To do this we’re going to use a DelegateModel to act as our Proxy Model.

DelegateModel{

id: displayDelegateModel

delegate: displayDelegate

model: myModel

}

DelegateModel{

id: displayDelegateModel

delegate: displayDelegate

model: myModel

}

Note: In order to use the DelegateModel you need to import the QtQml.Models library by adding the following statement to your QML script.

view plaincopy to clipboardprint?

import QtQml.Models 2.1

import QtQml.Models 2.1

Here we told the displayDelegateModel that its root model (or Data Model) is the myModel we created above and we’ve also told it how to represent the data on screen via its delegate property (we’ll defined displayDelegate later on as a Component).

The DelegateModel component wraps up a model and a delegate to be used by a view. By simply telling a view to use this component it will know to use the delegate defined in the DelegateModel vs. having to set its delegate property. The DelegateModel replaces the older Qt Quick element VisualDataModel.

If we plug this into a View as is we’ll get all 12 entries as we’ve not actually performed any sort of filtering on the data in the model.

Enters the DelegateModelGroup

In order to actually filter the data before it gets to the View, so that it only presents a sub set of the data, we need to add a DelegateModelGroup to the DelegateModel component.

view plaincopy to clipboardprint?

DelegateModel {

id: displayDelegateModel

delegate: displayDelegate

model: myModel

groups: [

DelegateModelGroup {

includeByDefault: false

name: "displayField"

}

]

filterOnGroup: "displayField"

}

DelegateModel {

id: displayDelegateModel

delegate: displayDelegate

model: myModel

groups: [

DelegateModelGroup {

includeByDefault: false

name: "displayField"

}

]

filterOnGroup: "displayField"

}

Here we add a DelegateModelGroup and give the group a distinctive name displayField. By setting the includeByDefault property to false we are telling the DelegateModelGroup not to automatically include all the entries in the myModel Data Model into the displayField group. If we left it at this point the displayField group would be empty as all 12 entries from the myModel would be in the un-grouped section.

Now that we have a group that we can use we then tell the DelegateModel (displayDelegateModel) to filter its data on the displayField group via the filterOnGroup property. That is we are telling the DelegateModel to only show entries that are part of the displayField group and filter out all entries which are not a member of this group. If we were to plug the displayDelegateModel into a view now we would get zero (0) entries shown in the view. That is because at this point no entries in the myModel Data Model are members of the displayField group since we told the DelegateModelGroup not to auto include the entries.

What we need to do now is start adding entries to the displayField group. I suppose we could do that in a number of different places; we could even do it dynamically via user input so that we could use a single view to show different sub sets of data per the user input. For this example lets just do it once during the Component.onCompleted event handler; that is once the DelegateModel component has finished being initialized.

view plaincopy to clipboardprint?

DelegateModel {

id: displayDelegateModel

delegate: displayDelegate

model: myModel

groups: [

DelegateModelGropu {

includeByDefault: false

name: "displayField"

}

]

filterOnGroup: "displayField"

Component.onCompleted: {

var rowCount = myModel.count;

items.remove(0,rowCount);

for( var i = 0;i < rowCount;i++ ) {

var entry = myModel.get(i);

if(entry.role_display !== undefined) {

items.insert(entry, "displayField");

}

}

}

}

DelegateModel {

id: displayDelegateModel

delegate: displayDelegate

model: myModel

groups: [

DelegateModelGropu {

includeByDefault: false

name: "displayField"

}

]

filterOnGroup: "displayField"

Component.onCompleted: {

var rowCount = myModel.count;

items.remove(0,rowCount);

for( var i = 0;i < rowCount;i++ ) {

var entry = myModel.get(i);

if(entry.role_display !== undefined) {

items.insert(entry, "displayField");

}

}

}

}

Here using properties and functions of the ListModel I am looping through all twelve (12) entries that exist in myModel and for each entry I am checking to see if it contains the role_display Role. If it does I use the items property of the DelegateModel, which is a reference to its default group and since we only have one its the DelegateModelGroup that we setup called displayField, and add the given entry to said group. Now at the completion of the loop the displayField group will contain only the four (4) entries from myModel which have the role_display Role defined.

When I plug this into a View I'll see these four (4) entries displayed on the screen.

Here is the complete code example:

view plaincopy to clipboardprint?

import QtQuick 2.2

import QtQuick.Controls 1.1

import QtQuick.Window 2.0

ApplicationWindow {

title: qsTr("DelegateModel Sample 1")

width: 640

height: 600

menuBar: MenuBar {

Menu {

title: qsTr("File")

MenuItem {

text: qsTr("Exit")

onTriggered: Qt.quit();

}

}

}

Item {
id: root
anchors.fill: parent
readonly property int rowHeight: 25 ListModel {
id: myModel
ListElement { role_display: "One"; role_value: 0; }
ListElement { role_display: "One"; role_value: 2; }
ListElement { role_display: "One"; role_value: 3; }
ListElement { role_display: "One"; role_value: 4; }
ListElement { role_details: "Two"; role_value: 5; }
ListElement { role_details: "Three"; role_value: 6; }
ListElement { role_details: "Four"; role_value: 7; }
ListElement { role_details: "Five"; role_value: 8; }
ListElement { role_details: "Six"; role_value: 9; }
ListElement { role_keyID: "Seven"; role_value: 10; }
ListElement { role_keyID: "Eight"; role_value: 11; }
ListElement { role_keyID: "hello"; role_value: 12; }
}
DelegateModel {
id: displayDelegateModel
delegate: displayDelegate
model: myModel
groups: [
DelegateModelGroup {
includeByDefault: false
name: "displayField"
}
]
filterOnGroup: "displayField"
Component.onCompleted: {
var rowCount = myModel.count;
items.remove(0,rowCount);
for( var i = 0;i < rowCount;i++ ) {
var entry = myModel.get(i);
if(entry.role_display !== undefined) {
items.insert(entry, "displayField");
}
}
}
}
Component{
id: displayDelegate Rectangle {
anchors.left: parent.left
anchors.right: parent.right
height: root.rowHeight
color: "green"
border.color: "black" Text {
text: role_display
anchors.centerIn: parent
}
}
}
ListView {
id: displayListView
anchors.left: parent.left
anchors.right: parent.right
height: root.rowHeight * 4
model: displayDelegateModel
}
}

}

import QtQuick 2.2

import QtQuick.Controls 1.1

import QtQuick.Window 2.0

ApplicationWindow {

title: qsTr("DelegateModel Sample 1")

width: 640

height: 600

menuBar: MenuBar {

Menu {

title: qsTr("File")

MenuItem {

text: qsTr("Exit")

onTriggered: Qt.quit();

}

}

}

Item {
id: root
anchors.fill: parent
readonly property int rowHeight: 25 ListModel {
id: myModel
ListElement { role_display: "One"; role_value: 0; }
ListElement { role_display: "One"; role_value: 2; }
ListElement { role_display: "One"; role_value: 3; }
ListElement { role_display: "One"; role_value: 4; }
ListElement { role_details: "Two"; role_value: 5; }
ListElement { role_details: "Three"; role_value: 6; }
ListElement { role_details: "Four"; role_value: 7; }
ListElement { role_details: "Five"; role_value: 8; }
ListElement { role_details: "Six"; role_value: 9; }
ListElement { role_keyID: "Seven"; role_value: 10; }
ListElement { role_keyID: "Eight"; role_value: 11; }
ListElement { role_keyID: "hello"; role_value: 12; }
}
DelegateModel {
id: displayDelegateModel
delegate: displayDelegate
model: myModel
groups: [
DelegateModelGroup {
includeByDefault: false
name: "displayField"
}
]
filterOnGroup: "displayField"
Component.onCompleted: {
var rowCount = myModel.count;
items.remove(0,rowCount);
for( var i = 0;i < rowCount;i++ ) {
var entry = myModel.get(i);
if(entry.role_display !== undefined) {
items.insert(entry, "displayField");
}
}
}
}
Component{
id: displayDelegate Rectangle {
anchors.left: parent.left
anchors.right: parent.right
height: root.rowHeight
color: "green"
border.color: "black" Text {
text: role_display
anchors.centerIn: parent
}
}
}
ListView {
id: displayListView
anchors.left: parent.left
anchors.right: parent.right
height: root.rowHeight * 4
model: displayDelegateModel
}
}

}

What about C++?

Well this is all fine and dandy however what if I'm using a C++ model instead of the ListModel QML component?

That is a good question; The issue I ran into was that the ListModel and QAbstractItemModel interfaces are quite different, the QAbstractItemModel does not have a get() method like the ListModel does and we're using that in our Component.OnComplete() handler. It took a while to figure out what the ListModel::get() method was returning but with the help of some colleges we found out that its returning a QVariantMap where the key is the QByteArray value which is defined in the QAbstractItemModel::roleNames() method.

So lets add a C++ view model to our sample project and use it instead of the ListModel we've been using to date. Since the above is a Qt Quick UI project we'll need to create a new Qt Quick Application (*.pro) project, copy the QML script DelegateModel_Smaple1.qml over to it and add a new C++ class called MyViewModel. Have the MyViewModel class extend the QStandardItemModel which is a template Qt Model from Qt's Model View Architecture.

This class will contain our data and we can assign different attributes to the data in the collection via Roles ( think of roles as attributes ). This is the same as we did in the ListModel except in C++ we define them as an enum and use them in the setData() and data() methods when entering and retrieving data into/from the collection. In order for the QML scripts to be able to reference these attributes without having to explicitly invoke a method call we create a QHash that maps enum values to strings where the C++ uses the enum values and the QML scripts use the string values; this is done within the virtual method QAbstractItemModel::roleNames.

I'll go over all this in more detail in a future post dedicated to creating C++ models for Qt's Model View Architecture but for now lets gloss over it and get back to this tutorial on the DelegateModelGroup.

So that we don't have to change our QML script to much we need to make the interface of our MyViewModel (which extends QStandardItemModel which is a concrete version of the QAbstractItemModel) match that of the ListModel that we were using to date. To do this we need to add a count property (via the Q_PROPERTY macro) and a get() method (via the Q_INVOKABLE macro).

The count property is pretty straight forward if you've read my previous tutorial Qt Quick 102: Introducing C++ to QML, we simply declare the property via the Q_PROPERTY macro so the QML scripts know about it and the slot that gets invoked when the QML scripts access the property simply ask the model how many items are in its collection.

In the MyViewModel header we declare the count property like so:

view plaincopy to clipboardprint?

Q_PROPERTY( int count READ getRowCount() NOTIFY rowCountChanged() )

Q_PROPERTY( int count READ getRowCount() NOTIFY rowCountChanged() )

count: is the name of the property which matches the property of the ListModel class so that the QML does not have to change.

getRowCount(): is a slot which asks the model its row count ( this->rowCount(); ) which is just the base implementation of QStandardItemModel.

rowCountChanged(): is a signal that MyViewModel emits whenever it adds or removes data form its collection ( this is a signal we have to emit manually the QStandardItemModel doesn't know about it; see the MyViewModel::initialize() method)

The real trick for me was the get() method; not that its a hard method to implement, far from it, but that it was hard to figure out what the architecture was looking for.

As part of the QStandardItemModel (or rather the QAbstractItemModel) there is a virtual method called roleNames() which the glue between the View and the ViewModel use to map the references used by the view to values the C++ model can use (string to enum). If we define custom attributes (Roles) that we want to use in our business logic and in the view we need to re-implement this method to include them so that the glue between the View and the ViewModel knows how to map the references.

view plaincopy to clipboardprint?

QHash<int,QByteArray> MyViewModel::roleNames() const

{

QHash<int, QByteArray> roles;

roles[MyViewModel_Roles::MyViewModel_Roles_Display] = "role_display";

roles[MyViewModel_Roles::MyViewModel_Roles_Details] = "role_details";

roles[MyViewModel_Roles::MyViewModel_Roles_KeyId] = "role_keyid";

roles[MyViewModel_Roles::MyViewModel_Roles_Value] = "role_value";

return roles;

}

QHash<int,QByteArray> MyViewModel::roleNames() const

{

QHash<int, QByteArray> roles;

roles[MyViewModel_Roles::MyViewModel_Roles_Display] = "role_display";

roles[MyViewModel_Roles::MyViewModel_Roles_Details] = "role_details";

roles[MyViewModel_Roles::MyViewModel_Roles_KeyId] = "role_keyid";

roles[MyViewModel_Roles::MyViewModel_Roles_Value] = "role_value";

return roles;

}

The MyViewModel::get() method uses these so that it can return an object to the View that uses the same mapping. Again this it to match what the ListModel was doing so that we don't have to change our QML scripts very much.

view plaincopy to clipboardprint?

QVariantMap MyViewModel::get( int rowNumber ) const

{

QVariantMap map;

QHash&lt:int,QByteArray> roleName = roleNames();

foreach (int i, roleName.keys())

{

// For each attribute (role) get its value and insert it into the map

// where the map's key is the attributes string reference

    // The data() method returns the value for the requested attribute
// where i is the attributes enum value.
// The index() method returns a QModelIndex which is a further
// abstraction layer (will talk about that in a later post)
map[roleName.value(i)] = data( index( rowNumber,0 ), i );
}
return map;

}

QVariantMap MyViewModel::get( int rowNumber ) const

{

QVariantMap map;

QHash&lt:int,QByteArray> roleName = roleNames();

foreach (int i, roleName.keys())

{

// For each attribute (role) get its value and insert it into the map

// where the map's key is the attributes string reference

    // The data() method returns the value for the requested attribute
// where i is the attributes enum value.
// The index() method returns a QModelIndex which is a further
// abstraction layer (will talk about that in a later post)
map[roleName.value(i)] = data( index( rowNumber,0 ), i );
}
return map;

}

Here we are returning a QVariantMap object back to the view; the keys in this map match the keys the QML script uses when displaying the collection of data from the view model when put inside a view such as a ListView. This way the return from this method can be used like so returnObject.role_display.

On the QML side of things we only have to change the Component.onCompleted() handlers a little bit in order to point it at the MyViewModel (C++) vs. the ListModel (QML).

view plaincopy to clipboardprint?

Component.onCompleted: {

var rowCount = MyModel.count;

items.remove(0,rowCount);

for( var i = 0;i < rowCount;i++ )

{

var entry = MyModel.get(i);

if(entry.role_display !== undefined){

items.insert(entry, "displayField");

}

}

}

Component.onCompleted: {

var rowCount = MyModel.count;

items.remove(0,rowCount);

for( var i = 0;i < rowCount;i++ )

{

var entry = MyModel.get(i);

if(entry.role_display !== undefined){

items.insert(entry, "displayField");

}

}

}

Notice that all we needed to do here is change the keyword myModel to MyModel where myModel was the id of the ListModel and MyModel is a reference to a root context which we set in the main.cpp file.

view plaincopy to clipboardprint?

MyViewModel* viewModel = new MyViewModel();

viewModel->initialize();

QQmlApplicationEngine engine;

//"MyModel" is a string reference that can be used in QML to look up this object.

engine.rootContext()->setContextProperty("MyModel", viewModel );

engine.load(QUrl(QStringLiteral("qrc:///main.qml")));

MyViewModel* viewModel = new MyViewModel();

viewModel->initialize();

QQmlApplicationEngine engine;

//"MyModel" is a string reference that can be used in QML to look up this object.

engine.rootContext()->setContextProperty("MyModel", viewModel );

engine.load(QUrl(QStringLiteral("qrc:///main.qml")));

When this version of the sample app is executed you'll get the exact same behaviour; Sample 1 and Sample 2 look and behave exactly the same the only difference is that one is using a ListModel (QML) and another is using MyViewModel (C++).

Ok so that is how you use the DelegateModelGroup in order to allow a View to only show a sub set of the data contained within the root Data Model.

You can download both samples which illustrates the use of a DelegateModelGroup here:

Sample 1: All QML

Sample 2: C++ ViewModel

Sample 3: All QML, One ListView, One DelegateModel, Multiple DelegateModelGroups

Thank you I hope you have enjoyed and found this tutorial helpful. Feel free to leave any comments or questions you might have below and I’ll try to answer them as time permits.

Until next time think imaginatively and design

一个QMLListView的例子--的更多相关文章

  1. 用一个简单的例子来理解python高阶函数

    ============================ 用一个简单的例子来理解python高阶函数 ============================ 最近在用mailx发送邮件, 写法大致如 ...

  2. Spring-Context之一:一个简单的例子

    很久之前就想系统的学习和掌握Spring框架,但是拖了很久都没有行动.现在趁着在外出差杂事不多,就花时间来由浅入深的研究下Spring框架.Spring框架这几年来已经发展成为一个巨无霸产品.从最初的 ...

  3. 高仿“点触验证码”做的一个静态Html例子

    先上源码: <html> <head> <title>TouClick - Designed By MrChu</title> <meta htt ...

  4. 关于apriori算法的一个简单的例子

    apriori算法是关联规则挖掘中很基础也很经典的一个算法,我认为很多教程出现大堆的公式不是很适合一个初学者理解.因此,本文列举一个简单的例子来演示下apriori算法的整个步骤. 下面这个表格是代表 ...

  5. 一个UWSGI的例子

    摘要:uwsgi执行顺序:启动master进程,执行python脚本的公共代码(import同一层).然后生成worker进程,uwsgi.post_fork_hook=init_functions, ...

  6. 扩展Python模块系列(二)----一个简单的例子

    本节使用一个简单的例子引出Python C/C++ API的详细使用方法.针对的是CPython的解释器. 目标:创建一个Python内建模块test,提供一个功能函数distance, 计算空间中两 ...

  7. fitnesse - 一个简单的例子(slim)

    fitnesse - 一个简单的例子(slim) 2017-09-30 目录1 编写测试代码(Fixture code)2 编写wiki page并运行  2.1 新建wikiPage  2.2 运行 ...

  8. Struts2的配置和一个简单的例子

    Struts2的配置和一个简单的例子 笔记仓库:https://github.com/nnngu/LearningNotes 简介 这篇文章主要讲如何在 IntelliJ IDEA 中使用 Strut ...

  9. 一个简单的例子搞懂ES6之Promise

    ES5中实现异步的常见方式不外乎以下几种: 1. 回调函数 2. 事件驱动 2. 自定义事件(根本上原理同事件驱动相同) 而ES6中的Promise的出现就使得异步变得非常简单.promise中的异步 ...

随机推荐

  1. ThreadLocal 实现线程内共享变量

    package com.cn.gbx; import java.util.Date; import java.util.Random; import java.util.Timer; import j ...

  2. 2013 Multi-University Training Contest 10

    HDU-4698 Counting 题意:给定一个二维平面,其中x取值为1-N,y取值为1-M,现给定K个点,问至少包括K个点中的一个的满足要求的<Xmin, Xmax, Ymin, Ymax& ...

  3. eclispse 中集成多个tomcat

    1.背景 在本地需要运行两个项目进行测试时,需要同时启动两个服务器,所以集成多个Tomcat到eclipse就成为一个必要的知识点. 2.准备知识 2.1 因为同时在一台主机上运行,所以多个服务器共用 ...

  4. SQL & PL/SQL 模块总结

    SQL 1. 各种function 2. merge 3. connect by PL/SQL 1. pl/sql 寄出 2. 游标 3. procedure 4. function 5. packa ...

  5. Android最佳性能实践(二)——分析内存的使用情况

    由于Android是为移动设备开发的操作系统,我们在开发应用程序的时候应当始终把内存问题充分考虑在内.虽然Android系统拥有垃圾自动回收机制,但这并不意味着我们就可以完全忽略何时去分配或释放内存. ...

  6. Java用WebSocket + tail命令实现Web实时日志

    原文:http://blog.csdn.net/xiao__gui/article/details/50041673 在Linux操作系统中,经常需要查看日志文件的实时输出内容,通常会使用tail - ...

  7. 关于时区的时间的详解,比如UTC\GMT等

    UTC 和 GMT 及 北京时间的关系 UTC和GMT,这两者几乎是同一概念.它们都是指的格林尼治标准时间,只不过UTC的称呼更为正式一点.两者的区别在于前者是一个天文 上的概念,而 后者是基于一个原 ...

  8. date 笔记

    1 语法 # date --help 用法:date [选项]... [+格式] 或:date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]] 1.1   ...

  9. Java 默认/缺省 server 还是 client 模式

    不多说,复制官方文档,适用于 Java 5 6 7 Architecture OS Default client VM if server-class, server VM; otherwise, c ...

  10. 激活MyEclipse 6.5方法-通过一段Java程序生成激活码

    在MyEclipse中新建一个Java类,名为MyEclipseKeyGen,将下面的Java代码拷贝到MyEclipseKeyGen类中,先修改变量subscriber的值,然后运行程序即可获得Su ...