You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何强制QPlainTextEdit立即更新文本格式?

问题描述

我在连接QPlainTextEdit::textChanged()信号的槽函数中,通过以下代码实现语法高亮:

struct Format {
    QTextBlock block;
    QList<QTextLayout::FormatRange> format;
};

//! Formats.
QMap<int, Format> formats;

void clearFormats()
{
    for( const auto & f : std::as_const( formats ) )
        f.block.layout()->clearFormats();

    formats.clear();
}

void applyFormats()
{
    for( const auto & f : std::as_const( formats ) )
        f.block.layout()->setFormats( f.format );
}

void setFormat( const QTextCharFormat & format,
    long long int startLine, long long int startColumn,
    long long int endLine, long long int endColumn )
{
    for( auto i = startLine; i <= endLine; ++i )
    {
        formats[ i ].block = editor->document()->findBlockByNumber( i );

        QTextLayout::FormatRange r;
        r.format = format;
        r.start = ( i == startLine ? startColumn : 0 );
        r.length = ( i == startLine ?
            ( i == endLine ? endColumn - startColumn + 1 :
                formats[ i ].block.length() - startColumn ) :
            ( i == endLine ? endColumn + 1 : formats[ i ].block.length() ) );

        formats[ i ].format.push_back( r );
    }
}

每次文本变更时,我执行以下流程:

clearFormats();

setFormat( ... ); // 根据文本内容按需调用

applyFormats()

在编辑器输入文本时,语法高亮正常工作。但当我通过设置对话框修改高亮颜色后,执行清除格式、设置新颜色格式、应用格式的流程后,颜色并未立即更新,必须手动在编辑器输入内容才会生效。

请问:如何无需手动输入,强制QPlainTextEdit应用新格式?

最小可复现代码

#include <QPlainTextEdit>
#include <QApplication>
#include <QVBoxLayout>
#include <QPushButton>
#include <QTextCharFormat>
#include <QTextDocument>
#include <QTextBlock>


class Edit
    :   public QPlainTextEdit
{
    Q_OBJECT

public:
    Edit()
    {
        connect( this, &QPlainTextEdit::textChanged,
            this, &Edit::onTextChanged );
    }

    ~Edit() override = default;

public slots:
    void changeColor()
    {
        if( color == Qt::red )
            color = Qt::green;
        else
            color = Qt::red;

        emit textChanged();
    }

private slots:
    void onTextChanged()
    {
        clearFormats();
        setFormat();
    }

private:
    void clearFormats()
    {
        auto b = document()->firstBlock();

        while( b.isValid() )
        {
            b.layout()->clearFormats();

            b = b.next();
        }
    }

    void setFormat()
    {
        auto b = document()->firstBlock();

        QTextCharFormat f;
        f.setForeground( color );

        while( b.isValid() )
        {
            QTextLayout::FormatRange r;
            r.format = f;
            r.start = 0;
            r.length = b.length();

            b.layout()->setFormats( { r } );

            b = b.next();
        }
    }

private:
    QColor color = Qt::red;
};

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

    QWidget w;
    QVBoxLayout l( &w );

    Edit e;
    l.addWidget( &e );
    QPushButton b;
    b.setText( "Change color" );
    l.addWidget( &b );
    b.connect( &b, &QPushButton::clicked, &e, &Edit::changeColor );

    w.resize( 800, 600 );
    w.show();


    return QApplication::exec();
}

#include "main.moc"
解决方案

问题核心是仅修改QTextLayout的格式后,QPlainTextEdit不会自动触发重绘,需要手动通知编辑器更新显示。以下是两种可行的解决方式:

方式一:调用update()强制全局重绘

在完成所有格式设置后,调用QPlainTextEdit的update()方法,强制整个编辑器区域重绘:

void setFormat()
{
    auto b = document()->firstBlock();

    QTextCharFormat f;
    f.setForeground( color );

    while( b.isValid() )
    {
        QTextLayout::FormatRange r;
        r.format = f;
        r.start = 0;
        r.length = b.length();

        b.layout()->setFormats( { r } );

        b = b.next();
    }

    // 强制编辑器重绘
    update();
}

方式二:触发单个文本块的布局更新

对每个修改了格式的文本块,调用QTextBlock::setUserData()(即使传入原有的userData),让Qt识别到文本块布局需要更新,从而触发局部重绘:

void setFormat()
{
    auto b = document()->firstBlock();

    QTextCharFormat f;
    f.setForeground( color );

    while( b.isValid() )
    {
        QTextLayout::FormatRange r;
        r.format = f;
        r.start = 0;
        r.length = b.length();

        b.layout()->setFormats( { r } );
        // 触发文本块布局更新
        b.setUserData( b.userData() );

        b = b.next();
    }
}

补充说明

在最小复现示例中,changeColor()里直接emit textChanged()虽然能触发槽函数,但因为文本内容无实际变化,Qt可能会忽略部分更新逻辑。上述两种方式都能绕过这一限制,直接强制更新格式显示。

内容的提问来源于stack exchange,提问作者Igor Mironchik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 12:03:30