Qt使用QXlsx读取Excel并显示到QTableWidget的实战指南

发布时间:2026/9/1 6:24:04
Qt使用QXlsx读取Excel并显示到QTableWidget的实战指南 简介这是一份面向Qt开发者的Excel读写示例工程基于QXlsx库封装了简洁的表格数据交互方案适合需要在QTableWidget中导入、编辑并导出Excel文件的场景。资源包共92个文件以C头文件.h和源文件.cpp为主完整包含QXlsx库源码及配套工程文件.pro、界面定义.ui与测试表格.xlsx压缩包仅161KB结构紧凑便于直接对照学习。已有1003人浏览学习。工程在官方演示基础上做了简化去除了商业许可限制核心功能支持读取Excel中的数据并显示到QTableWidget同时可修改单元格内容后重新保存至Excel当前重点处理了单元格合并情况对字体、颜色等格式未做深入展开适合关注数据读写逻辑的开发者快速上手。通过该资源可掌握QXlsx的基本调用方式、工程配置方法以及表格控件与Excel数据互通的实现思路。 我是在一个自动化数据管理系统里做的桌面端老板要求把客户发来的Excel直接导入程序里展示再支持筛选和导出。核心控件选定QTableWidget但Excel解析这块当时纠结了好一阵。网上搜了一圈读Excel的主流方案无非是QAxObject调COM接口、自己解析xlsx的XML、或者用第三方库QXlsx。对比下来我选择了QXlsx原因很简单跨平台、不用装Office、API清爽。这篇博文就把我基于QXlsx读取Excel并显示到QTableWidget的完整过程写出来包括库的配置、数据读取、表格展示、列宽自适应、大数据量加载优化以及我踩过的几个坑。1. 整体设计与思路拆解1.1 为什么选QXlsx而不是其他方案在Qt里处理Excel最常见的三条路QAxObject仅Windows需本机安装Office或WPS依赖COM组件部署时容易被杀毒软件拦截而且速度感人、直接将xlsx文件当zip解压后解析XMLxlsx本质是zip包里面是一堆XML文件但sheet、样式、共享字符串的关系复杂手写解析逻辑量大还容易漏、第三种就是直接用QXlsx这种第三方库。QXlsx是GitHub上的开源项目底层是把xlsx当zip处理核心代码是Qt风格的C类直接集成到工程里即可不依赖OfficeWindows/Linux/macOS通用。我的使用环境是Qt 5.15.2 MinGW 64位整个集成过程大约10分钟搞定。1.2 整体功能拆解按照实际需求我把这个功能拆成四块选择文件通过QFileDialog弹出对话框过滤xlsx、xlsm等格式。解析数据用QXlsx读取工作表所有行和列存入QList 或自定义结构体。表格展示把数据填充到QTableWidget设置行列数、表头、列宽自适应、单元格只读。性能优化数据量很大时关闭UI更新、使用批量插入、限制最大行数等策略。这套逻辑除了Qt5.15在Qt6.2、Qt6.5上我也试过QXlsx源码编译兼容性良好几乎不需要改动。2. 环境准备与QXlsx库集成2.1 QXlsx库的获取方式QXlsx的源码托管在GitHub有两种使用方式一是直接下载源码包把src目录下的文件全部拷贝到自己的工程里二是用qmake的subdirs机制把整个QXlsx作为一个子项目编译成静态库。我推荐第二种维护起来省心。如果你用qmake在.pro文件里这样配置include($$PWD/QXlsx/QXlsx.pri) CONFIG static使用CMake的话可以直接add_subdirectory或者把QXlsx源码编译成静态库再链接。需要注意QXlsx依赖Qt的Gui、Core模块如果你的工程还用到网络或打印不要忘了加模块依赖。2.2 一张表看懂QXlsx常用类在使用前我把QXlsx里最常用的几个类和用途列个表方便你自己对照类名主要作用典型用法Document打开/创建Excel文档Document doc(test.xlsx)Worksheet工作表的读写入口doc.worksheet(Sheet1)CellRange单元格区域sheet-selectedRange()Cell单元格数据cell-value()Format单元格样式cell-format()2.3 编译时常见的三个坑坑一QXlsx 在MSVC环境下需要把源代码文件里的UTF-8编码确认一下否则中文会乱码。解决方案是统一用UTF-8 without BOM保存源文件并在.pro里添加QMAKE_CXXFLAGS /utf-8。坑二MinGW环境下偶尔会出现moc文件找不到的问题一般是在pro里没有正确添加QT core gui或者QXlsx的pri文件路径写错了。建议用绝对路径include别用相对路径。坑三链接时报一堆undefined reference多半是因为没有把QXlsx的源码加进工程里。如果你有动态库libQXlsx.a右键项目-添加库即可但注意release和debug库要区分开。3. 核心代码实现读取Excel并显示到QTableWidget3.1 完整读写代码框架先贴一个完整的基础版本读取“D:/test.xlsx”的第一个工作表把内容填充到QTableWidget中#include QApplication #include QTableWidget #include QHeaderView #include QFileDialog #include xlsxdocument.h #include xlsxworksheet.h void loadExcelToTable(const QString filePath, QTableWidget *table) { QXlsx::Document xlsx(filePath); if (!xlsx.load()) { return; // 文件打开失败可能是格式不对或文件损坏 } QXlsx::Worksheet *sheet xlsx.worksheet(Sheet1); if (!sheet) { sheet xlsx.worksheet(0); // 如果Sheet1不存在取第一个工作表 } int rowCount sheet-dimension().lastRow(); // 最大行 int colCount sheet-dimension().lastColumn(); // 最大列 table-clear(); table-setRowCount(rowCount); table-setColumnCount(colCount); for (int row 1; row rowCount; row) { for (int col 1; col colCount; col) { QXlsx::Cell *cell sheet-cellAt(row, col); QString value; if (cell) { value cell-value().toString(); } QTableWidgetItem *item new QTableWidgetItem(value); if (row 1) { // 第一行当作表头加粗显示 QFont font item-font(); font.setBold(true); item-setFont(font); } item-setFlags(item-flags() ~Qt::ItemIsEditable); // 只读 table-setItem(row - 1, col - 1, item); } } }这段代码的核心逻辑是通过xlsx.dimension()拿到工作表的有效范围再用cellAt逐个取单元格转成QString后塞进QTableWidgetItem。第一行特殊处理为表头加粗其余单元格设为只读。3.2 解析单元格数据时注意类型转换QXlsx中cell-value()返回的是QVariant空单元格QVariant是invalid直接toString()没问题但日期类型会变成浮点串。日期通常需要特殊处理if (cell-isDateTime()) { QDateTime dt cell-dateTime(); value dt.toString(yyyy-MM-dd HH:mm:ss); } else if (cell-value().type() QVariant::Double) { double num cell-value().toDouble(); if (qAbs(num - qRound(num)) 1e-6) { value QString::number(qRound(num)); // 去掉多余的.0 } else { value QString::number(num); } } else { value cell-value().toString(); }为什么不直接用toString因为Excel里如果单元格是数字3QXlsx会返回QVariant(3.0)toString结果是“3”倒还好但遇到保留多位小数时就会带出一长串浮点尾巴让人摸不着头脑。实测下来统一用format处理最稳。3.3 设置列宽比例自适应内容数据加载后表格默认的列宽很窄手动拖拽费劲。可以根据内容长度设置一个合适的列宽// 设置列宽比例让TableWidget按内容自适应宽度 int colCount table-columnCount(); for (int col 0; col colCount; col) { int maxWidth 80; // 最小宽度 for (int row 0; row table-rowCount(); row) { QTableWidgetItem *item table-item(row, col); if (item item-text().length() maxWidth) { maxWidth item-text().length(); } } table-setColumnWidth(col, maxWidth * 8 10); // 估算每个字符占8像素 }如果想让某一列占满剩余空间可以配合header()-setStretchLastSection(true)或者手动设置列宽比例table-horizontalHeader()-setSectionResizeMode(QHeaderView::Interactive); table-horizontalHeader()-setStretchLastSection(true);比如有5列想让第3列占满剩余空间就这么设置先把所有列resize模式设为Interactive或Fixed再用setColumnWidth(2, 200)给个基准值最后setStretchLastSection(true)即可。3.4 弹出对话框选择文件这个功能最简单也是基础中的基础。很多新手会忘记过滤文件类型导致误选非Excel文件。标准写法QString filePath QFileDialog::getOpenFileName( this, tr(选择Excel文件), D:/, tr(Excel文件 (*.xlsx *.xlsm);;所有文件 (*.*)) ); if (!filePath.isEmpty()) { loadExcelToTable(filePath, ui-tableWidget); }注意这里有个小坑如果你的程序是在64位系统下运行QFileDialog的默认路径如果不存在会弹出错误提示。稳妥做法是判断一下目录是否存在QString defaultDir D:/; if (!QDir(defaultDir).exists()) { defaultDir QDir::homePath(); }3.5 大文件加载性能优化实录这是整个功能里最让人头疼的部分。两三万行的Excel加载到QTableWidget如果不做任何优化界面会卡好几秒甚至十几秒用户体验极差。优化策略我按优先级排列策略一加载前关闭UI更新table-setUpdatesEnabled(false); // ... 插入数据 ... table-setUpdatesEnabled(true);这个操作能省掉大量重绘开销实测5万行数据从卡顿7-8秒降到1-2秒。策略二设置table-setSortingEnabled(false)排序功能在加载数据时千万别开否则每插一行就触发一次全表排序性能灾难。策略三按需加载这个最实用。Excel几千行能全部加载但如果是几万行的数据UI这么操作显然不现实。一种做法是只加载前N行作为预览状态栏提示“已加载前10000/58432行”。策略四使用QTableWidget::setVerticalScrollMode(QAbstractItemView::ScrollPerPixel)滚动更丝滑但这只是视觉优化。策略五如果数据量真的巨大几十万行建议直接放弃QTableWidget改用QTableView配合QAbstractTableModelModel/View架构天然支持按需加载配合QSortFilterProxyModel还能做筛选。我这里凡是单表超过5万行的Excel都自动切换到QTableView模式。下表是我在i5-12400 DDR4 16G内存机器上测试的加载时间对比行数未优化耗时关闭更新禁用sorting按需加载前1万行5000行1.8s0.72s0.65s20000行6.5s2.1s0.85s100000行38s12.3s1.1s想都不敢想30万行的Excel如果不优化界面直接失去响应30秒以上。按需加载就是王道。4. 常见问题排查与实战避坑4.1 常见问题速查表现象可能原因解决方案打开xlsx文件后无任何反应文件损坏、路径不对、QXlsx未加进编译用Excel重新另存一份检查Document构造是否带路径中文乱码源文件编码不是UTF-8设置QMAKE_CXXFLAGS/utf-8或统一用UTF-8保存数字变成3.0000001QVariant浮点转换问题按数值类型手动四舍五入或先用qRoundQTableWidget加载卡死大数据量未优化关闭updatesEnabled、禁用sorting、按需加载某些单元格显示为####Excel里列太窄单元格内容溢出QXlsx读取的原始值并不受影响无需处理合并单元格读取错位QXlsx对合并单元格支持有限读值时把原区域的左上角值复制到所有合并单元格程序发布后运行时崩溃缺少QXlsx动态库部署时把QXlsx相关dll或lib一起打包4.2 合并单元格的坑与解决思路QXlsx对合并单元格的处理不太智能cellAt(row, col)在合并区域的非左上角位置返回的Cell可能是空的。处理方法QXlsx::CellRange range sheet-mergedCells(); // 遍历合并区域把左上角的 val 赋给区域内所有单元格 for (auto r : range) { QString topLeftValue sheet-cellAt(r.firstRow(), r.firstColumn())-value().toString(); for (int row r.firstRow(); row r.lastRow(); row) { for (int col r.firstColumn(); col r.lastColumn(); col) { QXlsx::Cell *cell sheet-cellAt(row, col); if (!cell || cell-value().isNull()) { sheet-write(row, col, topLeftValue); } } } }注意sheet-write改的是内存中的数据不影响原文件方便。4.3 列宽自动调整后竖排文字还是显示不全Excel里有些单元格是竖排文字format设置了文本旋转这种单元格在QTableWidget里不跟随Excel样式即使列宽调到很大文字是横着显示但内容会截断。建议做法读取时不检测旋转格式只在表格里全部横向显示如果确实需要Excel样式可以考虑用自定义Delegate绘制单元格内容。4.4 关于“未检测到Microsoft Excel的有效版本”之类提示这通常是你在机器上装了基于COM的Excel操作库或相关插件而你的Qt程序实际用的是QXlsx不依赖Office所以这个提示跟QXlsx无关。如果程序运行到这里弹出这个提示多半是系统里别的软件在搞鬼跟你的工程无关忽略即可。4.5 xls与xlsx的区别QXlsx只支持xlsx、xlsm这样的Office Open XML格式不支持老式的xlsBIFF格式。如果拿xls文件喂给QXlsx轻则打开失败重则解析出乱码。让用户把文件另存为xlsx再拖进来是最稳妥的方法。如果你要做xls兼容只能引入QAxObject仅Windows或LibreOffice的转换能力。5. 功能扩展筛选、排序与样式美化5.1 给表格加筛选功能显示数据只是第一步绝大多数用户希望能在表格里快速筛选。QTableWidget本身不带筛选功能但我们可以用QSortFilterProxyModel结合QTableView实现。如果坚持用QTableWidget只能自己写循环遍历行、隐藏不符合条件的行void filterRows(QTableWidget *table, int col, const QString keyword) { for (int row 0; row table-rowCount(); row) { QTableWidgetItem *item table-item(row, col); bool isMatch item item-text().contains(keyword, Qt::CaseInsensitive); table-setRowHidden(row, !isMatch); } }虽然QTableWidget的setRowHidden非常方便但数据量上万时效率不高因为每次都要遍历所有行。如果有几千行这个方案完全够用。5.2 样式美化做一个类似Excel的界面主要设置三块表头背景table-horizontalHeader()-setStyleSheet(QHeaderView::section { background-color: #f0f0f0; border: 1px solid #dcdcdc; padding: 4px; });斑马纹可以用setAlternatingRowColors(true)。选中行高亮默认的蓝色高亮在Excel里非常经典不用改。如果你想让选中单元格不加粗、不变色可以设置setSelectionBehavior(QAbstractItemView::SelectRows)让用户更容易观察到所在行。5.3 导出功能既然能读那自然要能写。QXlsx同样可以导出基础写法QXlsx::Document xlsx; for (int row 0; row table-rowCount(); row) { for (int col 0; col table-columnCount(); col) { QTableWidgetItem *item table-item(row, col); QString text item ? item-text() : QString(); xlsx.write(row 1, col 1, text); } } xlsx.saveAs(export.xlsx);如果是导出大表格用QXlsx写几万行性能还过得去但格式、合并单元格需要额外设置工作量一下子上来了。更省事的方案是导出CSVExcel也能打开但中文要记得用带BOM的UTF-8QTextStream加上QStringConverter或直接写\xEF\xBB\xBF头否则Excel打开会乱码。5.4 一个能扛大数据的升级方案如果项目确实需要处理几十万行数据建议改造为QTableView 自定义Model。QXlsx读取一次内存都放在QList 里Model的data()方法按需返回界面只保留可见区域的绘制开销。好处是滚动流畅坏处是排序时数据在内存里依然会卡但比QTableWidget好得多。我最后做了个模式切换按钮普通表格“预览模式”用QTableWidget专业统计“超级模式”用QTableView 虚拟Model用户根据实际数据量自己切换体验很好。6. 写在最后的一些体会整套Qt QXlsx读写Excel的方案我从最早简单的Demo到后来成熟的桌面应用前后迭代了好几个版本。最大的感受是库的选择决定了项目下限QXlsx虽然不是官方库也没有LibXL那些商业库功能全但胜在免费、开源、轻量做常规数据搬运足够。真正拉开体验差距的是你是否做了数据量性能优化和错误处理什么库都救不了不好好设计的UI卡顿。如果你也想做类似功能建议先把单个Excel文件读取并显示这件事跑通再逐步优化性能最后再考虑各种用户友好细节。不要一开始就追求完美否则容易陷进去调库调到头大。我踩过的最大一个坑其实是乱改源码导致后面升级QXlsx版本时一堆冲突因此建议你始终保持QXlsx源码干净只通过上层接口调用将来更新版本时平滑迁移。QTableWidget QXlsx这套组合在大多数中小型数据管理工具里完全够用也希望这篇分享能帮你少走弯路。本文还有配套的精品资源点击获取

相关新闻