百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 实用文章 > 正文

Qt之QListView使用

duoziman 2025-05-24 13:27 33 浏览

记录下使用QListView遇到的各种问题

QListView可以用来以列表的形式展示数据,在Qt中使用model/View结构来管理数据与视图的关系,model负责数据的存取,数据的交互通过delegate来实现。

添加数据模型

QT提供了一些现成的models用于处理数据项:
QStringListModel 用于存储简单的QString列表。
QStandardItemModel 管理复杂的树型结构数据项,每项都可以包含任意数据。
QDirModel 提供本地文件系统中的文件与目录信息。
QSqlQueryModel, QSqlTableModel,QSqlRelationTableModel用来访问数据库。

使用Qt自带的模型类QStandardItemModel即可。模型中的每个数据项都有一个与之对应的role来存储某一类数据。需要存取自定义数据可以使用UserRole,UserRole+1...

对于自定义数据类型,如果要使用QVariant,就必须使用Q_DECLARE_METATYPE注册。

Bash
struct ItemData{
    QString name;
    QString tel;
};

Q_DECLARE_METATYPE(ItemData)

单一数据存取

Bash
//存
Item->setData(itemStatus,Qt::UserRole);  // 单一存取

//取
ItemStatus status = (ItemStatus)(index.data(Qt::UserRole).toInt());

结构体数据存取

//存
Item->setData(QVariant::fromValue(itemData),Qt::UserRole+1);//整体存取

//取
QVariant variant = index.data(Qt::UserRole+1);
ItemData data = variant.value<ItemData>();

自定义delegate

模型的交互和绘制通过自定义delegate来实现,暂时没用到交互,先说下item的绘制。继承了QStyledItemDelegate后,重写paint函数处理item的样式,以及sizeHint函数返回item的大小:

绘制item

【领QT开发教程学习资料,点击下方链接莬费领取↓↓,先码住不迷路~】

点击→领取「链接」

void ItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{


    if(index.isValid())
    {
        painter->save();

        ItemStatus status = (ItemStatus)(index.data(Qt::UserRole).toInt());

        QVariant variant = index.data(Qt::UserRole+1);
        ItemData data = variant.value<ItemData>();

        QStyleOptionViewItem viewOption(option);//用来在视图中画一个item

        QRectF rect;
        rect.setX(option.rect.x());
        rect.setY(option.rect.y());
        rect.setWidth( option.rect.width()-1);
        rect.setHeight(option.rect.height()-1);

        //QPainterPath画圆角矩形
        const qreal radius = 7;
        QPainterPath path;
        path.moveTo(rect.topRight() - QPointF(radius, 0));
        path.lineTo(rect.topLeft() + QPointF(radius, 0));
        path.quadTo(rect.topLeft(), rect.topLeft() + QPointF(0, radius));
        path.lineTo(rect.bottomLeft() + QPointF(0, -radius));
        path.quadTo(rect.bottomLeft(), rect.bottomLeft() + QPointF(radius, 0));
        path.lineTo(rect.bottomRight() - QPointF(radius, 0));
        path.quadTo(rect.bottomRight(), rect.bottomRight() + QPointF(0, -radius));
        path.lineTo(rect.topRight() + QPointF(0, radius));
        path.quadTo(rect.topRight(), rect.topRight() + QPointF(-radius, -0));

        if(option.state.testFlag(QStyle::State_Selected))
        {
            painter->setPen(QPen(Qt::blue));
            painter->setBrush(QColor(229, 241, 255));
            painter->drawPath(path);
        }
        else if(option.state.testFlag(QStyle::State_MouseOver))
        {
            painter->setPen(QPen(Qt::green));
            painter->setBrush(Qt::NoBrush);
            painter->drawPath(path);
        }
        else{
            painter->setPen(QPen(Qt::gray));
            painter->setBrush(Qt::NoBrush);
            painter->drawPath(path);
        }

        //绘制数据位置
        QRect NameRect = QRect(rect.left() +10, rect.top()+10, rect.width()-30, 20);
        QRect circle = QRect(NameRect.right(), rect.top()+10, 10, 10);
        QRect telRect = QRect(rect.left() +10, rect.bottom()-25, rect.width()-10, 20);


        switch (status) {
        case S_RED:
            painter->setBrush(Qt::red);
            painter->setPen(QPen(Qt::red));
            break;
        case S_BLUE:
            painter->setBrush(Qt::blue);
            painter->setPen(QPen(Qt::blue));
            break;
        case S_YELLOW:
            painter->setBrush(Qt::yellow);
            painter->setPen(QPen(Qt::yellow));
            break;
        }

        painter->drawEllipse(circle);     //画圆圈

        painter->setPen(QPen(Qt::black));
        painter->setFont(QFont("Times", 12, QFont::Bold));
        painter->drawText(NameRect,Qt::AlignLeft,data.name); //绘制名字

        painter->setPen(QPen(Qt::gray));
        painter->setFont(QFont("Times", 10));
        painter->drawText(telRect,Qt::AlignLeft,data.tel); //绘制电话

        painter->restore();
    }
}

QSize ItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    return QSize(160, 60);
}

设置item不同状态下的样式

在paint函数中,还可以获得当前item的状态,并设置不同的样式:

 if(option.state.testFlag(QStyle::State_Selected)) //选中状态
        {
            painter->setPen(QPen(Qt::blue));
            painter->setBrush(QColor(229, 241, 255));
            painter->drawPath(path);
        }
        else if(option.state.testFlag(QStyle::State_MouseOver))//鼠标划过状态
        {
            painter->setPen(QPen(Qt::green));
            painter->setBrush(Qt::NoBrush);
            painter->drawPath(path);
        }
        else{
            painter->setPen(QPen(Qt::gray));
            painter->setBrush(Qt::NoBrush);
            painter->drawPath(path);
        }

设置好模型后,再对QListView进行下属性设置:

    ui->listView->setItemDelegate(m_delegate);       //为视图设置委托
    ui->listView->setSpacing(15);                   //为视图设置控件间距
    ui->listView->setModel(m_model);                  //为委托设置模型
    ui->listView->setViewMode(QListView::IconMode); //设置Item图标显示
    ui->listView->setDragEnabled(false); 

模型的数据和展示都处理好后,运行效果如下:

过滤item

Qt中提供了一个方便处理模型排序和过滤的类QSortFilterProxyModel,通过他可以非常方便的处理我们的model。将QListView展示的model设置成代理模型:

    ui->listView->setItemDelegate(m_delegate);       //为视图设置委托
    ui->listView->setSpacing(15);                   //为视图设置控件间距
    m_proxyModel = new QSortFilterProxyModel(ui->listView);
    m_proxyModel->setSourceModel(m_model);
    m_proxyModel->setFilterRole(Qt::UserRole);
    m_proxyModel->setDynamicSortFilter(true);
    ui->listView->setModel(m_proxyModel);                  //为委托设置模型
    ui->listView->setViewMode(QListView::IconMode); //设置Item图标显示
    ui->listView->setDragEnabled(false);            //控件不允许拖动

其中,m_proxyModel->setFilterRole(Qt::UserRole);设置根据模型的某一项数据来处理模型的过滤。proxyModel可以设置过滤的方式,根据QString或者正则表达式来过滤:

m_proxyModel->setFilterFixedString(QString::number(S_RED));//根据字符串过滤
m_proxyModel->setFilterRegExp(QRegExp("^[0|2]#34;)); //根据正则表达式过滤

获取选中item

对于列表中item的操作,可以是在delegate中处理交互事件,也可以通过QListView获取到所有选中item的QModelIndex,然后对模型本身进行修改。这里我选择的后者:

QModelIndexList modelIndexList = ui->listView->selectionModel()->selectedIndexes();
   

设置多选

将QListView的selectionBehavior设置成MultiSelection即可。

对于多选的时候,模型的修改有一个坑。在设置了代理模型后,由于开启了动态排序模式,如果修改代理模型的数据,在第一个item修改数据后可能就不在当前过滤模型中,会被过滤掉,后面的item的QModelIndex就会变化,导致后续的修改失败。

dynamicSortFilter : bool
This property holds whether the proxy model is dynamically sorted and filtered whenever the contents of the source model change.
Note that you should not update the source model through the proxy model when dynamicSortFilter is true. For instance, if you set the proxy model on a QComboBox, then using functions that update the model, e.g., addItem(), will not work as expected. An alternative is to set dynamicSortFilter to false and call sort() after adding items to the QComboBox.
The default value is true.

有两个方法处理这个坑,一是不修改代理模型,修改源模型的数据。二是在修改模型数据的时候关闭代理模型的动态排序功能。

修改数据

QModelIndexList sourceIndexList;
    foreach (QModelIndex modelIndex, modelIndexList){
        sourceIndexList<<m_proxyModel->mapToSource(modelIndex); //获取源model的modelIndex
    }

//    g_proxyModel->setDynamicSortFilter(false);
    foreach (QModelIndex sourceIndex, sourceIndexList){
        ItemStatus status = (ItemStatus)(sourceIndex.data(Qt::UserRole).toInt());
        qDebug() << "Index : " << sourceIndex.row();

        switch (status) {
            case S_RED:
                redNum--;
                break;
            case S_BLUE:
                blueNum--;
                break;
            case S_YELLOW:
                yellowNum--;
                break;
        }

        status = S_RED;
        redNum++;

        m_model->setData(sourceIndex,status,Qt::UserRole);
    }
//    g_proxyModel->setDynamicSortFilter(true);

弄完大概是酱紫的:

相关推荐

30天学会Python编程:18. Python数据库编程入门

18.1数据库基础18.1.1数据库类型对比18.1.2DB-API规范核心接口:connect()-建立连接cursor()-创建游标execute()-执行SQLfetchone...

Springboot数据访问(整合druid数据源)

Springboot整合druid数据源基本概念SpringBoot默认的数据源是:2.0之前:org.apache.tomcat.jdbc.pool.DataSource2.0及之后:com.z...

多维分析后台实践 1:基础宽表(基础宽度怎么确定)

【摘要】用实例、分步骤,详细讲解多维分析(OLAP)基础宽表的实现。点击了解多维分析后台实践1:基础宽表实践目标本期目标是练习如何搭建多维分析后台的基础宽表,并通过SPL和SQL访问基础宽表...

大促数据库压力激增,如何一眼定位 SQL 执行来源?

作者:京东科技王奕龙你是否曾经遇到过这样的情况:在大促活动期间,用户访问量骤增,数据库的压力陡然加大,导致响应变慢甚至服务中断?更让人头疼的是,当你试图快速定位问题所在时,却发现难以确定究竟是哪个业...

迭代器模式:解锁高效遍历新姿势!(迭代器模式实例)

迭代器模式:解锁高效遍历新姿势!一、前言在移动端开发中,我们经常需要遍历各种集合:比如处理联系人列表、动态加载图片、批量操作UI元素等。初学者往往直接使用for-in、while、下标索引等方式...

MySQL实战:基于游标与覆盖索引的高效分页解决分页查询卡顿

当分页成为性能杀手你是否遇到过这样的场景?用户浏览电商订单时,翻到第100页需要等待8秒后台管理系统查询日志,每次翻页都触发数据库CPU飙升移动端APP瀑布流加载,越往下滑动卡顿越明显根本原因:传统的...

SQL SERVER:游标概述(34)(sql数据库游标的使用步骤)

一、游标:1、游标的概念:即指针,可指向结果集中某一行,起到定位的作用,其用途有:1)定位到查询结果集中的某一行。2)对当前位置的数据进行读写。3)可以对结果集中的数据单独操作,而不是整行执行相同的操...

高级SQL之游标(sql server游标的作用)

【本文详细介绍了数据库中游标的基本概念和使用方法,欢迎读者朋友们阅读、转发和收藏!】1基本概念1.1定义一般情况下,我们用SELECT这些查询语句时,都是针对的一行记录而言,如果要在查询分析器中对...

mysql备份自动存储到开源的minio(mysql备份数据)

概述minio是开源企业级对象存储系统,有着高性能、使用简单、易扩展、兼容性强等特性。下面分享一下如何把mysql备份自动存储在minio中。一、前提条件安装minio客户端mc已经安装好的mini...

TiDB:10 TB超大数据库每秒GB速度备份和还原

作为mysql数据的分布式大型集群替代,TiDB被越来越多企业在生产中使用。作为一个超大的数据库集群,对TB集合数据的备份和还原是一个难点和挑战。最近TiDB官方在4.0RC版本中新增加了一个数据备...

服务器数据自动备份最佳组合工具(服务器备份软件有哪些)

如果你是一位公司网管或者是相关运维人员,想必对服务器的安全非常重视,因为涉及到一些重要数据的安全,要想确保数据安全,本地备份(就是具有数据库的服务器上备份),介质备份(就是用移动硬盘或者其他可以外置的...

一文掌握怎么利用Shell+Python实现多数据源的异地备份程序

简介:在信息化时代,数据安全和业务连续性已成为企业和个人用户关注的焦点。无论是网站数据、数据库、日志文件,还是用户上传的文档、图片等,数据一旦丢失,损失难以估量。尤其是当数据分布在多个不同的目录、服务...

五个在线备份解决方案及优势分析(在线备份shsh2)

在云计算快速、迅速、飞速发展的当下,你要是不用点和云计算相关的新产品你还真不好意思说你了解互联网。如今,在线备份已经成为个人和中小企业备份的重要工具,通过web页面的集中管理,就可以随时对数据进行备份...

一文掌握怎么利用Shell+Python实现完美版的多数据源备份程序

简介:在当今数字化时代,无论是企业还是个人,数据的安全性和业务的连续性都是至关重要的。数据一旦丢失,可能会造成无法估量的损失。因此,如何有效地对分布在不同位置的数据进行备份,尤其是异地备份,成为了一个...

SQL Server数据备份指南,3种方式关键时刻能救命!

公司服务器突然崩了!数据库里存着半年的订单记录全没了,别慌!这情况我见多了,今天手把手教你用SQLServer保住数据。就算你是刚入行的网管,跟着做也能零风险操作。一、最省事的图形化备份打开SQL...