十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Qt表格无存储案例:控件选型、核心用法与常见坑

Qt表格无存储案例:控件选型、核心用法与常见坑 简介面向Qt初学者的表格控件入门案例演示如何在不涉及数据持久化的情况下创建并操作表格界面适用于原型演示或仅需临时内存数据的应用场景。资源包共6个文件包含2个cpp源文件、1个头文件、1个pro工程文件、1个user配置文件和1个ui界面文件整体仅7KB结构精简。其中main.cpp和maindialog.cpp承担程序入口与核心交互逻辑maindialog.h完成类声明maindialog.ui指定对话框布局pro文件则定义项目编译设置。案例重点展示QTableView与QStandardItemModel的配合使用帮助读者理解Qt视图/模型分离思想、项目文件组织方式及信号与槽的触发流程。已有72位开发者学习过该资源适合需要快速上手Qt表格功能或梳理基础工程结构的入门者参考。 前阵子写一个内部工具需求一句话就能说完qt-表格的基本用法案例无存储功能。说白了就是界面上放一个表格把采集到的数据展示出来能看、能选、能排序不接数据库、不落盘。看着简单真写完一套下来发现坑还是不少从控件选型到列宽自适应再到排序后数据对不上号每一环都有值得记一笔的地方。这篇文章就把我实际用到的完整套路拆开讲一遍主要面向刚接触Qt、想在界面上快速跑起表格的开发者。先说明一下文中所有内容都是围绕“无存储”这个前提展开的数据只活在内存里关闭程序自动释放不涉及数据库、文件读写、持久化方案。如果你后续要接数据存储表格交互这部分依然能复用但存储层你得另做设计。1. 动手之前先弄明白你要用的是哪种表格1.1 QTableWidget和QTableView到底差在哪很多朋友做Qt表格时的第一个动作是打开Qt Designer从左边拖一个Table Widget控件进来然后就开始在代码里setItem。这个做法本身没错但前提是你得知道自己做了什么选择——你拖动的是QTableWidget它其实是在QTableView基础上封装了一层把数据模型和单元格Item绑定在了一起。QTableView本身是不存数据的它只负责把模型里的数据渲染出来数据要放在QAbstractTableModel子类里通过Model/View机制做数据与显示的分离。这套机制的优势很明显数据量再大它也只渲染可见区域所以复杂报表、商业软件里的表格基本都是走这条路。代价是写起来麻烦光一个model子类就要重写rowCount、columnCount、data、headerData这些方法新手容易直接懵圈。而QTableWidget是给懒人准备的。它内部内置了一个标准数据模型每个单元格就是一个QTableWidgetItem对象你直接new出来塞进表格就能显示。对于无存储功能、数据量不大几千行以内、快速开发的小工具来说QTableWidget绝对够用而且代码直观很多。1.2 无存储功能场景下的选型建议“无存储功能”这个限定其实很关键它意味着数据只活在内存里程序退出就消失不需要考虑数据一致性、事务、批量落盘这些问题。在这样的前提下选择QTableWidget能省掉大量心智负担。但有个例外如果你的页面需要频繁刷新、数据量在几万行以上或者你打算后续接数据库建议直接上QTableView搭配QStandardItemModel。别问我是怎么知道的我就是从QTableWidget在5万行数据面前卡成PPT之后才学乖的。下面按项目规模给个参考表方便你快速做决定。场景推荐控件理由临时演示、内部工具行数5000以内QTableWidget上手快代码量少足够直观频繁刷新行数5000以上QTableView QStandardItemModel渲染性能好刷新可控报表场景、复杂表头、需要树形展开QTableView 自定义Model分离结构清晰可扩展性最强纯展示、完全不需要用户编辑QTableView只读模式避免误操作性能表现更好选型这事别一开始就奔着最复杂的设计去。工具型页面用QTableWidget两周能做完用QTableView自定义Model可能光搭架子就要两天。性价比要算清楚。2. 环境准备版本、编译器与启动即崩的坑2.1 Qt版本怎么选不纠结Qt现在主要有两个大版本线Qt 5.15和Qt 6系列。对做桌面表格工具来说Qt 5.15.2的生命力依然很强资料多、生态稳很多公司内部项目还在用它Qt 6系列在CMake支持和渲染上更好但如果你手里的旧代码依赖了不少老模块迁移成本需要考虑。新手学表格用法选Qt 5.15.2或Qt 6.5 LTS版本都行两者在QTableWidget和QTableView上几乎没有API差异不必在这上面浪费时间纠结。安装时有个关键点组件务必勾选对应编译器套件。Windows下最常见的是MinGW和MSVC两种。MinGW版本安装简单、不依赖Visual Studio适合自己写小工具MSVC能和许多第三方库顺畅衔接但需要另外安装VS环境。很多工程编译不过最后查出来就是编译器套件装错了白白浪费半天。2.2 下载慢、装不上走镜像或离线包这方面建议直接走国内镜像比如清华、中科大的Qt镜像源。Qt在线安装器本身在网络不佳的时候基本用不上用镜像参数可以解决大部分问题。如果你不想折腾在线安装器直接下载对应版本的离线安装包也行体积虽然大但胜在稳定装一次能管很久。2.3 platform plugin崩溃到底是怎么回事“windows no qt platform plugin could be initialized, reinstalling the application”这个报错凡是打包过Qt程序的人基本都见过。它通常出现在你把exe拷到别的机器上运行、或者本机环境变量乱掉的时候。原因很简单Qt找不到平台插件也就是qwindows.dll。解决办法是使用windeployqt对编译出的exe执行一遍它会把依赖的Qt运行库和插件自动拷贝到exe旁边。执行完看一眼exe目录下有没有platforms文件夹里面有没有qwindows.dll基本就能确认是否成功。另外还有一种隐蔽情况你自己装了多个Qt版本PATH环境变量被提前指向了某个Qt目录运行时加载了版本不对的dll一样会崩。解决思路就是把环境变量理顺重新执行windeployqt部署干净。3. 核心实操从零搭一个无存储的表格案例3.1 最小化表格设置行列与表头下面直接开始写代码。创建一个10行3列的表格表头分别是“姓名”“数值”“备注”。#include QApplication #include QTableWidget #include QHeaderView #include QHBoxLayout int main(int argc, char *argv[]) { QApplication app(argc, argv); QWidget window; QHBoxLayout *layout new QHBoxLayout(window); QTableWidget *table new QTableWidget(window); table-setRowCount(10); table-setColumnCount(3); QStringList headers {姓名, 数值, 备注}; table-setHorizontalHeaderLabels(headers); layout-addWidget(table); window.setWindowTitle(Qt表格基本用法); window.resize(640, 480); window.show(); return app.exec(); }这里有一个细节很多人吃过亏setRowCount和setColumnCount除了建立行列结构还会把所有单元格初始化为空Item对象。注意是空Item不是nullptr。你调用table-item(row, col)得到的可能是一个有效的空指针text()返回值是空字符串所以不要用item nullptr来判断某个单元格“不存在”这个判断在这种场景下不成立。3.2 填充数据与单元格样式填充数据的方式是setItem需要自己new一个QTableWidgetItem。写一个双循环把数据填进去。for (int row 0; row 10; row) { for (int col 0; col 3; col) { QTableWidgetItem *item new QTableWidgetItem( QString(第%1行 第%2列).arg(row).arg(col)); if (col 1) { item-setTextAlignment(Qt::AlignCenter); } table-setItem(row, col, item); } }注意setItem之后表格接管了这个item的管理权你绝对不要自己delete这个指针否则会崩溃。想改某个单元格内容直接item-setText(新内容)就行不要重新new一个再setItem覆盖旧指针那样会泄漏旧对象。类似的如果要高亮异常值可以用下面的代码给单元格上色item-setBackground(QColor(255, 243, 224)); item-setForeground(QColor(192, 0, 0)); item-setFont(QFont(Microsoft YaHei, 9, QFont::Bold));setForeground是前景色setBackground是背景色。在工具型界面上用颜色区分异常值比让用户逐个读文本高效得多。3.3 常见的交互设置编辑、选中、焦点表格默认是允许双击编辑的。在无存储场景下是否允许用户编辑取决于你的业务需求。很多内部工具只希望用户查看数据不希望用户手滑改乱所以封装表格的第一件事往往是关掉编辑。table-setEditTriggers(QAbstractItemView::NoEditTriggers); table-setSelectionBehavior(QAbstractItemView::SelectRows); table-setSelectionMode(QAbstractItemView::SingleSelection); table-setAlternatingRowColors(true);setSelectionBehavior(SelectRows)在做表格工具时非常常用用户点击任意一个单元格选中的是整行后续读取当前行数据就方便很多。选中后想拿到当前点击位置可以连接cellClicked信号。connect(table, QTableWidget::cellClicked, this, [](int row, int column) { QTableWidgetItem *item table-item(row, column); if (!item) { return; } qDebug() 当前点击: row column item-text(); });我建议你形成条件反射从表格里取item后先判空再使用。前面说了行列结构初始化后可能已经有空Item对象但你手动插入的行和列未必每一格都有值取到nullptr也是常事。判空能替你挡掉很多莫名其妙的崩溃。3.4 无存储数据的来源内存数据填充既然无存储数据从哪来通常是内存里的临时结构体、从串口或网络收到的缓存数据、或者是传感器采集的数据。比如做一个串口助手收到一帧数据解析后追加一行到表格里这类场景下表格就是临时显示区关闭程序数据清零。模拟一批内存数据可以用QList 临时保存QListQStringList rawData { {张三, 12.5, 正常}, {李四, 13.1, 偏高}, {王五, 11.8, 偏低} }; table-setRowCount(rawData.size()); for (int i 0; i rawData.size(); i) { for (int j 0; j rawData.at(i).size(); j) { table-setItem(i, j, new QTableWidgetItem(rawData.at(i).at(j))); } }如果你要动态追加行比如数据一条一条到达那就别一次性setRowCount改用insertRow。在循环里动态插入时一定要控制最大行数比如保留最新的1000行超了就移除旧行否则内存会随着数据量无限增长界面也会越来越卡。4. 显示与交互优化让表格更像一个正经工具4.1 列宽自适应与表头优化表格做出来最容易被吐槽的就是列宽。默认情况下列宽是固定的表头文字和数据经常挤在一起。处理办法是给表头设置伸缩模式让列宽随着窗口变化自适应。table-horizontalHeader()-setSectionResizeMode(QHeaderView::Stretch);Stretch模式会把所有列均分为表头宽度适合列数较少的情况。另一种是内容自适应table-resizeColumnsToContents();resizeColumnsToContents只执行一次依赖当前内容计算宽度。如果你希望内容变化时自动重新计算宽度可以把resizeMode设为ResizeToContents。但注意ResizeToContents在大数据量下性能很差因为每变化一次就要重新测量单元格宽度。我个人的习惯是工具型页面优先用Stretch最后一列配合setStretchLastSection(true)再给表头加上“双击列边界自适应”的原生交互兼顾美观和性能。4.2 排序与合计行的实现表格排序只需要一行代码table-setSortingEnabled(true);开启后点击表头就会自动排序。但这里藏着一个大坑排序会改变行的底层顺序如果你按当前行号去数据源里取原始数据很可能取到完全不同的一行。很多新手在这里栽过。解决办法有三种一是别开排序二是用一个隐藏列存ID三是排序前备份一份索引映射。第三种在无存储场景下最容易实现。合计行也是工具表格里的高频需求。在表格末尾插入一行对需要求和的列遍历求和填入结果。为了不让合计行在排序时被混进数据里我通常在数据全部填充完成后再插入合计行并且给合计行的单元格设置明显的底色。int rowCount table-rowCount(); table-insertRow(rowCount); double sum 0.0; for (int row 0; row rowCount; row) { QTableWidgetItem *item table-item(row, 1); if (item) { sum item-text().toDouble(); } } QTableWidgetItem *totalItem new QTableWidgetItem( QString::number(sum, f, 2)); totalItem-setBackground(QColor(221, 221, 221)); totalItem-setTextAlignment(Qt::AlignCenter); table-setItem(rowCount, 1, totalItem);注意这里用到了setItem来覆盖新插入行的指定列。如果你有多个列需要求和可以在for循环里用数组或列表逐列统计再统一写入。4.3 大数据量下的渲染与刷新优化QTableWidget在数据量超过几万行后性能下滑很明显。原因在于每setItem一个单元格就生成一个QTableWidgetItem对象几万行十几列就是几十万个对象内存分配和释放的开销都很大。如果你只是临时显示大量数据建议换用QTableView配合QStandardItemModel。我当时从QTableWidget切到QTableView之后同样规模的数据刷新速度提升了好几倍。如果暂时没法换代码也有两个临时优化手段。一个是用setUpdatesEnabled先暂停刷新等数据填充完再恢复table-setUpdatesEnabled(false); // 清空并重新填充数据... table-setUpdatesEnabled(true); table-viewport()-update();另一个是在实时显示场景里配合QTimer做节流比如数据到达后不立刻刷新表格而是积攒100ms再统一刷新一次界面。这样串口或网络高频数据进来时界面不会被拖死只是显示上略有延迟工具场景完全够用。5. 常见问题与排查技巧实录5.1 发布后启动崩溃platform plugin报错这个前面已经说过直接给结论先用windeployqt部署再从Qt安装路径的plugins/platforms目录下找qwindows.dll手动检查。比较隐蔽的情况是开发机和目标机器上的Qt版本不一致导致部署出来还是缺运行时。处理方式是确认目标机器不需要安装Qt但要确保exe目录下有platforms、styles、imageformats这些插件文件夹以及对应dll。5.2 表格刷新卡顿界面无响应真实场景我在工具里用QTableWidget一次性setItem 3万行乘6列界面卡死好几秒。排查下来发现每个单元格都new了对象光内存分配就花了很久。后来改成每次只显示其中1000行配合按钮翻页界面响应速度立刻上来了。如果你的场景确实要求一次性显示大量数据直接上QTableView加自定义ModelQTableWidget不要硬扛。另外还要注意setSortingEnabled(true)开启后如果频繁insertRow每次插入都会触发排序性能会很差。正确做法是数据全部填充完后再开启排序填充期间保持关闭。5.3 读取item文本时崩溃从表格里读某个单元格时直接崩溃十有八九是没有判空。前面反复提过这里总结成一条原则从item(row, col)取到指针后先判断是否为nullptr再判断text是否为空。QTableWidgetItem *item table-item(row, col); if (item !item-text().isEmpty()) { // 安全使用item-text() }尤其是动态插入行时不是每一列都填了内容空的那一格取到nullptr非常正常。遍历所有单元格前判空是一个必须养成的好习惯。5.4 排序后读数错乱开启排序后用户点击表头行的物理顺序就会变。你按照当前行号从原始数据容器里取数据取到的可能是别的东西。我踩过一次之后所有表头带排序的表格都会加一个隐藏列专门存原始ID或索引。QTableWidgetItem *idItem new QTableWidgetItem(QString::number(rowIndex)); idItem-setData(Qt::UserRole, rowIndex); table-setItem(row, 0, idItem); table-setColumnHidden(0, true);这样排序之后你依然可以通过item-data(Qt::UserRole)拿到原始索引精准定位数据来源。5.5 中文字体和样式的小细节Qt默认字体在中文环境下有时显得粗糙可以通过样式表统一调整table-setStyleSheet( QTableWidget { font-family: Microsoft YaHei; font-size: 12px; } QHeaderView::section { background-color: #f0f0f0; padding: 4px; } );这里有一点要注意样式表里的字体名称必须和系统实际字体名一致否则不生效。如果跨平台使用建议用QFont统一设置比直接写死字体名更稳。最后再分享一个个人习惯凡是无存储的临时表格我都会在窗口关闭前把表格内容导出成CSV方便调试时快速核对数据。实现起来很简单遍历所有行把单元格内容拼接成字符串写文件即可。虽然标题说无存储功能但临时导出的能力对排查问题帮助极大强烈建议你也加上。本文还有配套的精品资源点击获取
返回列表