简介:一份基于Qt6框架的PDF阅读器完整工程,面向C++开发者、Qt学习者以及需要高效查阅电子文档的办公人群。项目实现了标签定位、页码跳转和关键字搜索三大核心功能:标签功能可在多文档间快速切换;页码功能支持输入数字直接跳转至目标页;搜索功能能识别复杂查询语句并提供智能建议,显著提升信息检索效率。资源共61个文件,压缩包大小84.12MB,主要包含cpp和h源码文件、ui界面定义文件、qrc资源文件,另有pdf说明文档、exe可执行程序及dll动态库等,便于直接运行体验或二次开发。已有239人学习使用。通过学习该工程,可以掌握Qt6环境下PDF页面渲染、自定义标签管理、文本搜索高亮以及界面布局的实现思路,可快速改造成适配个人习惯的阅读工具,也可作为毕业设计或课程项目的参考资料。
1. QT6 PDF阅读器:一个工程把标签、页码定位和关键字搜索串起来
QT6 PDF阅读器这个工程,解开压缩包的那一刻你就能猜到它是为“快速定位”设计的。它不是那种只能翻页的简易阅读器,而是同时给了你三条找内容的路径:标签定位、页码直达、关键字搜索。对经常要同时摊开十几个PDF文档、在几百页里找某一段话的人来说,这三条路径意味着每天少点几十次鼠标。它适合两种人:一类是准备用QT6做桌面工具、想要一个能跑的完整参考工程的开发者,另一类是正在被“PDF文档分散、定位靠肉眼”折磨的文档处理从业者。前者能拆出GUI、渲染、搜索的完整写法,后者可以直接把它当成顺手的工作工具来用。
2. 从源码包到可运行工程:QT6选型与项目结构解读
2.1 为什么选QT6而不是QT5:界面与渲染库的边界条件
做PDF阅读器,QT版本的选择直接影响两条线:渲染能力和控件生态。QT6在QPainter、QOpenGLWidget、QTextDocument这几个和文档渲染强相关的模块上做了清理和重写,渲染高DPI PDF时的清晰度、滚动流畅度都比QT5有明显改善。如果再考虑HiDPI屏幕,QT6从底层就支持了缩放策略的自动感知,不用像QT5那样在main函数里反复手动设置缩放因子。
这个工程用的是VS的Qt插件工程格式,后缀是vcxproj、filters、sln,说明在Windows上拉起来就能编。进入工程之前要注意一个边界条件:PDF解析和渲染依赖的底层库是QPDF(业界常用的C++ PDF处理库),也就是说,QT6只负责界面和事件循环,真正的PDF解释、页面光栅化、文本提取是QPDF在做。弄清楚这个边界,后面调试“页码跳了但页面没变”“搜索有结果但不高亮”这类问题时,才知道该去界面层找原因还是去渲染层找原因。
注意:如果编译时遇到“找不到PDF库头文件”,先查的不是QT环境,而是QPDF库的包含路径和链接路径是否已经写进vcxproj的附加依赖项。
2.2 源码包目录拆解:接口层、控件层、资源层各干什么
解开压缩包后,第一眼会觉得文件有点散,但按职责划分其实很清楚。我把核心文件按层摊开来看:
| 文件 | 层 | 职责 |
|---|---|---|
| zxPDFReaderWidget.h / .cpp | 控件层 | 阅读器主控件,负责PDF加载、翻页、页面渲染、标签栏和搜索面板的交互 |
| zxPDFReaderWidget.ui | 界面定义层 | 用designer拖出来的界面布局,标签栏、页码输入框、搜索框都在这里定义 |
| searchresultdelegate.h / .cpp | 控件层 | 搜索结果列表的绘制委托,负责命中关键词的高亮显示 |
| zoomselector.h / .cpp | 辅助控件层 | 缩放比例选择器,支持预设百分比和自定义输入 |
| QtWidgetsApplication1.* | 应用入口层 | 承载阅读器控件的宿主窗口 |
| resources.qrc | 资源层 | 管理工具栏图标、样式文件等资源的索引 |
| main.cpp | 入口层 | 启动应用、初始化QApplication、加载主窗口 |
这个分层结构值得照着拆。zxPDFReaderWidget是核心控件类,它把阅读器的所有能力都收口到一个自定义控件里,QtWidgetsApplication1窗口只是这个控件的容器。这样设计的好处是:你以后想把这个阅读器嵌入别的界面,不需要动阅读器内部,只要在目标窗口里加一句addWidget就能复用;想单独跑一个PDF阅读器窗口,也可以用这个控件做独立窗口。
main.cpp里做的是标准的QT6初始化,同时会做PDF渲染库的全局配置。常见做法是在main函数里设置PDF文件路径参数、指定默认缩放模式、初始化翻译等信息。我在实际拆这个工程时,习惯先把main.cpp和zxPDFReaderWidget.ui对照着看:ui里定义了那些控件,代码里就会对应地出现findChild或connect调用,这一步能快速建立起“界面元素到代码逻辑”的映射。
界面加载之后,zxPDFReaderWidget的构造函数会依次做几件事:初始化PDF解析库、创建空标签页容器、连接页码输入框的跳转信号、初始化搜索面板。注意构造函数里对PDF库的初始化必须放在控件绘制之前,否则页面区域第一次重绘时可能拿不到渲染上下文,表现为“窗口出来了但页面白屏”。
2.3 工程配置里容易被忽略的两个开关
在vcxproj文件里,QT6工程的配置有两项影响运行行为:一是“Qt Installation”路径指向,二是编译器工具集版本。前者决定你用哪个版本的QT6运行时,后者决定链接的是哪个MSVC运行库。换一台电脑编译时,这两项最容易不一致,表现就是“代码没问题,但一跑就找不到Qt6Pdf.dll”或者“Debug能跑、Release编译不过”。
另一个容易被忽略的是这个工程把UI文件(.ui)放进了zxPDFReaderWidget模块里,而不是放在主程序模块里。想在Qt Designer里直接拖拽修改阅读器界面的,要正确加载zxPDFReaderWidget.ui工程,如果打开的是主程序工程下的窗口文件,会发现连标签栏的影子都找不到。
3. 标签与页码定位:界面信号槽与页面坐标换算的实现
3.1 标签语义:文档、页码、命名词条如何绑定
标签系统解决的核心问题,是让“第2份文档的第37页”这种语义找到落点。不要简单地把标签想成一个书签字符串,它在代码里是一个数据结构,至少保存三样东西:源文档标识、目标页码、用户可见的标签名。源文档标识用来在标签被点击时定位到正确的文档实例,页码用来触发跳转,标签名是给用户看的。
打开文档并创建标签的核心代码如下:
void zxPDFReaderWidget::addPdfTab(const QString &filePath, const QString &labelName) { // 1. 创建并解析PDF文档 QSharedPointer<PdfDocument> doc = QSharedPointer<PdfDocument>::create(); QPdfDocumentError err = doc->load(filePath); if (err != QPdfDocumentError::None) { qWarning() << "PDF加载失败:" << filePath; return; } // 2. 在标签栏里插入一个标签项 QListWidgetItem *item = new QListWidgetItem(m_pDocTabList); item->setText(labelName.isEmpty() ? QFileInfo(filePath).fileName() : labelName); item->setData(Qt::UserRole, filePath); // 绑定文档路径 item->setData(Qt::UserRole + 1, 0); // 绑定初始页码,第1页记为0 // 3. 维护文档索引 m_docList.append(doc); }第2步里用setData把文档路径和初始页码挂到QListWidgetItem上,这是标签栏与文档绑定的关键。Qt::UserRole保存文档路径,Qt::UserRole + 1保存当前页码,后续点击标签时,通过item->data()就能取回这两个值。PDF文档对象单独维护在m_docList里,标签项里只存索引路径,不直接持有文档对象,好处是避免文档被多个界面元素引用后出现生命周期混乱。
标签跳转的槽函数需要额外处理一个问题:多个标签可能对应同一个文档的不同页面。要判断目标文档是否已经打开在阅读器里,如果已打开,直接切换页码;如果未打开,则重新加载并定位。这个分支很容易漏,很多第一次接触标签系统的人会把每个标签都当成一个独立文档去加载,结果同一份PDF在内存里被加载了十几次,滚动和搜索都卡。
3.2 页码输入跳转与QPDF坐标换算
页码定位的UI是页码输入框,用户输入“37”后按回车,阅读器跳转到第37页。这个交互看似简单,实际上要处理的是坐标体系的换算——PDF文档内部的页码是从0开始的,而用户看到的页码是从1开始的。
void zxPDFReaderWidget::onPageJumpRequested(int displayedPage) { // 用户输入的页码从1开始,内部索引从0开始 int documentIndex = displayedPage - 1; // 边界保护:超出有效页码范围时直接忽略 if (documentIndex < 0 || documentIndex >= m_currentDoc->pageCount()) { qWarning() << "页码越界:" << displayedPage; return; } // 执行页码切换 m_currentDoc->setPageIndex(documentIndex); // 根据当前缩放比例刷新渲染 m_pdfView->setPage(documentIndex); m_pdfView->update(); // 同步标签栏里记录的当前页 int tabIndex = m_pDocTabList->currentRow(); m_pDocTabList->item(tabIndex)->setData(Qt::UserRole + 1, documentIndex); }括号里注释已经点明:displayedPage是用户视角页码,documentIndex是内部页码,两者永远差1。这个换算不复杂,但它是页码功能里出现bug最多的位置。一旦在某个环节忘记减1,表现就是“输入第37页,打开的却是第38页的内容”,而且这种bug在文档实际只有80%内容可见时特别难发现。
参数上需要注意setPageIndex和setPage的区别。setPageIndex是文档层的页面索引切换,setPage是渲染视图层的页面显示切换。只调前者不调后者,文档内部页码变了但屏幕上还是旧页面;只调后者不调前者,界面跳到新页面但内部状态没更新,后续做关键字搜索时会按旧页码去找内容。
页面渲染再往后一步,是坐标映射问题。PDF内容在页面内是有完整坐标体系的,渲染出来的图像需要被缩放到控件宽高内。工程里zoomselector就是为了解决缩放比例的:它把百分比数字转换成渲染缩放系数,缩放系数直接决定QPdfView渲染时用到的变换矩阵。缩放比例不只是一个视觉参数,它还影响“从关键字搜索命中到页面跳转后,命中文本在屏幕上的显示位置”。
3.3 从标签栏点击到页面重绘:信号槽链路
标签栏点击到页面刷新的完整链路是:QListWidget::itemClicked信号 →onTabItemClicked槽函数 → 从item的数据区取文档路径和页码 → 调用loadDocument或setPageIndex→ 触发QPdfView的重绘事件 → 渲染新页面。
connect(m_pDocTabList, &QListWidget::itemClicked, this, &zxPDFReaderWidget::onTabItemClicked); void zxPDFReaderWidget::onTabItemClicked(QListWidgetItem *item) { if (!item) return; QString filePath = item->data(Qt::UserRole).toString(); if (m_currentDoc->documentPath() != filePath) { // 目标文档与当前显示的文档不同,先加载文档 loadPdfDocument(filePath); } int pageIndex = item->data(Qt::UserRole + 1).toInt(); m_currentDoc->setPageIndex(pageIndex); m_pdfView->setPage(pageIndex); m_pdfView->update(); }注意节点是m_currentDoc->documentPath() != filePath这个判断。如果同一份文档被用户从不同目录打开过,可能存在路径语义相同但字符串写法不同的情况,比如D:/docs/a.pdf和D:\\docs\\a.pdf。统一走一次QDir::cleanPath再做比较,可以避免这种边角比较失误。
标签栏的动态语义还能继续扩展。工作中有个稍微进阶的用法:给标签项设置不同颜色的前景色来表示文档状态——绿色表示“已读完”,黄色表示“正在读”,灰色表示“未开始”。这个做法不需要改数据结构,只要在标签项的setForeground里根据文档状态更新颜色即可,对多文档并行阅读的场景非常实用。
4. 关键字搜索:从高亮命中列表到页面精准跳转
4.1 搜索输入框与结果列表:从字符串触发到delegate绘制
PDF阅读器的搜索,看起来是“输入关键词→出结果列表”,但落到QT6里至少需要四个组件的协作:搜索输入框负责接收文本、搜索面板负责组织搜索流程、搜索结果列表负责展示命中项、delegate负责在列表里绘制高亮效果。
searchresultdelegate.cpp承担了高亮绘制工作。它重写了paint方法,把命中关键词的文本区域绘制成带背景色的样式。
void SearchResultDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); // 从model中取出该条目保存的原始文本 QString fullText = index.data(Qt::DisplayRole).toString(); QString keyword = index.data(Qt::UserRole).toString(); if (keyword.isEmpty()) { QStyledItemDelegate::paint(painter, opt, index); return; } painter->save(); // 绘制默认背景 painter->fillRect(opt.rect, opt.palette.window()); // 找到关键词在整条文本中的位置 int hitPos = fullText.indexOf(keyword); if (hitPos >= 0) { // 绘制三个区域:命中前、命中段、命中后 QRect textRect = opt.rect.adjusted(4, 2, -4, -2); QFont font = opt.font; // 命中前文本 QString beforeText = fullText.left(hitPos); painter->setFont(font); painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, beforeText); // 命中段 QString hitText = fullText.mid(hitPos, keyword.length()); QRect hitRect = textRect; int beforeWidth = QFontMetrics(font).horizontalAdvance(beforeText); hitRect.setLeft(hitRect.left() + beforeWidth); painter->fillRect(hitRect, QColor(255, 230, 100)); painter->drawText(hitRect, Qt::AlignLeft | Qt::AlignVCenter, hitText); // 命中后文本 QString afterText = fullText.mid(hitPos + keyword.length()); int hitWidth = QFontMetrics(font).horizontalAdvance(hitText); QRect afterRect = hitRect; afterRect.setLeft(afterRect.left() + hitWidth); painter->drawText(afterRect, Qt::AlignLeft | Qt::AlignVCenter, afterText); } else { QStyledItemDelegate::paint(painter, opt, index); } painter->restore(); }这里的关键逻辑是indexOf定位与三段绘制。QStyleOptionViewItem是QT6中绘制list item的标准参数,opt.rect是当前条目在视图中占的矩形区域。绘制命中文本前,先用QFontMetrics.horizontalAdvance计算命中前文本的像素宽度,从而把命中区域平移正确的偏移量,保证背景色刚好覆盖在关键词正下方。
如果搜索结果是直接从PDF页面文本提取的,还要注意一个情况:PDF文本提取的字符间可能夹带着不可见的控制字符或换行符,导致indexOf明明存着关键词却匹配不中。工程里在提取页面文本后,做了空白字符归一化处理,把\u00A0、连续空格和换行替换成普通空格,同时记录原始文本与归一化文本的字符偏移映射。有了这个映射,搜索时用归一化文本匹配,跳转时通过偏移映射找原始文本位置,两个方向都不丢信息。
4.2 命中策略与页码回跳:搜索循环里最容易被忽略的边界
搜索的完整流程可以抽象成三个动作:遍历当前文档的所有页面、在每页提取文本并匹配关键词、把命中页码和上下文片段写入结果列表。
void zxPDFReaderWidget::performSearch(const QString &keyword) { m_searchResultList->clear(); for (int pageIndex = 0; pageIndex < m_currentDoc->pageCount(); ++pageIndex) { QString pageText = m_currentDoc->extractPageText(pageIndex); if (pageText.contains(keyword, Qt::CaseInsensitive)) { // 截取命中位置前后的上下文,作为列表里展示的文本 int pos = pageText.indexOf(keyword, 0, Qt::CaseInsensitive); int start = qMax(0, pos - 15); int end = qMin(pageText.length(), pos + keyword.length() + 15); QString snippet = pageText.mid(start, end - start); snippet.replace('\n', ' '); QListWidgetItem *item = new QListWidgetItem(m_searchResultList); item->setText(snippet); item->setData(Qt::UserRole, pageIndex); item->setData(Qt::UserRole + 1, keyword); // 记录最大命中数,用于状态栏提示 m_lastHitCount++; } } }循环里最容易被忽略的边界有三个。第一个是页码从0开始遍历,结果列表里Qt::UserRole存的是从0开始的页索引,但用户看结果时习惯认为“第1页”,列表项上直接显示0会让人困惑,需要在展示时加1。第二个是大小写敏感问题。Qt::CaseInsensitive适用于英文和部分拉丁文场景,但中文和日文文本受大小写影响不大,多语言文档环境下建议统一用Qt::CaseInsensitive,避免中文文档里因为大小写转换出沙特歧义。
第三个是跨页搜索的上下文截断。用户常遇到“搜到一段话,但列表里只显示15个字,看不出是不是自己要找的那句”。这里有两个优化方向:一是把上下文截断窗口从固定的15个字符改成按句子边界截取,遇到句号、分号就断;二是在列表项上增加页码前缀,显示成“第12页:这两台设备之间的同步策略…”,让用户先看页码定位再确认上下文。
命中结果的页码回跳也做了保护:点击结果列表项后,会先从Qt::UserRole取出页索引,执行setPageIndex(pageIndex),紧接着做一次m_pdfView->setPage(pageIndex),最后把标签栏里记录的当前页同步掉。三步动作缺一不可,缺了任何一步都可能出现“搜索结果列表显示的是第12页,阅读器内容停在原页”。
4.3 把搜索做得更顺手:历史建议与多词组合
摘要里提到这个工程会根据搜索习惯提供建议。落到实现上,常见做法是把用户每次成功触发的搜索词记录到本地配置里,下次点击搜索框时弹出下拉建议。QT6里可以用QCompleter搭配QStringListModel,把最近搜索词灌进过滤模型,再设置setCompletionMode(QCompleter::PopupCompletion)实现联想下拉。
多词组合搜索是我拆这个工程时顺手加的一个扩展。PDF里的业务文档经常出现“设备型号 + 日期 + 状态”这样的组合查询,比如“A823 AND 2024-01 AND 故障”。实现方式是先把关键词按空格拆解,再对提取的页面文本依次做contains判断,只有所有子关键词都命中的页面才算是真命中。注意拆词和中文黏着文本之间的冲突——取词时不要按整个空格硬切,遇到“A823”这种带数字的短词,要配合正则做词边界判定,避免把“A8230”错误匹配成“A823”。
高亮跳转到页面后,屏幕上的命中文本位置还值得再做一件事:根据命中文本的坐标执行一段偏移滚动,让命中内容出现在窗口中部而不仅是最顶部。实现思路是先从QPdfDocument拿到命中文本的bounding box,再把该区域中心坐标换算成视图坐标,用verticalScrollBar()->setValue()滚动过去。这一步能让搜索使用体验从“能跳”上升到“跳得准”,操作量不大,但可感知的提升非常明显。
5. 编译与运行避坑:模式不匹配、中文路径、工具栏丢失排查
5.1 Debug编译通过,Release链接直接炸
现象:Debug配置下程序能正常运行,切换到Release配置后,编译报出一堆无法解析的外部符号,指向QPDF库的各个接口。
原因:这是QT6 + VS环境下最典型的配置问题。工程在Debug模式下链接的是qpdfd.lib(带d后缀的调试库),Release模式下要链接qpdf.lib。vcxproj条件配置里,如果只写了Debug的附加依赖项没有写Release的,或者写死了绝对路径指向了某个Debug库文件,一换配置就会链接失败。另一个常见原因是编译器工具集版本不一致——Debug用了v143工具集编译的库,Release却链接了v142工具集的库,二者导出的符号修饰方式不同,同样会报未解析。
解决:打开zxPDFReaderWidget.vcxproj,检查Linker -> Input -> AdditionalDependencies,确认Debug和Release各有一份独立的库文件名,且库文件路径指向的PDB版本和当前工程一致。建议直接使用QT6的QtPdf模块库+系统自带链接方式,避免手动维护QPDF的Debug/Release双份依赖。顺手在vcxproj.user里确认一下QtInstallPath指向的是同一个QT6目录,不要在debug用一套、release用另一套。
5.2 页码跳转无效:所有PDF都停留在第1页
现象:输入任意页码回车,窗口底部状态栏显示“已跳转”,但阅读区域的内容始终停在第1页。
原因:先说结论,这是信号槽连接断链了。工程里页码输入框的信号是QLineEdit::returnPressed,在ui文件里连着onPageJumpRequested。如果某个改造环节中,用代码新建了一个QLineEdit但没有手动connect这个信号,或者界面重新setupUi时把原先的连接覆盖掉了,键盘回车的信号没有进入槽函数。还有一种情况是信号确实触发了槽函数,但槽函数里先做了文档加载判断,而加载判断的条件写反了,导致每次都走“加载新文档”分支,页面被重置到第1页。
解决:断点打在onPageJumpRequested入口,第一件事确认槽函数有没有被调用。没调用,检查connect连接;被调用了,看m_currentDoc->pageCount()的返回值,如果返回0说明文档加载本身失败了,页面页数未知自然无法跳转。再把断点移到m_pdfView->setPage(pageIndex)这一步,确认页码索引是目标值还是恒等于0。
5.3 英文能搜到,中文全是“未找到”
现象:文档是中文版,搜索“设备”返回0条;换成英文关键词能搜到,所有中文文本匹配全部失败。
原因:这类问题在QT6下要分两层查。第一层是PDF文档本身的中文编码——如果PDF里的中文字符使用的是CID编码而非Unicode映射,页面文本提取后在QLabel或list里能正常显示,但底层的字符串内容可能包含特殊的字体映射标记,QString::contains进行精确匹配时匹配不上。第二层是QT6的QString::contains对UTF-8和UTF-16的转换差,如果在进行indexOf之前没有把搜索引擎拿到的QString统一转成Qt::CaseSensitive匹配模式,某些中文标点或全角字符会干扰匹配结果。
解决:先做字符一致性校验。对所有页面提取出来的文本,统一走一遍QString::normalized(QString::NormalizationForm_C),把兼容性字符规范到统一码点。然后在搜索入口打印调试日志,输出第一个字符的实际码点。如果确认是CID编码问题,那么只能退回到用PDF文本层的“选取文本”特性做搜索,或者引入文本抽取层,在打开文档时把所有页面文本预抽成UTF-8的纯文本索引。第二种方案多花几十兆内存,但搜索稳定性会提升一个级别。
5.4 更换Qt版本后工具栏图标消失
现象:把工程从QT6.2迁移到QT6.5后,代码编译正常,但工具栏上的按钮全部变成空白方块。
原因:resources.qrc里的图标路径没变,变的是QT6版本之间资源编译器的行为。QT6.5对qrc文件里的相对路径解析更严格,原先能容忍的./images/open.png写法在新版本里可能解析失败。另一种情况是图标文件命名里含有大写字母或空格,在Windows下编译期没问题,但运行时资源引用大小写敏感导致查找失败。
解决:打开resources.qrc检查所有路径统一使用反斜杠或正斜杠,不要混用;图标文件名改成全小写字母加下划线;删除构建目录下的资源临时文件重新编译一次。如果图标还是没有,就在main.cpp里手动追加一次QDir::setCurrent(app.applicationDirPath()),排除掉当前目录不对导致qrc资源加载不到的情况。
注意:以上四条排查经验都来自我实拆这个工程的过程,每一条的第一个排查点都先怀疑QT6的坐标和字符串处理,不要急着怀疑逻辑正确性。界面层和渲染层的问题往往出在最基础的数据一致性上。
6. 把阅读器改顺手:页面渲染缓存与二次开发技巧
这个工程能跑通只是起点,真正让它变成日常工具,还差一个关键优化——页面渲染缓存。PDF页面光栅化是非常吃CPU的操作,每次翻页都把整页重新渲染一遍,翻页速度会明显拖后腿。常见做法是给阅读器加一个LRU页面缓存,最多保留最近16页的渲染结果,翻页时先查缓存,命中就直接贴图,没命中再走文档渲染管线。
void zxPDFReaderWidget::tryRenderPageWithCache(int pageIndex) { QPixmap cached = m_renderCache.object(pageIndex); if (!cached.isNull()) { m_pdfView->setPixmap(cached); return; } QPixmap rendered = m_currentDoc->renderPage(pageIndex, m_currentZoom); m_renderCache.insert(pageIndex, rendered); m_pdfView->setPixmap(rendered); }参数m_currentZoom是当前缩放比例,插入缓存的key是页索引和缩放系数的组合,这样在不同缩放级别下来回切换时,不会误用低分辨率缓存。缓存对象用QCache管理,自动处理淘汰,不用自己维护计数。
对日常使用来说,页码显示还有一个体验细节值得加:把页签栏的“当前页/总页数”更新逻辑从硬编码改成响应pageChanged信号。QPdfView在pageChanged信号里带页码索引和总页数,同步到状态栏后,用户在翻页时能实时看到进度变化。缩放选择器那边,补充一个“适配宽”按钮,作用是读取当前视图宽度,反推出合适的缩放比例,这个功能在多列排版的PDF里尤其有用。
打包发布时,QT6程序依赖的DLL比QT5更多,不要只拷贝exe和图片资源。运行windeployqt时,加上--pdf参数把PDF支持模块一并带出,否则程序到了没有安装QT环境的机器上,一加载文档就崩。我一般把这一步写在发布脚本里,每次编译完都强制走一遍windeployqt、把缺失DLL记录在release notes里,从那以后几乎没有再被“换台机器跑不起来”的问题卡过。希望帮到你。
本文还有配套的精品资源,点击获取