警告

本节包含从 C++ 自动翻译到 Python 的代码片段,可能包含错误。

QTextCursor 接口#

文档可以通过 QTextCursor 类提供的接口进行编辑;光标可以通过构造函数创建,或者从一个编辑小部件中获取。光标用于执行与用户在编辑器中能够自行进行的操作相对应的编辑操作。因此,通过光标还可以获取文档结构信息,从而可以修改结构。使用以光标为中心的接口进行编辑,使得编写自定义编辑器对于开发者来说更加简单,因为编辑操作可以轻松可视化。

QTextCursor 类还维护关于它在文档中选择的任何文本的信息,这也遵循了与用户在编辑器中选择文本概念上类似的模式。

富文本文档可以与多个光标相关联,每个光标都包含有关其在文档中的位置以及它们可能持有的任何选择的信息。这种基于光标的方法使得文本剪切和粘贴等常见操作可以轻松实现,同时也允许在文档上执行更复杂的编辑操作。

本章描述了您在使用光标时需要执行的大部分常见编辑操作,从基本的文本和文档元素插入到更复杂的文档结构操作。

基于光标的编辑#

在最简单级别上,文本文档由一系列字符组成,以某种方式标记来表示文档中文本的块结构。 QTextCursor 提供了一个基于光标的接口,允许以字符级别操作 QTextDocument 的内容。由于元素(块、框架、表格等)也编码在字符流中,因此可以 cursor 也改变文档结构。

光标跟踪其在父文档中的位置,并可以报告有关周围结构的信息,例如封装的文本块、框架、表格或列表。封装结构的形式也可以通过光标直接获得。

#使用光标

光标的主要用途是在一个块内插入或修改文本。我们可以使用文本编辑器的光标来完成此操作

editor = QTextEdit()
cursor = QTextCursor(editor.textCursor())

或者,我们可以直接从文档中获取光标

document = QTextDocument(editor)
cursor = QTextCursor(document)

光标位于文档的开头,这样我们就可以写入文档中第一个(空)块。

分组光标操作#

可以将一系列编辑操作打包在一起,以便可以一次性重放或撤销。这可以通过使用以下方式中的 `beginEditBlock()` 和 `endEditBlock()` 函数实现,如下例所示,我们选择包含光标的单词

cursor.beginEditBlock()
cursor.movePosition(QTextCursor.StartOfWord)
cursor.movePosition(QTextCursor.EndOfWord, QTextCursor.KeepAnchor)
cursor.endEditBlock()

如果不分组编辑操作,文档会自动记录个别操作,以便稍后可以撤销。将操作分组到更大的包中可以提高用户和应用程序的编辑效率,但必须小心不要将太多操作组合在一起,因为用户可能希望对撤销过程有更细致的控制。

多个光标#

可以使用多个光标同时编辑同一个文档,尽管在 QTextEdit 小部件中只有一个将可见给用户。`QTextDocument` 确保每个光标正确写入文本,不会相互干扰。

插入文档元素#

`QTextCursor` 提供了几个函数,可以用来更改富文本文档的结构。通常,这些函数允许使用相关的格式信息创建文档元素,并将它们插入到光标位置。

第一组函数插入块级元素,并更新光标位置,但它们不返回插入的元素

  • `insertBlock()` 插入一个新的文本块(段落)到文档中光标的位置,并将光标移动到新块的开头。

  • `insertFragment()` 将一个现有的文本片段插入到文档中光标的位置。

  • `insertImage()` 在文档中光标的位置插入一个图片。

  • `insertText()` 在光标的位置插入文本。

您可以通过光标界面检查插入的元素的内容。

第二组函数插入提供文档结构的元素,并返回插入的结构

  • `insertFrame()` 在光标当前块后插入一个框架,并将光标移动到新框架中空块的开头。

  • `insertList()` 在光标位置插入一个列表,并将光标移动到列表的第一项的开头。

  • insertTable() 在文档中光标当前块的 后面 插入一个表格,并将光标移到表格后面的块的开始处。

这些元素包含或组合文档中的其他元素。

文本和文本片段#

文本可以以当前字符格式或使用文本指定的自定义格式插入到当前块中。

cursor.insertText(tr("Character formats"),
                  headingFormat)
cursor.insertBlock()
cursor.insertText(tr("Text can be displayed in a variety of "
                              "different character formats. "), plainFormat)
cursor.insertText(tr("We can emphasize text by "))
cursor.insertText(tr("making it italic"), emphasisFormat)

一旦字符格式与光标一起使用,该格式就变为用该光标插入的任何文本的默认格式,直到指定另一个字符格式。

如果使用未指定字符格式的光标插入文本,则文本将具有该位置在文档中使用的字符格式。

#

使用 insertBlock() 函数将文本块插入到文档中。

backgroundFormat = blockFormat
backgroundFormat.setBackground(QColor("lightGray"))
cursor.setBlockFormat(backgroundFormat)

光标位于新块的开始处。

框架#

使用光标将框架插入文档中,框架将放置在光标当前框架的 后面 当前块中。下面的代码展示了如何在文档根框架的两个文本块之间插入一个框架。我们首先找到光标当前框架

mainFrame = cursor.currentFrame()
cursor.insertText(...)

在这个框架中插入一些文本,然后设置子框架的框架格式

frameFormat = QTextFrameFormat()
frameFormat.setMargin(32)
frameFormat.setPadding(8)
frameFormat.setBorder(4)

框架格式将为框架提供 32 像素的外边距、8 像素的内部填充和 4 像素宽的边框。有关框架格式的更多信息,请参见 QTextFrameFormat 文档。

框架插入到文档中在先前的文本之后

cursor.insertFrame(frameFormat)
cursor.insertText(...)

我们在插入框架后立即向文档添加一些文本。由于文本光标在插入到文档时位于 框架内,因此此文本也将插入到框架内。

最后,我们将光标定位在框架之外,方法是使用我们先前记录的框架内的最后一个可用光标位置

cursor = mainFrame.lastCursorPosition()
cursor.insertText(...)

最后添加的文本将在文档中插入到子框架之后。由于每个框架都填充有文本块,这确保可以用光标插入更多的元素。

表格#

使用光标将表格插入文档中,并将它们放置在光标当前框架的 后面 当前块中。

cursor = QTextCursor(editor.textCursor())
table = cursor.insertTable(rows, columns, tableFormat)

表格可以使用特定格式创建,该格式定义了表格的整体属性,例如其对齐方式、背景颜色和单元格间距。它还可以确定每列的约束,允许每列具有固定宽度或根据可用空间调整大小。

 tableFormat = QTextTableFormat()
 tableFormat.setBackground(QColor("#e0e0e0"))
 QList<QTextLength>raints
raints << QTextLength(QTextLength.PercentageLength, 16)
raints << QTextLength(QTextLength.PercentageLength, 28)
raints << QTextLength(QTextLength.PercentageLength, 28)
raints << QTextLength(QTextLength.PercentageLength, 28)
 tableFormat.setColumnWidthConstraints(constraints)
 table = cursor.insertTable(rows, columns, tableFormat)

上表中创建的每列将占用可用宽度的一定百分比。请注意,表格格式是可选的;如果您插入一个没有格式的表格,表格的属性将使用一些合理的默认值。

由于单元格可以包含其他文档元素,因此它们也可以根据需要格式化和样式化。

可以通过使用光标导航到每个单元格并在其中插入文本将文本添加到表格中。

cell = table.cellAt(0, 0)
cellCursor = cell.firstCursorPosition()
cellCursor.insertText(tr("Week"), charFormat)

可以通过遵循这种方法创建简单的日程表

for column in range(1, columns):
    cell = table.cellAt(0, column)
    cellCursor = cell.firstCursorPosition()
    cellCursor.insertText(tr("Team %1").arg(column), charFormat)

for row in range(1, rows):
    cell = table.cellAt(row, 0)
    cellCursor = cell.firstCursorPosition()
    cellCursor.insertText(tr("%1").arg(row), charFormat)
    for column in range(1, columns):
        if (row-1) % 3 == column-1:
cell = table.cellAt(row, column)
cellCursor = cell.firstCursorPosition()
cellCursor.insertText(tr("On duty"), charFormat)

列表#

可以自动创建块元素的列表并将其插入到文档中当前光标位置。以这种方式创建的每个列表都需要指定列表格式

listFormat = QTextListFormat()
if list:
    listFormat = list.format()
    listFormat.setIndent(listFormat.indent() + 1)

listFormat.setStyle(QTextListFormat.ListDisc)
cursor.insertList(listFormat)

上述代码首先检查光标是否位于现有列表中,如果是,则为新列表的格式提供一个合适的缩进级别。这允许以递增缩进级别创建嵌套列表。更高级的实现也会在列表的每一级别使用不同类型的符号作为项目符号。

图片#

内联图片通过光标以常规方式添加到文档中。与许多其他元素不同,所有图像属性都是由图像的格式指定的。这意味着在插入图像之前必须先创建一个 QTextImageFormat 对象

imageFormat = QTextImageFormat()
imageFormat.setName(":/images/advert.png")
cursor.insertImage(imageFormat)

图像名称指向应用程序的资源配置文件中的一个条目。用于推导此名称的方法在《Qt资源配置系统》中描述。

示例#

丰富的文本存储在可以通过从外部源导入HTML或使用QTextCursor 生成的文本文档中。

处理丰富的文本#

最简单使用丰富文本文档的方法是通过 QTextEdit 类,它提供了一个对文档的可编辑视图。以下代码将HTML导入到文档中,并通过文本编辑小部件显示该文档。

editor = QTextEdit(parent)
editor.setHtml(aStringContainingHTMLtext)
editor.show()

您可以使用 document() 函数从文本编辑器中检索文档。然后可以使用 QTextCursor 类来编程方式编辑文档。这个类模仿了一个屏幕光标,编辑操作遵循相同的语义。以下代码将文档的第一行改为粗体,而保留所有其他字体属性不变。编辑器将自动更新以反映对基础文档数据的更改。

document = edit.document()
cursor = QTextCursor(document)
cursor.movePosition(QTextCursor.Start)
cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor)
format = QTextCharFormat()
format.setFontWeight(QFont.Bold)
cursor.mergeCharFormat(format)

注意,光标已从第一行的开头移动到结尾,但它保留了行开头的锚点。这证明了 QTextCursor 类的基于光标的选取功能。

生成日历#

使用基于光标的方法可以非常快速地生成丰富的文本。以下示例展示了在 QTextEdit 小部件中以带有每周日期粗体标题的简单日历

editor = QTextEdit(self)

cursor = QTextCursor(editor.textCursor())
cursor.movePosition(QTextCursor.Start)
format = QTextCharFormat(cursor.charFormat())
format.setFontFamily("Courier")
boldFormat = format
boldFormat.setFontWeight(QFont.Bold)
cursor.insertBlock()
cursor.insertText(" ", boldFormat)
date = QDate.currentDate()
year = date.year(), month = date.month()
for weekDay in range(1, 8):
    cursor.insertText(QString("%1 ").arg(QLocale.system().dayName(weekDay), 3),
        boldFormat)

cursor.insertBlock()
cursor.insertText(" ", format)
for column in range(1, QDate(year, month, 1).dayOfWeek()):
    cursor.insertText(" ", format)

for day in range(1, date.daysInMonth() + 1):
        weekDay = QDate(year, month, day).dayOfWeek()
        if QDate(year, month, day) == date:
            cursor.insertText(QString("%1 ").arg(day, 3), boldFormat)
else:
            cursor.insertText(QString("%1 ").arg(day, 3), format)
        if weekDay == 7:
            cursor.insertBlock()
            cursor.insertText(" ", format)

上述示例演示了如何轻松使用少量代码快速生成新的丰富文本文档。虽然我们为了避免引用过多代码生成了一个粗略的等宽日历,但Scribe提供了更复杂的布局和格式化功能。