警告

本节包含自动从C++转换为Python的代码片段,可能包含错误。

富文本文档结构#

文本文档由QTextDocument类表示,其中包含有关文档内部表示、结构和跟踪修改信息(以提供撤销/重做功能)。

文本文档的结构化表示将内容呈现为一系列文本块、框架、表格和其他对象分层结构。这些对象提供了文档的逻辑结构,并描述了它们内容如何显示。通常,框架和表格用于组织其他结构,而文本块包含实际文本信息。

可以通过程序使用QTextCursor或在编辑器小部件(如QTextEdit)中使用来创建和插入到文档中的新元素。元素在创建时可以指定特定的格式;否则,它们将采取元素所对应的游标当前格式。

richtext-document1

基本结构

文档的“顶级”可能是按此方式填充的。每个文档都包含一个根框架,并且总是包含至少一个文本块。

对于包含一些文本内容的文档,根框架通常包含一系列块和其他元素。

文档中的框架和表格序列始终由文本块分隔,即使文本块不包含任何信息。这确保了新元素总是可以插入到现有结构之间。

在本章中,我们将查看用于富文本文档的每个结构元素,概述其功能和用途,并展示如何检查其内容。文档编辑将在QTextCursor接口中进行描述。

富文本文档#

QTextDocument对象包含了构建富文本文档所需的所有信息。文本文档可以通过两种互补的方式来访问:作为编辑器使用的线性缓冲区,以及对于布局引擎有用的对象层次结构。在文档的层次模型中,对象通常对应于如图框架、表格和列表等视觉元素。在较低层次,这些元素描述了如文字样式和校对等属性。文档的线性表示用于编辑和操作文档的内容。

尽管QTextEdit使得显示和编辑富文本变得容易,但文档也可以与任何编辑器小部件独立使用,例如

newDocument = QTextDocument()

或者,它们可以从现有的编辑器中提取

editor = QTextEdit()
editorDocument = editor.document()

这种灵活性使得应用程序能够处理多个富文本文档,无需开销多个编辑器小部件,也不需要将文档存储在某种中间格式中。

一个空的文档包含一个根框架,该框架本身包含一个单独的空文本块。框架在文档的不同部分之间提供逻辑分隔,同时具有属性,这些属性决定了它们在渲染时的外观。表格是一种特殊的框架类型,由多个单元格组成,这些单元格排列成行和列,每个单元格都可以包含进一步的结构和文本。表格提供了管理和布局特性,可以创建出灵活的单元格配置。

文本块包含文本片段,每个片段都指定了文本和字符格式信息。文本属性在字符级别和块级别都被定义。在字符级别,可以指定如字体族、文本颜色和字体粗细等属性。块级别的属性控制文本的更高级别的外观和行为,如文本流的方向、对齐方式和背景颜色。

文档结构不是直接操作的。编辑是通过基于光标的界面进行的。文本光标界面自动将新的文档元素插入到根框架中,并确保在必要时以空块填充。

以下是获取根框架的方法

editorDocument = editor.document()
root = editorDocument.rootFrame()

在导航文档结构时,从根框架开始非常有用,因为它可以提供对整个文档结构的访问。

文档元素#

富文本文档通常由段落、框架、表格和列表等常见元素组成。这些元素通过QTextDocument 中的 QTextBlockQTextFrameQTextTableQTextList 类来表示。与文档中的其他元素不同,图像是由特别格式化的文本片段表示的。这使得它们可以像周围的文本一样格式化后内联放置。

文档中的基本结构构建块是 QTextBlockQTextFrame 。块本身包含丰富文本片段(QTextFragment),但这不会直接影响文档的高层结构。

可以组合其他文档元素的元素通常是 QTextObject 的子类,并分为两类:组合文本块的元素是 QTextBlockGroup 的子类,而组合框架和其他元素的元素是 QTextFrame 的子类。

文本块#

文本块由 QTextBlock 类提供。

文本块将具有不同字符格式的一组文本片段组合在一起,并用于表示文档中的段落。每个块通常包含多个不同样式的文本片段。片段在将文本插入文档时创建,并在编辑文档时添加更多。文档通过拆分、合并和删除片段来有效地表示块中不同风格的文本。

可以使用一个 iterator 来遍历块的内部结构,从而可以检查给定块内的片段。

QTextBlock.iterator it
for it in currentBlock:
    currentFragment = it.fragment()
    if currentFragment.isValid():
processFragment(currentFragment)

块还被用来表示列表项。因此,块可以定义自己的字符格式,其中包含有关块级装饰的信息,例如用于列表项的编号类型。块本身的格式由 QTextBlockFormat 类描述,描述了文本对齐、缩进和背景颜色等属性。

尽管给定的文档可能包含复杂的结构,但一旦我们获得文档中有效块的引用,我们就可以按照编写的顺序在文本块之间导航。

currentBlock = textDocument.begin()
while currentBlock.isValid():
processBlock(currentBlock)
currentBlock = currentBlock.next()

此方法对于您只想从文档中提取纯文本文档很有用,因为它忽略了框架、表格和其他类型的结构。

QTextBlock 提供比较运算符,这使得操作块变得更容易:使用 operator==()operator!=() 测试两个块是否相同,并使用 operator 确定在文档中哪个先出现。

框架#

框架由 QTextFrame 类提供。

文本框架将文本块和子框架组合在一起,创建超过段落的文档结构。框架的格式指定了它在页面上的渲染和定位方式。框架可以是插入到文本流中的,也可以在页面的左端或右端浮动。每个文档都包含一个根框架,其中包含所有其他文档元素。因此,除了根框架以外的所有框架都有一个父框架。

由于文本块用于分隔其他文档元素,因此每个框架将始终包含至少一个文本块以及零个或多个子框架。我们可以使用 iterator 来遍历框架的子元素来检查框架的内容。

QTextFrame.iterator it
for it in frame:
    childFrame = it.currentFrame()
    childBlock = it.currentBlock()
    if childFrame:
        processFrame(childFrame)
    elif childBlock.isValid():
        processBlock(childBlock)

请注意,迭代器选择框架和块,因此需要检查它是指哪个。这允许我们逐个框架地遍历文档结构,但在需要时也可以访问文本块。可以使用两种方式相互补充使用 iteratoriterator 类来从文档中提取所需的结构。

表格#

表格由QTextTable类提供。

表格是由行和列排列的单元格集合。每个表格单元格都是一个具有自己字符格式的文档元素,但也可以包含其他元素,如框架和文本块。表格单元格在构建表格时或在添加额外的行或列时自动创建。它们也可以在表格之间移动。

QTextTableQTextFrame的子类,因此表格在文档结构中被视为框架。对于在文档中遇到的每个框架,我们都可以测试它是否代表一个表格,然后以不同的方式处理它。

    QTextFrame.iterator it
    for it in frame:
        childFrame = it.currentFrame()
        childBlock = it.currentBlock()
        if childFrame:
            childTable = QTextTable(childFrame)
            if childTable:
                processTable(childTable)
else:
                processFrame(childFrame)
         elif childBlock.isValid():
            processBlock(childBlock)

可以通过遍历行和列来检查现有表格内的单元格。

for row in range(0, table.rows()):
    for column in range(0, table.columns()):
        tableCell = table.cellAt(row, column)
processTableCell(tableCell)

列表#

列表由QTextList类提供。

列表是按常规格式编排的文本块序列,但同时也提供了标准列表装饰,如项目符号和枚举项。列表可以嵌套,并且如果列表的格式指定了非零缩进,则将缩进。

我们可以通过列表中的索引来引用每个列表项。

for index in range(0, list.count()):
    listItem = list.item(index)
processListItem(listItem)

由于QTextListQTextBlockGroup的子类,它不会将列表项作为子元素分组,而是提供各种管理它们的函数。这意味着当我们遍历文档时找到的任何文本块实际上可能是一个列表项。我们可以通过以下代码确保正确识别列表项。

QTextFrame.iterator it
for it in frame:
    block = it.currentBlock()
    if block.isValid():
        list = block.textList()
        if list:
            index = list.itemNumber(block)
processListItem(list, index)

图像#

QTextDocument中的图像通过引用外部图像的资源机制表示。图像使用光标接口创建,可以通过更改图像文本片段的字符格式在以后进行修改。

if fragment.isValid():
    newImageFormat = fragment.charFormat().toImageFormat()
    if newImageFormat.isValid():
        newImageFormat.setName(":/images/newimage.png")
        helper = cursor
        helper.setPosition(fragment.position())
        helper.setPosition(fragment.position() + fragment.length(),
                            QTextCursor.KeepAnchor)
        helper.setCharFormat(newImageFormat)

可以通过遍历包含图像的文本块中的片段来找到表示图像的片段。