做过 Qt 后台工具、数据管理系统或者日志查询界面的朋友,应该都遇到过分页需求。
刚开始看,分页控件似乎很简单:
上一页
下一页
页码按钮
首页和末页
好像随手写几十行代码就能完成。
但真正放进项目以后,各种细节会不断冒出来:
总页数怎么计算?
当前页怎么高亮?
页码太多时怎么显示省略号?
第一页和最后一页时,按钮怎么禁用?
每页条数修改后,当前页怎么调整?
当前页显示的是第几条到第几条数据?
表格刷新时,分页状态怎么通知业务层?
不同页面重复使用时,怎么减少重复代码?
这些问题都不算难。
但每做一个表格页、日志页、查询页都重新写一遍,时间久了就会发现,项目里到处都是相似的分页代码。
所以这次,我把 Qt Widgets 分页功能单独封装成了一个可复用控件。
控件已经整理为独立 Demo,可以直接移植到自己的项目中使用。
项目名称:
PaginationDemo需要完整开源工程的朋友,可以在评论区回复“开源”,或者私信发送“开源”获取。
为什么要单独封装分页控件
分页本身并不是一个复杂功能。
但它的使用频率非常高。
在 Qt 项目中,下面这些场景基本都会用到分页:
QTableView 数据分页
日志记录查询
用户列表
文件列表
设备记录
任务历史
检索结果
数据库查询结果
后台管理工具
本地数据浏览器
如果每个页面都单独写分页逻辑,就会不断重复处理这些问题:
页码计算 当前页状态 按钮禁用 省略号显示 总记录数换算 每页条数变化 数据起止范围 信号与业务联动分页控件单独封装以后,业务页面只需要关心两个核心参数:
当前第几页 每页加载多少条剩下的显示和状态管理,都交给分页控件完成。
这样不仅能减少重复代码,也能让表格页面的逻辑更清晰。
控件支持哪些功能
目前分页控件已经支持常见的分页操作:
首页
上一页
下一页
末页
当前页高亮
自动生成页码
页码过多时自动显示省略号
首页和末页快速跳转
第一页、最后一页按钮禁用
紧凑显示模式
总记录数分页
每页条数动态修改
当前页数据范围计算
分页状态信号通知
控件整体启用和禁用
对于大多数 Qt 表格和列表项目来说,这些功能已经可以覆盖常见需求。
页码太多时自动显示省略号
如果总页数比较少,例如只有 3 页,可以直接显示:
1 2 3但如果总共有 20 页、100 页,甚至更多,就不适合把所有页码按钮全部显示出来。
控件会根据当前页自动生成可见页码,并使用省略号隐藏中间部分。
例如当前页在中间时,可以显示为:
1 ... 7 8 9 ... 20这样既能保留首页和末页,又能突出当前页附近的页码。
用户既可以连续翻页,也可以快速回到第一页或最后一页。
当前页左右显示几个相邻页码,也可以通过参数控制:
pager->setSiblingCount(1);设置为1后,当前页左右各显示一个相邻页码。
例如当前是第 8 页时:
1 ... 7 8 9 ... 20如果设置为2,则可以显示更多相邻页码:
1 ... 6 7 8 9 10 ... 20支持根据总记录数自动计算页数
真实项目中,后端或者数据库通常返回的是总记录数,而不是总页数。
例如接口返回:
总记录数:237前端设置:
每页显示:20 条分页控件需要自动计算总页数。
推荐使用下面的方式初始化:
pager->setPagination(237, 20, 1);三个参数分别表示:
总记录数:237 每页条数:20 当前页:1控件会自动计算总页数。
计算结果为:
共 12 页最后一页不足 20 条也会正常处理,不需要业务层额外判断。
相比直接设置总页数,这种方式更贴近实际项目中的数据分页流程。
分页状态可以直接通知业务层
分页控件不只是负责显示按钮。
真正用于项目时,它还需要和表格、列表、数据库查询或者网络请求进行联动。
控件提供了完整的分页状态信号:
voidpaginationChanged( int currentPage, int pageSize, int totalItemCount );业务层只需要监听这个信号,就可以在页码变化时重新加载数据。
示例:
connect( pager, &PaginationWidget::paginationChanged, this, [this](int page, int pageSize, int total) { qDebug() << "当前页:" << page; qDebug() << "每页条数:" << pageSize; qDebug() << "总记录数:" << total; // 在这里重新查询数据库 // 或者重新请求接口 // 然后刷新 QTableView } );这样分页控件只负责分页状态。
表格页面只负责数据查询和显示。
两部分逻辑分开以后,后续修改和维护都会方便很多。
支持动态修改每页条数
很多数据管理界面都会提供每页条数选择,例如:
每页 10 条 每页 20 条 每页 50 条 每页 100 条修改每页条数时,可以直接调用:
pager->setPageSize(50);控件会重新计算总页数。
同时会尽量保持用户当前查看的数据位置,避免每次修改每页条数都直接跳回第一页。
例如用户当前正在查看第 101 条附近的数据。
原来每页显示 20 条,当前可能位于第 6 页。
切换到每页显示 50 条后,控件会重新计算页码,尽量让用户仍然看到原来附近的数据,而不是突然回到第一条。
这种细节看起来不明显,但在真实项目中会直接影响使用体验。
当前页显示的数据范围可以直接获取
很多表格底部都会显示类似的信息:
显示第 21 - 40 条,共 237 条这个范围不需要业务层重复计算。
控件已经提供了对应接口:
constint begin = pager->firstItemNumber(); constint end = pager->lastItemNumber();然后可以直接生成提示信息:
QString text = QString("显示第 %1 - %2 条,共 %3 条") .arg(begin) .arg(end) .arg(totalCount);例如当前是第 2 页,每页显示 20 条,总记录数为 237,结果就是:
显示第 21 - 40 条,共 237 条如果当前位于最后一页,也会自动处理不足一整页的情况。
例如最后一页只剩 17 条:
显示第 221 - 237 条,共 237 条最简单的使用方式
如果项目中已经知道总页数,可以直接设置页数和当前页:
auto *pager = new PaginationWidget(this); pager->setPageCount(20); pager->setCurrentPage(1); connect( pager, &PaginationWidget::currentPageChanged, this, [](int page) { qDebug() << "当前页:" << page; } );这种方式适合只需要简单页码切换的场景。
如果项目中使用的是总记录数分页,更推荐下面的方式:
auto *pager = new PaginationWidget(this); pager->setPagination(237, 20, 1); connect( pager, &PaginationWidget::paginationChanged, this, [this](int page, int pageSize, int total) { qDebug() << page << pageSize << total; // 重新加载表格数据 } );数据加载时可以临时禁用分页
如果分页切换后需要请求接口或者查询大量数据,建议在加载过程中临时禁用分页控件。
pager->setEnabled(false);数据加载完成后再恢复:
pager->setEnabled(true);这样可以避免用户连续点击页码,导致重复查询、重复请求或者界面状态错乱。
对于网络请求和耗时查询场景来说,这一点非常实用。
控件如何移植到自己的项目
控件本体主要包含两个文件:
src/PaginationWidget.h src/PaginationWidget.cpp把这两个文件复制到自己的 Qt Widgets 项目中,然后加入构建配置即可。
MainWindow.*主要用于演示,不是控件必须文件。
Demo 中包含了几个常见示例:
普通分页
紧凑分页
页码省略
总记录数分页
每页条数切换
表格与分页联动
当前数据范围显示
控件禁用状态
因此可以直接参考 Demo 中的使用方式,将控件接入现有项目。
控件使用纯 Qt Widgets 实现,不依赖第三方 UI 控件库,移植成本比较低。
适合哪些项目使用
这个分页控件比较适合下面这些项目:
Qt 后台管理系统 数据库管理工具 日志查看工具 设备管理平台 文件管理工具 任务调度工具 数据检索程序 本地客户端管理软件 QTableView 数据展示页面它可能不是视觉效果最炫的控件。
但却是项目中使用频率非常高的一类基础组件。
分页功能越早统一封装,后面的表格页和列表页就越容易维护。
后续可以继续增加哪些功能
目前的分页功能已经可以满足大多数项目需求。
后续还可以继续增加:
输入页码快速跳转
每页条数下拉框
键盘左右键切换
鼠标滚轮切页
自定义主题颜色
深色模式
QSS 样式接口
服务端分页示例
QTableView 完整联动案例
加载中状态
无数据状态
页码跳转校验
大家也可以根据自己的项目需求进行扩展。
demo位置:https://download.csdn.net/download/qq_40170041/93142990