【Qt之QTextDocument】使用及表格显示富文本解决方案

【Qt之QTextDocument】使用

描述

QTextDocument类保存格式化的文本。
QTextDocument是结构化富文本文档的容器,支持样式文本和各种文档元素,如列表、表格、框架和图像。它们可以用于创建QTextEdit,也可以单独使用。

每个文档元素由一个相关的格式对象描述。每个格式对象都被QTextDocuments视为唯一的对象,并且可以通过objectForFormat()传递以获取应用于它的文档元素。

可以使用QTextCursor以编程方式编辑QTextDocument,并通过遍历文档结构来检查其内容。整个文档结构都存储在根框架下的文档元素层次结构中,可以使用rootFrame()函数找到。或者,如果只想迭代文档的文本内容,可以使用begin()end()findBlock()检索文本块,以便检查和迭代。

文档的布局由documentLayout()确定;如果要使用自己的布局逻辑,则可以创建自己的QAbstractTextDocumentLayout子类,并使用setDocumentLayout()设置它。可以通过调用metaInformation()函数获取文档的标题和其他元信息。对于通过QTextEdit类向用户公开的文档,文档标题也可以通过QTextEdit::documentTitle()函数获得。
toPlainText()toHtml()便利函数允许以纯文本HTML格式检索文档的内容。可以使用find()函数搜索文档的文本。

可以使用setUndoRedoEnabled()函数控制对文档执行的操作的撤消/重做。通过undo()redo()插槽可以控制撤消/重做系统;文档还提供了contentsChanged()undoAvailable()redoAvailable()信号,通知连接的编辑器部件有关撤消/重做系统的状态。

以下是QTextDocument的撤消/重做操作:

  • 插入或删除字符。在同一文本块内插入或删除的序列被视为单个撤消/重做操作。
  • 插入或删除文本块。在单个操作中的插入或删除序列(例如通过选择然后删除文本)被视为单个撤消/重做操作。
  • 文本字符格式更改。
  • 文本块格式更改。
  • 文本块组格式更改。

常用方法及示例

  1. void addResource(int type, const QUrl &name, const QVariant &resource) : 添加资源。将资源资源添加到资源缓存中,使用类型和名称作为标识符。type应该是QTextDocument::ResourceType中的值。
  2. QVariant resource(int type, const QUrl &name) const : 获取资源。从具有给定名称的资源返回指定类型的数据。
    富文本引擎调用这个函数来请求没有直接存储在QTextDocument中,但仍然与之关联的数据。例如,图像通过QTextImageFormat对象的name属性间接引用。
    资源在文档内部缓存。如果在缓存中找不到资源,则调用loadResource来尝试加载该资源。然后loadResource应该使用addResource将资源添加到缓存中。
cpp 复制代码
    QTextDocument* doc = ui->textEdit->document();

    QImage image(":/images/xxx.png");
    doc->addResource(QTextDocument::ImageResource, QUrl("qrc:/images/xxx.png"), QVariant(image));

    // 在文档中插入图像
    QTextCursor cursor(doc);
    cursor.insertImage(doc->resource(QTextDocument::ImageResource, QUrl("qrc:/images/xxx.png")).value<QImage>());

使用addResource()方法的好处主要包括:

  • 方便管理:该方法允许您将资源(如样式表、图像等)添加到文档中,并将其存储在内存中,以便在需要时进行访问。这样可以使资源的管理更加集中和方便。
  • 提高效率:通过将资源添加到文档中,您可以在整个文档中重复使用这些资源,而无需每次都从磁盘或网络中加载。这可以减少加载时间,提高应用程序的响应速度。
  • 易于维护:使用addResource()方法将资源添加到文档中,可以使应用程序的资源管理更加清晰和有序。这使得资源的更新和替换变得更加简单,因为您只需在文档中修改相应的资源即可。
  • 支持重用:通过将资源添加到文档中,您可以轻松地在不同的应用程序或文档中使用相同的资源。这有助于减少重复代码,提高代码的可重用性。
  • 支持动态内容:addResource()方法允许您根据需要动态添加和更新资源。这使得在运行时根据用户交互或其他事件动态更改文档内容变得更加容易。
  1. QTextBlock begin() const : 返回文档的第一个文本块。
  2. QTextBlock end() const : 该函数返回一个块,用于在迭代文档时测试文档的结尾。返回的块是无效的,并且表示文档中最后一个块之后的块。您可以使用lastBlock()来检索文档的最后一个有效块。
cpp 复制代码
for (QTextBlock it = doc->begin(); it != doc->end(); it = it.next())
        qDebug().noquote() << it.text();
  1. QTextBlock firstBlock() const : 返回文档的第一个文本块内容。
  2. QTextBlock lastBlock() const : 返回文档的最后一个文本块内容。
cpp 复制代码
doc->firstBlock().text();
doc->lastBlock().text();
  1. bool QTextDocument::isEmpty() const : 如果文档为空,返回true;否则返回false。
  2. bool QTextDocument::isRedoAvailable() const : 如果可重做,则返回true;否则返回false。
  3. bool QTextDocument::isUndoAvailable() const : 如果可撤消,则返回true;否则返回false。
  4. QTextBlock QTextDocument::lastBlock() const : 返回文档的最后一个(有效的)文本块。
  5. int QTextDocument::lineCount() const : 返回此文档的行数(如果布局支持此项)。否则,这与块数相同。
cpp 复制代码
doc->isEmpty();
  1. 枚举:enum QTextDocument::FindFlag
  2. 获取枚举值:flags QTextDocument::FindFlags
    这个enum描述QTextDocumentfind函数可用的选项。这些选项可以从以下列表中进行OR-ed组合:
常量 描述 解释
QTextDocument::FindBackward 0x00001 Search backwards instead of forwards. 向后搜索而不是向前搜索。
QTextDocument::FindCaseSensitively 0x00002 By default find works case insensitive. Specifying this option changes the behaviour to a case sensitive find operation. 默认情况下,find不区分大小写。指定此选项会将行为更改为区分大小写的查找操作。
QTextDocument::FindWholeWords 0x00004 Makes find match only complete words. 使查找只匹配完整的单词。
  1. QTextCursor find(const QString &subString, const QTextCursor &cursor, FindFlags options = FindFlags()) const
    查找给定字符串 subStringcursor 所代表的文本中的位置,返回一个文本光标,该光标指向找到的文本。如果未找到匹配的字符串,则返回一个空的光标。可以设置查找选项 options,例如大小写敏感、全字匹配等。
  2. QTextCursor find(const QString &subString, int position = 0, FindFlags options = FindFlags()) const
    查找给定字符串 subString 在当前文本中的指定位置 position 处开始查找,返回一个文本光标,该光标指向找到的文本。如果未找到匹配的字符串,则返回一个空的光标。可以设置查找选项 options,例如大小写敏感、全字匹配等。
  3. QTextCursor find(const QRegExp &expr, int from = 0, FindFlags options = FindFlags()) const
    使用正则表达式 expr 在当前文本中的指定位置 from 处开始查找,返回一个文本光标,该光标指向找到的文本。如果未找到匹配的字符串,则返回一个空的光标。可以设置查找选项 options,例如大小写敏感、全字匹配等。
  4. QTextCursor find(const QRegExp &expr, const QTextCursor &cursor, FindFlags options = FindFlags()) const
    使用正则表达式 exprcursor 所代表的文本中的位置查找,返回一个文本光标,该光标指向找到的文本。如果未找到匹配的字符串,则返回一个空的光标。可以设置查找选项 options,例如大小写敏感、全字匹配等。
  5. QTextCursor find(const QRegularExpression &expr, int from = 0, FindFlags options = FindFlags()) const
    使用正则表达式 expr 在当前文本中的指定位置 from 处开始查找,返回一个文本光标,该光标指向找到的文本。如果未找到匹配的字符串,则返回一个空的光标。可以设置查找选项 options,例如大小写敏感、全字匹配等。
  6. QTextCursor find(const QRegularExpression &expr, const QTextCursor &cursor, FindFlags options = FindFlags()) const
    使用正则表达式 exprcursor 所代表的文本中的位置查找,返回一个文本光标,该光标指向找到的文本。如果未找到匹配的字符串,则返回一个空的光标。可以设置查找选项 options,例如大小写敏感、全字匹配等。
cpp 复制代码
    QTextDocument* pDoc = ui->textEdit->document();
    pDoc->setPlainText("helloworld");
    QTextCursor cursor = ui->textEdit->textCursor();

    cursor.insertText("\n");
    cursor.insertText("earth");
    cursor.insertText("\n");
    cursor.insertText("sun");
    // 输出查找到的位置,从后往前查找
    qDebug() << pDoc->find(QRegularExpression("ear"), cursor.position(), QTextDocument::FindBackward).position();
	// 以下查找到会自动选择匹配到的
    ui->textEdit->find("ear", QTextDocument::FindBackward);


  1. 枚举:enum QTextDocument::MetaInformation
    这个枚举描述了可以添加到文档中的不同类型的元信息。
常量 描述 解释
QTextDocument::DocumentTitle 0 The title of the document. 文件的标题。
QTextDocument::DocumentUrl 1 The url of the document. The loadResource() function uses this url as the base when loading relative resources. 文档的url。loadResource()函数在加载相关资源时使用此url作为基。
  1. QString metaInformation(MetaInformation info) const
  2. void setMetaInformation(MetaInformation info, const QString &string) : 将由info指定的类型的文档元信息设置为给定的字符串。
cpp 复制代码
    pDoc->setMetaInformation(QTextDocument::DocumentTitle, "helloworld");
    qDebug() << pDoc->metaInformation(QTextDocument::DocumentTitle);
  1. 获取文本块数目、行数、是否编辑、是否为空等操作
cpp 复制代码
    // 文本块数目
    qDebug() << pDoc->blockCount();
    // 行数
    qDebug() << pDoc->lineCount();
    // 是否是空
    qDebug() << pDoc->isEmpty();
    // 是否编辑
    qDebug() << pDoc->isModified();
    // 是否执行Redo
    qDebug() << pDoc->isRedoAvailable();
    // 是否执行Undo
    qDebug() << pDoc->isUndoAvailable();
    // Undo和Redo是否使能
    qDebug() << pDoc->isUndoRedoEnabled();
  1. void setHtml(const QString &html)
cpp 复制代码
pDoc->setHtml("<p style='color:#ff0000;'>helloworld</p>");
  1. void setPlainText(const QString &text)

使用此方法,会将textEdit.setTextColor()方法设置的文本颜色失效。

cpp 复制代码
pDoc->setPlainText("helloworld");

使用QTextList

cpp 复制代码
    QTextDocument* pDoc = ui->textEdit->document();
    pDoc->setPlainText("helloworld\n");
    QTextCursor cursor(pDoc);
    QTextListFormat listFormat;
    listFormat.setStyle(QTextListFormat::ListDecimal); // 使用有序列表
    QTextList * pList = cursor.insertList(listFormat);

    cursor.insertText("Item 1\n");
    cursor.insertText("Item 2\n");
    cursor.insertText("Item 3");

    QTextList* list = cursor.currentList();
    qDebug() << list->count();
    if (list) {
        QTextBlock currentItem = list->item(0);
        // 处理列表中的第一个项
        QString text = currentItem.text();
        qDebug() << text;
    }

使用QTextBlock

cpp 复制代码
    QTextDocument* pDoc = ui->textEdit->document();
    QTextCursor cursor(pDoc);
    cursor.insertText("\n");
    cursor.insertText("Hello, World!");

    QTextBlock currentBlock = pDoc->firstBlock();
    while (currentBlock.isValid()) {
        QString text = currentBlock.text();
        // 处理当前段落的文本内容
        qDebug() << text;
        currentBlock = currentBlock.next();
    }


使用QTextTable

cpp 复制代码
    QTextDocument* pDoc = ui->textEdit->document();
    // pDoc->setPlainText("helloworld\n");
    QTextCursor cursor(pDoc);
    QTextTableFormat tableFormat;
    tableFormat.setBorder(1);
    tableFormat.setCellPadding(5);
    tableFormat.setAlignment(Qt::AlignCenter);
    cursor.insertTable(3, 2, tableFormat);

    QTextTable* table = cursor.currentTable();
    if (table) {
        for (int row = 0; row < table->rows(); ++row) {
            for (int col = 0; col < table->columns(); ++col) {
                QTextTableCell cell = table->cellAt(row, col);
                QTextCursor cellCursor = cell.firstCursorPosition();
                cellCursor.insertText(QString("Row %1, Col %2").arg(row).arg(col));
            }
        }
    }

表格显示富文本

QTableWidget是Qt框架提供的一个用于显示表格数据的控件。然而,QTableWidget本身并不直接支持富文本(rich text)的显示。如果需要在QTableWidget中显示富文本,一种方法是使用QTableWidgetItemsetData方法,将富文本作为自定义数据存储在表格中,然后在单元格的渲染过程中使用自定义的QStyledItemDelegate 来显示这些数据。

cpp 复制代码
#include <QApplication>
#include <QTableWidget>
#include <QTableWidgetItem>
#include <QTextDocument>
#include <QTextCharFormat>
#include <QTextCursor>
#include <QStyledItemDelegate>

class RichTextDelegate : public QStyledItemDelegate {
public:
    explicit RichTextDelegate(QTableWidget* pTW){
        m_pTW = pTW;
    }
public:
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override {
        QTableWidgetItem *item = m_pTW->item(index.row(), index.column());
        if (item) {
            QTextDocument doc;
            doc.setHtml(item->data(Qt::UserRole).toString());
            doc.drawContents(painter);
        }
    }


private:
    QTableWidget*   m_pTW;
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    QTableWidget table;
    table.setColumnCount(1);
    table.setRowCount(3);
    table.setItemDelegateForColumn(0, new RichTextDelegate(&table));

    // 添加富文本数据到表格中
    QTableWidgetItem *item1 = new QTableWidgetItem;
    item1->setData(Qt::UserRole, "<h1>标题1</h1><p>段落1</p>");
    table.setItem(0, 0, item1);

    QTableWidgetItem *item2 = new QTableWidgetItem;
    item2->setData(Qt::UserRole, "<h1>标题2</h1><p>段落2</p>");
    table.setItem(1, 0, item2);

    table.show();
    return app.exec();
}
  • 创建了一个自定义的RichTextDelegate类,继承自QTableWidgetItemDelegate
  • paint方法中,检查每个表格项的数据,如果存在自定义的富文本数据(存储在Qt::UserRole),则使用QTextDocument来渲染这些数据。
  • 将这个自定义委托设置为表格的第一列的代理。

结论

只要未来可期,今天就值得欣喜

相关推荐
一休哥助手3 分钟前
Redis 五种数据类型及底层数据结构详解
数据结构·数据库·redis
落落落sss7 分钟前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
翔云12345611 分钟前
MVCC(多版本并发控制)
数据库·mysql
简单.is.good24 分钟前
【测试】接口测试与接口自动化
开发语言·python
代码敲上天.28 分钟前
数据库语句优化
android·数据库·adb
Yvemil744 分钟前
MQ 架构设计原理与消息中间件详解(二)
开发语言·后端·ruby
盒马盒马1 小时前
Redis:zset类型
数据库·redis
程序员是干活的1 小时前
私家车开车回家过节会发生什么事情
java·开发语言·软件构建·1024程序员节
静听山水1 小时前
mysql语句执行过程
数据库·mysql
我是陈泽1 小时前
一行 Python 代码能实现什么丧心病狂的功能?圣诞树源代码
开发语言·python·程序员·编程·python教程·python学习·python教学