资讯中心

Qt 富文本处理(03):QTextCursor 接口【来自官档的翻译】配 TaoToken 统一 Key 通道

📅 2026/9/26 12:03:57
Qt 富文本处理(03):QTextCursor 接口【来自官档的翻译】配 TaoToken 统一 Key 通道
1. 为什么 QTextCursor 值得单独拎出来讲如果你正在用 Qt 做富文本编辑器、日志查看器或者任何需要程序化改文档的功能QTextCursor 基本绕不开。它是 QTextDocument 的“程序化光标”能定位、能选区、能插文本、能改格式、能建表格和列表。官方文档把它放在 Rich Text Processing 的第三篇标题就叫 The QTextCursor Interface但说实话官档写得偏“接口罗列”很多开发者看完还是不知道从哪下手。这篇我按“能直接抄进项目”的思路来写先讲清楚 QTextCursor 是什么、适合谁再给一套可复制的接口骨架最后用 TaoToken 的统一 Key 通道跑一次接口验证把官档示例真正跑通。你不需要先啃完整个 Qt 文档跟着步骤走就行。QTextCursor 的核心价值在于它把“用户在编辑器里能做的操作”变成了代码能调用的接口。用户能选中一段字加粗你就能用 cursor 选中再 mergeCharFormat用户能插入表格你就能 insertTable。官档原话是“游标用于执行与用户能够在编辑器中创建自己的操作完全对应的编辑操作”这句话是理解整个类的钥匙。适合读这篇的人写过 QTextEdit 但没系统用过 QTextCursor 的需要批量生成富文本报告或日历的想在 Qt 里做自定义编辑器但被文档结构绕晕的。下面从环境准备开始。2. TaoToken 统一 Key 通道的前置准备这一节解决“验证接口时请求往哪发”的问题。QTextCursor 本身是本地 Qt 类不需要网络但官档示例跑通后你往往要接一个模型或接口做内容生成、翻译、格式建议。这时候如果每个服务都配一套 Key管理会很乱。TaoToken 提供统一 Key 通道一个 Key 走多个模型接口省去反复换配置。先拿 Key。打开官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册后在控制台创建 API Key。控制台地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite Key 管理页在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。API 基址统一用 https://taotoken.net/api 注意这个地址不带 UTM 参数配置里直接写它。注意Key 只存在服务端或本地环境变量里别硬编码进提交到仓库的源码。Qt 项目里可以用 qgetenv 读环境变量。如果你后面要做长期编码或 Agent 类任务可以看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。只是验证模型对话的话模型对话入口在 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 配置细节以文档为准。3. 可复制的 QTextCursor 接口骨架这一节是主体把官档里散落的接口按“插入类、选区类、结构类、格式类”四组整理成能直接用的代码。先建一个最小 Qt Widgets 工程.pro 里加QT widgets。3.1 获取游标的两种方式官档给了两条路从编辑器拿或直接从文档拿。区别是前者跟随用户当前光标后者是独立游标。// 方式一从 QTextEdit 获取当前用户光标 QTextEdit *editor new QTextEdit(); QTextCursor cursor(editor-textCursor()); // 方式二直接从文档创建独立游标位于文档开头 QTextDocument *document new QTextDocument(editor); QTextCursor cursor(document);方式二创建的游标默认在文档第一个空块的开头适合程序化生成内容。方式一适合响应用户操作。3.2 插入文本与块insertText 和 insertBlock 是最常用的两个。注意官档强调的一点一旦用某个字符格式插入过文本这个格式会成为该游标后续插入的默认格式直到你换格式。QTextCharFormat headingFormat; headingFormat.setFontWeight(QFont::Bold); headingFormat.setFontPointSize(16); QTextCharFormat plainFormat; QTextCharFormat emphasisFormat; emphasisFormat.setFontItalic(true); cursor.insertText(QStringLiteral(Character formats), headingFormat); cursor.insertBlock(); cursor.insertText(QStringLiteral(Text can be displayed in a variety of different character formats. ), plainFormat); cursor.insertText(QStringLiteral(We can emphasize text by )); cursor.insertText(QStringLiteral(making it italic), emphasisFormat);这里 headingFormat 插入后如果不换格式下一段也会是粗体 16 号。所以官档示例里每次 insertText 都显式带格式这是好习惯。3.3 选区与格式合并把第一行改成粗体、其他属性不变是官档的经典例子。关键是 movePosition 的 KeepAnchor 模式它相当于“按住 Shift 移动光标”形成选区。QTextDocument *document editor-document(); QTextCursor cursor(document); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor); QTextCharFormat format; format.setFontWeight(QFont::Bold); cursor.mergeCharFormat(format);mergeCharFormat 只改你指定的属性字体、字号这些没动的保持原样。如果你用 setCharFormat会把整个字符格式替换掉容易丢属性。这是踩过的坑优先用 merge。3.4 分组编辑与撤销粒度一连串操作想当成一次撤销用 beginEditBlock / endEditBlock 包起来。官档示例是选中一个单词cursor.beginEditBlock(); cursor.movePosition(QTextCursor::StartOfWord); cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); cursor.endEditBlock();注意别把太多操作塞进一个 block。官档明确提醒用户可能希望对撤销有更细的粒度控制。一个 block 对应一次 CtrlZ 比较合理。3.5 插入表格与列表insertTable 返回 QTextTable 指针之后通过 cellAt 拿单元格游标填内容。官档的排班表例子很典型QTextTableFormat tableFormat; tableFormat.setBackground(QColor(#e0e0e0)); QVectorQTextLength constraints; constraints QTextLength(QTextLength::PercentageLength, 16); constraints QTextLength(QTextLength::PercentageLength, 28); constraints QTextLength(QTextLength::PercentageLength, 28); constraints QTextLength(QTextLength::PercentageLength, 28); tableFormat.setColumnWidthConstraints(constraints); QTextTable *table cursor.insertTable(rows, columns, tableFormat); QTextCharFormat charFormat; QTextTableCell cell table-cellAt(0, 0); QTextCursor cellCursor cell.firstCursorPosition(); cellCursor.insertText(QStringLiteral(Week), charFormat);列表用 insertList嵌套列表靠 indent 递增QTextListFormat listFormat; if (QTextList *list cursor.currentList()) { listFormat list-format(); listFormat.setIndent(listFormat.indent() 1); } listFormat.setStyle(QTextListFormat::ListDisc); cursor.insertList(listFormat);3.6 插入框架与图像框架用 insertFrame插入后光标进入框架内部后续 insertText 都落在框架里。要回到框架外用之前记录的 lastCursorPosition。QTextFrame *mainFrame cursor.currentFrame(); cursor.insertText(QStringLiteral(before frame)); QTextFrameFormat frameFormat; frameFormat.setMargin(32); frameFormat.setPadding(8); frameFormat.setBorder(4); cursor.insertFrame(frameFormat); cursor.insertText(QStringLiteral(inside frame)); cursor mainFrame-lastCursorPosition(); cursor.insertText(QStringLiteral(after frame));图像必须先建 QTextImageFormatQTextImageFormat imageFormat; imageFormat.setName(QStringLiteral(:/images/advert.png)); cursor.insertImage(imageFormat);3.7 settings.json 配置示例如果你在 Qt 项目里通过外部配置读接口参数可以放一个 settings.json把 TaoToken 的基址和模型名集中管理。Key 不要写进文件用环境变量占位。{ api: { base_url: https://taotoken.net/api, api_key_env: TAOTOKEN_API_KEY, model: claude-sonnet-4-20250514, timeout_ms: 30000 }, editor: { default_font_family: Courier, default_font_size: 12 } }Qt 里用 QJsonDocument 解析这个文件api_key 从 qgetenv(TAOTOKEN_API_KEY) 取。这样换环境只改环境变量不动代码。4. 验证请求与成功结果接口骨架有了现在跑一次验证确认 QTextCursor 生成的内容能正常走通同时确认 TaoToken 通道可用。分两步先本地验证 QTextCursor 输出再发一次接口请求。4.1 本地验证 QTextCursor写一个最小 main.cpp生成一段带粗体标题和表格的富文本然后打印纯文本确认结构。#include QApplication #include QTextEdit #include QTextCursor #include QTextTable #include QDebug int main(int argc, char *argv[]) { QApplication app(argc, argv); QTextEdit editor; QTextCursor cursor(editor-textCursor()); cursor.movePosition(QTextCursor::Start); QTextCharFormat bold; bold.setFontWeight(QFont::Bold); cursor.insertText(QStringLiteral(Qt Cursor Demo), bold); cursor.insertBlock(); cursor.insertText(QStringLiteral(plain body)); QTextTableFormat tf; QTextTable *table cursor.insertTable(2, 2, tf); table-cellAt(0, 0).firstCursorPosition().insertText(QStringLiteral(A)); table-cellAt(0, 1).firstCursorPosition().insertText(QStringLiteral(B)); qDebug() editor.toPlainText(); return 0; }运行后输出应该是Qt Cursor Demo\nplain body\nA\tB这样的结构。如果表格内容没出现检查 insertTable 后光标位置是否被后续操作覆盖。4.2 发一次接口请求验证通道用 curl 验证 TaoToken 通道确认 Key 和基址配对了。请求体里 model 换成你控制台里可用的模型名。export TAOTOKEN_API_KEY你的Key curl -s https://taotoken.net/api/v1/messages \ -H x-api-key: $TAOTOKEN_API_KEY \ -H anthropic-version: 2023-06-01 \ -H content-type: application/json \ -d { model: claude-sonnet-4-20250514, max_tokens: 128, messages: [ {role: user, content: 用一句话说明 QTextCursor 的作用} ] }成功的话返回 JSON 里会有 content 数组第一项 text 字段就是模型回复。如果返回 401检查 Key 是否带上了、环境变量是否 export 成功。如果返回 404检查基址是不是写成了带路径的变体统一用 https://taotoken.net/api 。提示Qt 里发请求用 QNetworkAccessManager把上面的 header 和 body 照搬即可。注意 Qt 的 JSON 拼接用 QJsonObject别手拼字符串。5. 本篇常见错排查这一节列几个高频问题都是接口调用和配置层面容易卡住的。游标位置不对插入内容跑到文档末尾。新建的 QTextCursor(document) 默认在开头但从 QTextEdit 拿的 textCursor() 跟随用户当前位置。如果你想要固定位置先 movePosition(QTextCursor::Start)。官档示例里每次插入前都显式 move就是这个原因。mergeCharFormat 没效果。检查是否真的形成了选区。只 movePosition 不带 KeepAnchor 的话选区是空的merge 自然没作用。用 cursor.hasSelection() 打印确认。insertTable 后表格不显示。表格插入在当前块之后如果当前块是空的且你没插入任何文本表格可能看起来“消失”。先 insertBlock 或 insertText 再插表格。另外确认 tableFormat 的列约束总和不超 100%。insertFrame 后文本跑到框架外。插入框架后光标在框架内部后续 insertText 都在框架里。要出去必须用 mainFrame-lastCursorPosition() 把光标移回。官档这个细节很容易漏。接口返回 401 或 403。Key 没读到、Key 失效、或者 header 名写错。Anthropic 风格用 x-api-keyOpenAI 风格用 Authorization: Bearer。以接入文档为准别混用。settings.json 解析失败。Qt 的 QJsonDocument 对尾逗号零容忍JSON 里不能有多余逗号。用在线校验器过一遍再放进项目。中文乱码。Qt 源码文件用 UTF-8 保存QStringLiteral 包中文别用裸字符串。MSVC 下加 /utf-8 编译选项。6. 继续往下走QTextCursor 的接口看着多但归类后就四组插入、选区、结构、格式。官档的日历例子其实就是这几组的组合用最少代码生成一个固定间距的日历。你可以先把第 3 节的骨架抄进项目跑通第 4 节的验证再按需扩展。接口验证通过后如果要做模型对话类功能走模型对话入口 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 长期编码或 Agent 任务看 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite Key 管理和接入细节分别在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 和 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。Claude Code 相关接入参考 https://taotoken.net/claude-code?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecodeutm_campaignrewrite 。最后给一个实用技巧调试 QTextCursor 时把 editor-toHtml() 打印出来比 toPlainText() 更能看清块、表格、格式有没有按预期生成。HTML 里能看到table、span stylefont-weight:600这些标记定位问题快很多。

看完文章,想为自己的企业也做一次专业网站诊断?

尧图顾问免费为您评估现有网站,并给出建站/改版建议与报价方案。

免费获取方案