语法高亮示例
“语法高亮”示例演示了如何实现简单的语法高亮功能。

语法高亮应用程序会以自定义语法高亮的方式显示 C++ 文件。
该示例由两个类组成:
Highlighter类负责定义和应用高亮规则。MainWindow控件是应用程序的主窗口。
我们将首先回顾Highlighter 类,了解如何根据个人偏好自定义QSyntaxHighlighter 类,然后查看MainWindow 类的相关部分,了解如何在应用程序中使用自定义的语法高亮类。
高亮类定义
class Highlighter : public QSyntaxHighlighter
{
Q_OBJECT
public:
Highlighter(QTextDocument *parent = nullptr);
protected:
void highlightBlock(const QString &text) override;
private:
struct HighlightingRule
{
QRegularExpression pattern;
QTextCharFormat format;
};
QList<HighlightingRule> highlightingRules;
QRegularExpression commentStartExpression;
QRegularExpression commentEndExpression;
QTextCharFormat keywordFormat;
QTextCharFormat classFormat;
QTextCharFormat singleLineCommentFormat;
QTextCharFormat multiLineCommentFormat;
QTextCharFormat quotationFormat;
QTextCharFormat functionFormat;
};若要实现自定义语法高亮,您必须继承QSyntaxHighlighter 类,重写highlightBlock() 函数,并定义自己的高亮规则。
我们选择使用一个私有结构体来存储高亮规则:一条规则由一个QRegularExpression 模式和一个QTextCharFormat 实例组成。随后,各种规则通过QList 进行存储。
QTextCharFormat 类为QTextDocument 中的字符提供格式化信息,指定文本的视觉属性,以及其在超文本文档中的作用。在此示例中,我们将仅使用QTextCharFormat::setFontWeight()和QTextCharFormat::setForeground()函数来定义字体的粗细和颜色。
Highlighter 类的实现
在继承QSyntaxHighlighter 类时,必须将 parent 参数传递给基类构造函数。parent 表示将应用语法高亮效果的文本文档。在本示例中,我们还选择在构造函数中定义高亮规则:
Highlighter::Highlighter(QTextDocument *parent)
: QSyntaxHighlighter(parent)
{
HighlightingRule rule;
keywordFormat.setForeground(Qt::darkBlue);
keywordFormat.setFontWeight(QFont::Bold);
const QString keywordPatterns[] = {
QStringLiteral("\\bchar\\b"), QStringLiteral("\\bclass\\b"), QStringLiteral("\\bconst\\b"),
QStringLiteral("\\bdouble\\b"), QStringLiteral("\\benum\\b"), QStringLiteral("\\bexplicit\\b"),
QStringLiteral("\\bfriend\\b"), QStringLiteral("\\binline\\b"), QStringLiteral("\\bint\\b"),
QStringLiteral("\\blong\\b"), QStringLiteral("\\bnamespace\\b"), QStringLiteral("\\boperator\\b"),
QStringLiteral("\\bprivate\\b"), QStringLiteral("\\bprotected\\b"), QStringLiteral("\\bpublic\\b"),
QStringLiteral("\\bshort\\b"), QStringLiteral("\\bsignals\\b"), QStringLiteral("\\bsigned\\b"),
QStringLiteral("\\bslots\\b"), QStringLiteral("\\bstatic\\b"), QStringLiteral("\\bstruct\\b"),
QStringLiteral("\\btemplate\\b"), QStringLiteral("\\btypedef\\b"), QStringLiteral("\\btypename\\b"),
QStringLiteral("\\bunion\\b"), QStringLiteral("\\bunsigned\\b"), QStringLiteral("\\bvirtual\\b"),
QStringLiteral("\\bvoid\\b"), QStringLiteral("\\bvolatile\\b"), QStringLiteral("\\bbool\\b")
};
for (const QString &pattern : keywordPatterns) {
rule.pattern = QRegularExpression(pattern);
rule.format = keywordFormat;
highlightingRules.append(rule);
}首先,我们定义了一条关键字规则,用于识别最常见的 C++ 关键字。我们将这些keywordFormat 设置为粗体、深蓝色的字体。对于每个关键字,我们将该关键字和指定的格式赋值给一个 HighlightingRule 对象,并将该对象追加到我们的规则列表中。
classFormat.setFontWeight(QFont::Bold);
classFormat.setForeground(Qt::darkMagenta);
rule.pattern = QRegularExpression(QStringLiteral("\\bQ[A-Za-z]+\\b"));
rule.format = classFormat;
highlightingRules.append(rule);
quotationFormat.setForeground(Qt::darkGreen);
rule.pattern = QRegularExpression(QStringLiteral("\".*\""));
rule.format = quotationFormat;
highlightingRules.append(rule);
functionFormat.setFontItalic(true);
functionFormat.setForeground(Qt::blue);
rule.pattern = QRegularExpression(QStringLiteral("\\b[A-Za-z0-9_]+(?=\\()"));
rule.format = functionFormat;
highlightingRules.append(rule);接着,我们创建一种将应用于 Qt 类名的格式。类名将以深洋红色和粗体样式显示。 我们指定了一个字符串模式,该模式实际上是一个正则表达式,用于捕获所有 Qt 类名。然后,我们将该正则表达式与指定的格式赋值给一个 HighlightingRule 对象,并将该对象追加到规则列表中。
我们还采用相同的方法为引用和函数定义高亮规则:这些模式采用正则表达式的形式,并连同关联的格式一起存储在 HighlightingRule 对象中。
singleLineCommentFormat.setForeground(Qt::red);
rule.pattern = QRegularExpression(QStringLiteral("//[^\n]*"));
rule.format = singleLineCommentFormat;
highlightingRules.append(rule);
multiLineCommentFormat.setForeground(Qt::red);
commentStartExpression = QRegularExpression(QStringLiteral("/\\*"));
commentEndExpression = QRegularExpression(QStringLiteral("\\*/"));
}C++ 语言有两种注释形式:单行注释(// )和多行注释(/*...*/ )。单行注释可以通过与前述类似的高亮规则轻松定义。但由于 `QSyntaxHighlighter ` 类的设计,多行注释需要特别注意。
创建QSyntaxHighlighter 对象后,每当富文本引擎需要时,都会自动调用其highlightBlock()函数,并对给定的文本块进行高亮显示。当注释跨越多个文本块时,问题便会出现。 在审查Highlighter::highlightBlock() 函数的实现时,我们将详细探讨如何解决此问题。目前,我们仅指定多行注释的颜色。
void Highlighter::highlightBlock(const QString &text)
{
for (const HighlightingRule &rule : std::as_const(highlightingRules)) {
QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text);
while (matchIterator.hasNext()) {
QRegularExpressionMatch match = matchIterator.next();
setFormat(match.capturedStart(), match.capturedLength(), rule.format);
}
}每当富文本引擎认为有必要时(即存在已更改的文本块时),都会自动调用 highlightBlock() 函数。
首先,我们应用存储在highlightingRules 列表中的语法高亮规则。对于每条规则(即每个HighlightingRule对象),我们使用QString::indexOf()函数在给定的文本块中搜索该模式。 当找到模式的首次出现时,我们使用QRegularExpressionMatch::capturedLength()函数来确定待格式化的字符串。QRegularExpressionMatch::capturedLength()返回最后匹配的字符串的长度,如果没有匹配则返回0。
为了执行实际的格式化操作,QSyntaxHighlighter 类提供了setFormat()函数。该函数对作为highlightBlock() 函数参数传递的文本块进行操作。 指定的格式将从给定的起始位置开始,按给定的长度应用于文本。在显示时,给定格式中设置的格式化属性将与直接存储在文档中的格式化信息合并。请注意,文档本身不会因通过此函数设置的格式而发生改变。
该过程将重复进行,直到在当前文本块中找到该模式的最后一次出现为止。
setCurrentBlockState(0);为了处理可能跨越多个文本块的结构(如 C++ 多行注释),必须知道前一个文本块的结束状态(例如“in comment”)。 在highlightBlock() 的实现中,您可以使用QSyntaxHighlighter::previousBlockState()函数查询前一个文本块的结束状态。解析完该文本块后,可以使用QSyntaxHighlighter::setCurrentBlockState()保存最后的状态。
previousBlockState() 函数返回一个 int 值。如果未设置状态,返回值为 -1。您可以使用setCurrentBlockState() 函数指定任意其他值来标识特定状态。一旦状态被设置,QTextBlock 会保留该值,直到该状态被重新设置或相应的文本段落被删除为止。
在此示例中,我们选择使用 0 表示“非注释”状态,使用 1 表示“注释”状态。当应用存储的语法高亮规则时,我们会将当前代码块状态初始化为 0。
int startIndex = 0;
if (previousBlockState() != 1)
startIndex = text.indexOf(commentStartExpression);如果前一个代码块状态为“在注释中”(previousBlockState() == 1 ),则从文本块开头开始搜索结束表达式。如果 previousBlockState() 返回 0,则从首次出现开始表达式的位置开始搜索。
while (startIndex >= 0) {
QRegularExpressionMatch match = commentEndExpression.match(text, startIndex);
int endIndex = match.capturedStart();
int commentLength = 0;
if (endIndex == -1) {
setCurrentBlockState(1);
commentLength = text.length() - startIndex;
} else {
commentLength = endIndex - startIndex
+ match.capturedLength();
}
setFormat(startIndex, commentLength, multiLineCommentFormat);
startIndex = text.indexOf(commentStartExpression, startIndex + commentLength);
}
}当找到结束表达式时,我们计算注释的长度并应用多行注释格式。然后,我们搜索下一个开始表达式的出现位置并重复该过程。如果在当前文本块中找不到结束表达式,则将当前块状态设置为 1,即“in comment”。
至此,Highlighter 类的实现已完成;该类现已可供使用。
MainWindow 类定义
使用QSyntaxHighlighter 的子类非常简单;只需为应用程序提供该类的实例,并向其传递需要进行高亮显示的文档即可。
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
public slots:
void about();
void newFile();
void openFile(const QString &path = QString());
private:
void setupEditor();
void setupFileMenu();
void setupHelpMenu();
QTextEdit *editor;
Highlighter *highlighter;
};在此示例中,我们声明了一个指向Highlighter 实例的指针,稍后将在私有函数setupEditor() 中对其进行初始化。
MainWindow 类的实现
主窗口的构造函数非常直观。我们首先设置菜单,然后初始化编辑器并将其设为应用程序的核心控件。最后,我们设置主窗口的标题。
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
setupFileMenu();
setupHelpMenu();
setupEditor();
setCentralWidget(editor);
setWindowTitle(tr("Syntax Highlighter"));
}我们在私有辅助函数 setupEditor() 中初始化并安装Highlighter 对象:
void MainWindow::setupEditor()
{
QFont font;
font.setFamily("Courier");
font.setFixedPitch(true);
font.setPointSize(10);
editor = new QTextEdit;
editor->setFont(font);
highlighter = new Highlighter(editor->document());
QFile file("mainwindow.h");
if (file.open(QFile::ReadOnly | QFile::Text))
editor->setPlainText(file.readAll());
}首先,我们创建要在编辑器中使用的字体,然后创建编辑器本身——它是QTextEdit 类的实例。在使用MainWindow 类定义文件初始化编辑器之前,我们会创建一个Highlighter 实例,并将编辑器的文档作为参数传入。这就是将应用高亮效果的文档。至此,初始化工作就完成了。
一个QSyntaxHighlighter 对象一次只能安装在一个文档上,但你可以使用QSyntaxHighlighter::setDocument()函数轻松地将高亮器重新安装到另一个文档上。QSyntaxHighlighter 类还提供了document()函数,该函数返回当前设置的文档。
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.