富文本文档结构
文本文档由QTextDocument 类表示,该类包含有关文档内部表示形式及其结构的信息,并记录修改内容以提供撤销/重做功能。
文本文档的结构化表示将内容呈现为文本块、框架、表格及其他对象的层次结构。这些元素为文档提供了逻辑结构,并描述了其内容将如何显示。通常,框架和表格用于分组其他结构,而文本块则包含实际的文本信息。
可以通过 QTextCursor或使用编辑器控件(如QTextEdit )以编程方式创建新元素并将其插入文档。元素在创建时可以指定特定的格式;否则,它们将采用光标当前针对该元素的格式。
![]() | 基本结构 文档的“顶级”结构可能如所示进行填充。每个文档都包含一个根框架,且该框架始终至少包含一个文本块。 对于包含文本内容的文档,根框架通常包含一系列文本块和其他元素。 在文档中,框架和表格的序列之间总是由文本块分隔,即使这些文本块不包含任何信息。这确保了新元素总能插入到现有结构之间。 |
在本章中,我们将逐一探讨富文本文档中使用的各个结构元素,概述其特征和用途,并演示如何检查其内容。文档编辑的相关内容在《QTextCursor 接口》中进行了说明。
富文本文档
QTextDocument 对象包含构建富文本文档所需的所有信息。可以通过两种互补的方式访问文本文档:一种是作为编辑器使用的线性缓冲区,另一种是作为对版面布局引擎有用的对象层次结构。在分层文档模型中,对象通常对应于框架、表格和列表等视觉元素。 在更底层,这些元素描述了文本样式和对齐方式等属性。文档的线性表示形式用于编辑和操作文档内容。
尽管QTextEdit 使富文本的显示和编辑变得简单,但文档也可以独立于任何编辑器控件使用,例如:
QTextDocument *newDocument = new QTextDocument;或者,它们可以从现有的编辑器中提取出来:
QTextEdit *editor = new QTextEdit;
QTextDocument *editorDocument = editor->document();这种灵活性使应用程序能够处理多个富文本文档,而无需承担多个编辑器控件带来的开销,也不需要将文档存储在某种中间格式中。
一个空文档包含一个根框架,该框架本身包含一个空文本块。框架在文档各部分之间提供了逻辑分隔,同时还具有决定渲染时显示效果的属性。表格是一种特殊的框架类型,由若干按行和列排列的单元格组成,每个单元格均可包含进一步的结构和文本。 表格提供了管理和布局功能,允许创建灵活的单元格配置。
文本块包含文本片段,每个片段都指定了文本和字符格式信息。 文本属性既可在字符级别定义,也可在块级别定义。在字符级别,可以指定字体家族、文本颜色和字体粗细等属性。块级属性则控制文本的高级外观和行为,例如文本流向、对齐方式和背景颜色。
文档结构不会被直接操作。编辑通过基于光标的界面进行。文本光标界面会自动将新的文档元素插入到根框架中,并确保在必要时用空块进行填充。
我们通过以下方式获取根框架:
QTextDocument *editorDocument = editor->document();
QTextFrame *root = editorDocument->rootFrame();在浏览文档结构时,从根帧开始非常有用,因为它提供了访问整个文档结构的途径。
文档元素
富文本文档通常由段落、框架、表格和列表等常见元素组成。在QTextDocument 中,这些元素分别由QTextBlock 、QTextFrame 、QTextTable 和QTextList 类表示。与文档中的其他元素不同,图像由特殊格式的文本片段表示。这使得它们能够与周围文本保持格式一致地内联放置。
文档中的基本结构构建块是QTextBlock 和QTextFrame 。这些块本身包含富文本片段(QTextFragment ),但这些片段不会直接影响文档的高级结构。
能够将其他文档元素分组在一起的元素通常是QTextObject 的子类,并分为两类:将文本块分组在一起的元素是QTextBlockGroup 的子类,而将框架和其他元素分组在一起的元素则是QTextFrame 的子类。
文本块
文本块由QTextBlock 类提供。
文本块将具有不同字符格式的文本片段组合在一起,用于表示文档中的段落。每个文本块通常包含多个具有不同样式的文本片段。当文本插入到文档中时会生成这些片段,而在编辑文档时会添加更多片段。 文档会通过拆分、合并和移除片段,来高效地呈现块内不同样式的文本。
可以通过使用 `QTextBlock::iterator ` 遍历块的内部结构,来检查给定块中的片段:
QTextBlock::iterator it;
for (it = currentBlock.begin(); !(it.atEnd()); ++it) {
QTextFragment currentFragment = it.fragment();
if (currentFragment.isValid())
processFragment(currentFragment);
}块还用于表示列表项。因此,块可以定义自己的字符格式,其中包含关于块级装饰的信息,例如列表项所使用的项目符号类型。块本身的格式由QTextBlockFormat 类描述,该类定义了文本对齐、缩进和背景颜色等属性。
尽管给定的文档可能包含复杂的结构,但只要我们获得了文档中某个有效块的引用,就可以按照它们被写入的顺序在各个文本块之间进行导航:
QTextBlock currentBlock = textDocument->begin();
while (currentBlock.isValid()) {
processBlock(currentBlock);
currentBlock = currentBlock.next();
}当您仅需从文档中提取富文本时,此方法非常有用,因为它会忽略框架、表格及其他类型的结构。
QTextBlock 该类提供了便于操作文本块的比较运算符:operator==() 和operator!=() 用于判断两个文本块是否相同,而operator<() 用于确定文档中哪个文本块出现得更早。
框架
框架由QTextFrame 类提供。
文本框将文本块和子框组合在一起,形成比段落更大的文档结构。文本框的格式指定了其在页面上的呈现方式和位置。文本框要么插入到文本流中,要么浮动在页面的左侧或右侧。 每个文档都包含一个根框架,其中包含所有其他文档元素。因此,除根框架外,所有框架都有一个父框架。
由于文本块用于分隔其他文档元素,因此每个框架始终至少包含一个文本块,以及零个或多个子框架。我们可以使用QTextFrame::iterator 遍历框架的子元素来检查其内容:
QTextFrame::iterator it;
for (it = frame->begin(); !(it.atEnd()); ++it) {
QTextFrame *childFrame = it.currentFrame();
QTextBlock childBlock = it.currentBlock();
if (childFrame)
processFrame(childFrame);
else if (childBlock.isValid())
processBlock(childBlock);
}请注意,该迭代器既会选中框架也会选中文本块,因此必须确认其所指向的具体对象。这使我们能够以逐帧的方式遍历文档结构,同时在需要时仍可访问文本块。QTextBlock::iterator 和QTextFrame::iterator 这两个类可以互补使用,从而从文档中提取所需的结构。
表格
表格由QTextTable 类提供。
表格是由按行和列排列的单元格组成的集合。每个表格单元格都是一个具有自身字符格式的文档元素,但它也可以包含其他元素,例如画框和文本块。当构建表格或添加额外的行或列时,表格单元格会自动创建。它们还可以在不同的表格之间移动。
QTextTable 是QTextFrame 的子类,因此表格在文档结构中被视为框架。对于文档中遇到的每个框架,我们可以检测它是否代表一个表格,并采取不同的处理方式:
QTextFrame::iterator it;
for (it = frame->begin(); !(it.atEnd()); ++it) {
QTextFrame *childFrame = it.currentFrame();
QTextBlock childBlock = it.currentBlock();
if (childFrame) {
QTextTable *childTable = qobject_cast<QTextTable*>(childFrame);
if (childTable)
processTable(childTable);
else
processFrame(childFrame);
} else if (childBlock.isValid()) {
processBlock(childBlock);
}
}可以通过遍历行和列来检查现有表格中的单元格。
for (int row = 0; row < table->rows(); ++row) {
for (int column = 0; column < table->columns(); ++column) {
QTextTableCell tableCell = table->cellAt(row, column);
processTableCell(tableCell);
}
}列表
列表由QTextList 类提供。
列表是由文本块组成的序列,这些文本块采用常规格式,但同时提供标准列表装饰,例如项目符号和编号项目。列表可以嵌套,如果列表格式指定了非零缩进,则会进行缩进。
我们可以根据列表中的索引来引用每个列表项:
for (int index = 0; index < list->count(); ++index) {
QTextBlock listItem = list->item(index);
processListItem(listItem);
}由于 `QTextList ` 是 `QTextBlockGroup` 的子类,它不会将列表项作为子元素进行分组,而是提供了各种用于管理它们的函数。这意味着在遍历文档时遇到的任何文本块实际上都可能是列表项。我们可以使用以下代码确保正确识别列表项:
QTextFrame::iterator it;
for (it = frame->begin(); !(it.atEnd()); ++it) {
QTextBlock block = it.currentBlock();
if (block.isValid()) {
QTextList *list = block.textList();
if (list) {
int index = list->itemNumber(block);
processListItem(list, index);
}
}
}图像
在QTextDocument 中,图像由文本片段表示,这些片段通过资源机制引用外部图像。图像通过光标接口创建,并可通过更改图像文本片段的字符格式在后续进行修改:
if (fragment.isValid()) {
QTextImageFormat newImageFormat = fragment.charFormat().toImageFormat();
if (newImageFormat.isValid()) {
newImageFormat.setName(":/images/newimage.png");
QTextCursor helper = cursor;
helper.setPosition(fragment.position());
helper.setPosition(fragment.position() + fragment.length(),
QTextCursor::KeepAnchor);
helper.setCharFormat(newImageFormat);
}
}可以通过遍历包含该图像的文本块中的片段,来查找代表该图像的片段。
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.
