Qt開發技術:Qt富文本(二)Qt文本光標操作、文檔佈局、富文本編輯和處理

若該文爲原創文章,未經允許不得轉載
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客導航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:
各位讀者,知識無窮而人力有窮,要麼改需求,要麼找專業人士,要麼自己研究

目錄

前話

文本光標接口

概述

基於光標的編輯

使用光標

分組光標操作

多個光標

插入文檔元素

文本和文本片段

文本塊:QTextBlock

文本文本框架:QTextFrame

表格:QTextTable

圖像:QTextImageFormat

示例:createCalendarDemo

效果

關鍵代碼

文檔佈局

概述

示例:LayoutDemo

關鍵代碼

常見的富文本編輯任務

概述

常見編輯任務

使用QTextEdit

選擇文本

查找文本

打印文檔

高級富文本

處理大文件

工程模板v1.0.0

下載工程模板


Qt開發專欄:開發技術(傳送門)

《Qt開發技術:Qt富文本(一)富文本介紹、文檔結構》

《Qt開發技術:Qt富文本(二)Qt文本光標操作、文檔佈局、富文本編輯和處理以及Demo》

《Qt開發技術:Qt富文本(三)Qt支持的HTML子集(查詢手冊)以及涉及的類》

 

    Qt開發技術:Qt富文本(二)Qt文本光標操作、文檔佈局、富文本編輯和處理

 

前話

      紅胖子,來也!

      Qt的富文本技術介紹,文本光標操作、文檔佈局、富文本常用操作和處理大文件富文本。

 

文本光標接口

概述

文檔可以通過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);

一旦字符格式與光標一起使用,該格式將成爲隨光標插入的任何文本的默認格式,直到指定了其他字符格式。

如果使用光標插入文本而不指定字符格式,則將爲文本提供文檔中該位置使用的字符格式。

文本塊:QTextBlock

      文本塊使用insertBlock()函數插入到文檔中。

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

文本文本框架:QTextFrame

      使用光標插入到文檔中,並將放置在當前塊之後光標的當前框架內。下面的代碼演示如何在文檔根框架中的兩個文本塊之間插入框架。我們首先找到光標的當前文本框架:

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(...);

最後添加的文本將插入文檔的子框架之後。由於每個文本框架都用文本塊填充,這確保了可以始終用光標插入更多的元素。

表格:QTextTable

表格使用光標插入到文檔中,並將放置在當前塊之後光標的當前文本框架中:

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

可以使用特定格式創建表,該格式定義表的總體屬性,例如其對齊方式、背景顏色和使用的單元格間距。它還可以確定每列上的約束,允許每列具有固定的寬度,或者根據可用空間調整大小。

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

上面的代碼首先檢查光標是否在現有列表中,如果是,則爲新列表的列表格式提供適當的縮進級別。這允許創建嵌套列表,並增加縮進級別。一個更復雜的實現還將使用不同類型的符號來表示列表中每個級別的要點。

圖像:QTextImageFormat

內聯圖像以通常的方式通過光標添加到文檔中。與許多其他元素不同,所有圖像屬性都由圖像的格式指定。這意味着必須先創建QTextImageFormat對象,然後才能插入圖像:

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

圖片名稱指的是應用程序資源文件中的一個條目(:/xxxx)。Qt資源系統中描述了用於派生此名稱的方法。

示例:createCalendarDemo

效果

關鍵代碼

修改富文本

void CalendarWidget::modifyRichText()
{
    ui->textEdit_dst->setText(ui->textEdit_src->toHtml());
    QTextDocument *pDocument = ui->textEdit_dst->document();
    // 獲取光標
    QTextCursor cursor(pDocument);
    // 光標移動到最開始
    cursor.movePosition(QTextCursor::Start);
    // 光標移動到本行末尾,保持光標原來的位置,這樣等於選中了本行
    cursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
    // 設置字體加速
    QTextCharFormat format;
    format.setFontWeight(QFont::Bold);
    cursor.mergeCharFormat(format);

    // 再移動到網頁鏈接的(0、1、2,第三行),斜體,加粗,紅色,24號字體
    // 光標移動到最開始
    cursor.movePosition(QTextCursor::Start);
    // 光標移動2次,第3行,下標爲2(0,1,2)
    cursor.movePosition(QTextCursor::NextBlock);
    cursor.movePosition(QTextCursor::NextBlock);
    // 光標移動到本行末尾,保持光標原來的位置,這樣等於選中了本行
    cursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
    // 設置字體斜體、加粗、紅色
    QTextCharFormat format2;
    QFont font = format2.font();
    // 斜體
    font.setStyle(QFont::StyleItalic);
    // 粗體
    font.setBold(true);
    // 字體大小:24號
    font.setPixelSize(24);
    // 前景色:黑色
    format2.setForeground(QBrush(Qt::red));
    format2.setFont(font);
    cursor.mergeCharFormat(format2);

    // 光標移動到最開始
    cursor.movePosition(QTextCursor::Start);
    // 光標移動4次,第5行,下標爲2(0,1,2,3,4)
    cursor.movePosition(QTextCursor::NextBlock);
    cursor.movePosition(QTextCursor::NextBlock);
    cursor.movePosition(QTextCursor::NextBlock);
    cursor.movePosition(QTextCursor::EndOfBlock);
    cursor.insertBlock();
    // 插入圖片
    QTextImageFormat textImageFormat;
    textImageFormat.setName(":/images/images/1.jpg");
    textImageFormat.setHeight(200);
    textImageFormat.setWidth(200);
    cursor.insertImage(textImageFormat);
    // 插入gif,顯示時,沒有動畫
    textImageFormat.setName(":/images/images/1.gif");
    cursor.insertImage(textImageFormat);
}

生成日曆

void CalendarWidget::createCalendar()
{
    QTextEdit *pEditor = ui->textEdit_canlendar;

    // 獲取光標
    QTextCursor cursor(pEditor->textCursor());
    cursor.movePosition(QTextCursor::Start);

    // 獲取光標的字符屬性1,設置了字體
    QTextCharFormat format(cursor.charFormat());
    format.setFontFamily("Courier");

    // 獲取光標的字符屬性2,設置了字體,加粗了字體
    QTextCharFormat boldFormat = format;
    boldFormat.setFontWeight(QFont::Bold);

    // 插入空文本塊(可理解爲插入一個空行),從第0行開始,光標插入空文本塊後,第0行爲空行,光標到達第1行
    cursor.insertBlock();
    // 插入空格(帶不帶字體沒什麼關係)
    cursor.insertText(" ");
    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(QDate::shortDayName(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);
        }
    }
}

 

文檔佈局

概述

文檔的佈局僅在要在設備上顯示時才相關,或者在請求某些需要文檔可視化表示的信息時才相關。在此之前,不需要爲設備格式化和準備文檔。

每個文檔的佈局都由QAbstractTextDocumentLayout類的一個子類管理。這個類爲佈局和呈現引擎提供了一個公共接口。默認呈現行爲當前在私有類中實現。

這種方法使創建自定義佈局成爲可能,並提供了在準備打印頁面或導出爲可移植文檔格式(PDF)文件時使用的機制。

示例:LayoutDemo

關鍵代碼

(在paintEvent中調用該函數)

void LayoutWidget::paintLayout()
{
    QString text = "Hello world!!!";
    for(int index = 0; index < 200; index++)
    {
        text += "Hello world!!!";
    }
    QFont font = this->font();
    QTextLayout textLayout(text, font);
    qreal margin = 10;
    qreal radius = qMin(width()/2.0, height()/2.0) - margin;
    QFontMetrics fm(font);

    qreal lineHeight = fm.height();
    qreal y = 0;

    textLayout.beginLayout();

    while(true)
    {
        // 創建一根直線
        QTextLine line = textLayout.createLine();
        if (!line.isValid())
        {
            break;
        }
        // 計算橢圓有邊界x的值
        qreal x1 = qMax(0.0, pow(pow(radius,2)-pow(radius-y,2), 0.5));
        qreal x2 = qMax(0.0, pow(pow(radius,2)-pow(radius-(y+lineHeight),2), 0.5));
        qreal x = qMax(x1, x2) + margin;
        qreal lineWidth = (width() - margin) - x;

        // 設置每一行textLine的右邊的開始座標
        line.setLineWidth(lineWidth);
        line.setPosition(QPointF(x, margin+y));
        y += line.height();
    }

    textLayout.endLayout();

    QPainter painter;
    painter.begin(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.fillRect(rect(), Qt::white);
    painter.setBrush(QBrush(Qt::black));
    painter.setPen(QPen(Qt::black));
    textLayout.draw(&painter, QPoint(0,0));

    painter.setBrush(QBrush(QColor("#a6ce39")));
    painter.setPen(QPen(Qt::black));
    painter.drawEllipse(QRectF(-radius, margin, 2*radius, 2*radius));
    painter.end();
}

 

常見的富文本編輯任務

概述

在使用Qt編輯和處理文本文檔時,開發人員經常執行許多任務。其中包括使用顯示小部件,如QTextBrowser和QTextEdit,使用QTextDocument創建文檔,使用QTextCursor編輯,以及導出文檔結構

常見編輯任務

使用QTextEdit

可以構造文本編輯器小部件,並使用它以以下方式顯示HTML:

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

默認情況下,文本編輯器包含一個帶有根框架的文檔,其中包含一個空文本塊。本文件可通過以下方式獲取,以便直接由應用程序修改:

QTextDocument *document = editor->document();

文本編輯器的光標也可用於編輯文檔:

QTextCursor cursor = editor->textCursor();

雖然可以同時使用多個光標編輯文檔,但QTextEdit一次只顯示一個光標(閃光標)。因此,如果要更新編輯器以顯示特定光標或其選擇,則需要在修改文檔後設置編輯器光標:

editor->setTextCursor(cursor);

選擇文本

通過使用類似於用戶在文本編輯器中執行的操作移動光標來選擇文本。要在文檔中的兩點之間選擇文本,需要將光標定位在第一點,然後使用帶有移動操作(QTextCursor::MoveOperation)的特殊模式(QTextCursor::MoveMode)移動它。

當我們選擇文本時,我們將選擇錨定保留在舊的光標位置,就像用戶在選擇文本時按住Shift鍵一樣:

cursor.movePosition(QTextCursor::StartOfWord);
cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);

在上面的代碼中,使用此方法選擇整個單詞。QTextCursor提供了許多常用的移動操作,用於選擇單個字符、單詞、行和整個塊。

查找文本

QTextDocument提供了一個基於光標的搜索界面,使得在文本編輯器的樣式中很容易找到和修改文本。以下代碼查找文檔中特定單詞的所有實例,並更改每個實例的顏色:

QTextCursor newCursor(document);
while (!newCursor.isNull() && !newCursor.atEnd())
{
    newCursor = document->find(searchString, newCursor);
    if (!newCursor.isNull())
    {
        newCursor.movePosition(QTextCursor::WordRight,
                             QTextCursor::KeepAnchor);
        newCursor.mergeCharFormat(colorFormat);
    }
}

請注意,在每次搜索和替換操作之後,不必移動光標;它始終位於剛剛替換的單詞的末尾。

打印文檔

QTextEdit是爲顯示在屏幕上讀取的大型富文本文檔而設計的,其呈現方式與web瀏覽器相同。因此,它不會自動將文檔內容分成適合打印的頁面大小的部分。

QTextDocument提供了print()函數,允許使用QPrinter類打印文檔。下面的代碼演示如何在QTextEdit中準備文檔以便使用QPrinter打印:

QTextDocument *document = editor->document();
QPrinter printer;
QPrintDialog *dlg = new QPrintDialog(&printer, this);
if (dlg->exec() != QDialog::Accepted)
{
   return;
}
document->print(&printer);

從文本編輯器獲取文檔,然後使用QPrintDialog構造QPrinter並進行配置。如果用戶接受打印機的配置,則使用print()函數格式化並打印文檔。

 

高級富文本

處理大文件

Qt不限制用於文本處理的文件的大小。在大多數情況下,這不會造成問題。但是,對於特別大的文件,可能會遇到應用程序將變得無響應或內存不足的情況。可以加載的文件大小取決於硬件、Qt和應用程序的實現。

面臨此問題,建議採取以下解決方案:

  • 考慮將大段落分解成小段落,因爲Qt更好地處理小段落。可以按固定的間隔插入換行符,這看起來與QTextEdit中的一個大段落相同。
  • 可以使用maximumBlockCount()減少QTextDocument中的塊數量。就QTextEdit而言,文檔只與塊的數量一樣大。
  • 將文本添加到文本編輯時,將其添加到編輯塊中是一種優勢(請參見下面的示例)。結果是文本編輯不需要一次構建整個文檔結構。

給出了以上最後一種技術的示例代碼,假設文本編輯是可見的:

textEdit.show();
textCursor.beginEditBlock();
for (int i = 0; i < 1000; ++i)
{
    textCursor.insertBlock();
    textCursor.insertText(paragraphText.at(i));
}
textCursor.endEditBlock();

 

工程模板v1.0.0

富文本工程模板v1.0.0:附帶2個子Demo。

 

下載工程模板

CSDN:https://download.csdn.net/download/qq21497936/12336683

QQ羣:1047134658(點擊“文件”搜索“qtRichTextDemo”,羣內與博文同步更新所有可開源的源碼模板)

 

原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客導航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章