如何强制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
相关产品推荐
相关产品推荐

