做过 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