QTextCursor接口

文档可以通过QTextCursor类提供的接口进行编辑;游标可以是使用构造函数创建的,也可以是从编辑器小部件获取的。游标用于执行与用户在编辑器中能够执行的操作相对应的编辑操作。因此,也可以通过游标获取文档结构信息,并允许修改结构。使用基于游标的编辑接口可以简化开发人员自定义编辑器的编写过程,因为编辑操作可以轻松地可视化。

QTextCursor类还维护有关其在文档中选择的任何文本的信息,这再次采用了与用户在编辑器中选择文本的操作概念上相似的模型。

富文本文档可以有多个与它们关联的游标,这些游标包含有关它们在文档中的位置以及它们可能持有的任何选择的信息。这种基于游标的范式使得文本剪切和粘贴等常见操作简单易实现,但也可以在文档上执行更复杂的编辑操作。

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

基于游标的编辑

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

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

使用游标

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

    QTextEdit *editor = new QTextEdit();
    QTextCursor cursor(editor->textCursor());

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

    QTextDocument *document = new QTextDocument(editor);
    QTextCursor cursor(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()函数将文本块插入文档。

    QTextBlockFormat backgroundFormat = blockFormat;
    backgroundFormat.setBackground(QColor("lightGray"));

    cursor.setBlockFormat(backgroundFormat);

光标定位在新区块的起始位置。

框架

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

    QTextFrame *mainFrame = cursor.currentFrame();
    cursor.insertText(...);

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

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

框架格式将为框架提供一个32像素的外边距、一个8像素的内边距,以及一个4像素宽的边框。有关框架格式的更多信息,请参阅QTextFrameFormat文档。

框架将插入到文档中的前文之后

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

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

最后,我们将光标移到框架外,方法是使用记录下来先前框架内的最后一个可用光标位置

    cursor = mainFrame->lastCursorPosition();
    cursor.insertText(...);

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

表格

使用光标将表格插入文档,并将表格放置在光标当前框架的当前块之后

    QTextCursor cursor(editor->textCursor());
    QTextTable *table = cursor.insertTable(rows, columns, tableFormat);

表格可以带有特定的格式,这定义了表格的整体属性,如对齐方式、背景颜色以及单元格间距。它还可以确定每个列的约束,允许每个列具有固定宽度,或根据可用空间进行缩放。

    QTextTableFormat tableFormat;
    tableFormat.setBackground(QColor("#e0e0e0"));
    QList<QTextLength> 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);

上面创建的表格中的列将各占一定比例的可用宽度。请注意,表格格式是可选的;如果您没有格式插入表格,则某些合理的默认值将被用于表格的属性。

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

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

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

我们可以通过这种方法创建一个简单的时间表

    for (column = 1; column < columns; ++column) {
        cell = table->cellAt(0, column);
        cellCursor = cell.firstCursorPosition();
        cellCursor.insertText(tr("Team %1").arg(column), charFormat);
    }

    for (row = 1; row < rows; ++row) {
        cell = table->cellAt(row, 0);
        cellCursor = cell.firstCursorPosition();
        cellCursor.insertText(tr("%1").arg(row), charFormat);

        for (column = 1; column < columns; ++column) {
            if ((row-1) % 3 == column-1) {
                cell = table->cellAt(row, column);
                QTextCursor cellCursor = cell.firstCursorPosition();
                cellCursor.insertText(tr("On duty"), charFormat);
            }
        }
    }

列表

可以在当前光标位置自动创建和插入块元素的列表。通过这种方式创建的每个列表都需要指定一个列表格式

    QTextListFormat listFormat;
    if (list) {
        listFormat = list->format();
        listFormat.setIndent(listFormat.indent() + 1);
    }

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

上述代码首先检查光标是否位于现有列表中,如果是,则为新列表格式指定适当的比例缩放。这允许创建具有不同缩放级别的嵌套列表。更复杂的实现还会在每个列表层次中使用不同类型的符号进行项目符号。

图片

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

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

图片名称指的是应用资源文件中的一个条目。获取此名称的方法在Qt资源系统中描述。

实例

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

操作丰富的文本

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

QTextEdit *editor = new QTextEdit(parent);
editor->setHtml(aStringContainingHTMLtext);
editor->show();

您可以通过document()函数从文本编辑中获取文档。然后可以使用QTextCursor类对文档进行程序性编辑。这个类是模仿屏幕光标设计的,并且编辑操作遵循相同的语义。以下代码将文档的第一行改为粗体字体,而保留其他所有字体属性。编辑器会自动更新以反映对底层文档数据的更改。

QTextDocument *document = edit->document();
QTextCursor cursor(document);

cursor.movePosition(QTextCursor::Start);
cursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);

QTextCharFormat format;
format.setFontWeight(QFont::Bold);

cursor.mergeCharFormat(format);

注意,光标被从第一行的开头移到结尾,但保持在行的开头有一个锚点。这展示了QTextCursor类的基于光标的选取功能。

生成一个日历

使用基于光标的方案,可以非常快速地生成富文本。以下示例展示了一个包含每周第一天粗体标题的简单日历,位于QTextEdit小部件中。

    editor = new QTextEdit(this);

    QTextCursor cursor(editor->textCursor());
    cursor.movePosition(QTextCursor::Start);

    QTextCharFormat format(cursor.charFormat());
    format.setFontFamily("Courier");

    QTextCharFormat boldFormat = format;
    boldFormat.setFontWeight(QFont::Bold);

    cursor.insertBlock();
    cursor.insertText(" ", boldFormat);

    QDate date = QDate::currentDate();
    int year = date.year(), month = date.month();

    for (int weekDay = 1; weekDay <= 7; ++weekDay) {
        cursor.insertText(QString("%1 ").arg(QLocale::system().dayName(weekDay), 3),
            boldFormat);
    }

    cursor.insertBlock();
    cursor.insertText(" ", format);

    for (int column = 1; column < QDate(year, month, 1).dayOfWeek(); ++column) {
        cursor.insertText("    ", format);
    }

    for (int day = 1; day <= date.daysInMonth(); ++day) {
        int 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提供了更多复杂的布局和格式化功能。

© 2024 Qt公司有限。此处包含的文档贡献的版权属于各自的所有者。本提供的文档根据自由软件基金会发布的GNU自由文档许可证版本1.3的条款进行许可。Qt及其相关标志是芬兰及其它国家Qt公司的商标。所有其他商标均为各自所有者的财产。