语法高亮示例
语法高亮示例展示了如何执行简单的语法高亮。
语法高亮应用显示了具有自定义语法高亮的 C++ 文件。
该示例包含两个类
Highlighter
类定义并应用高亮规则。MainWindow
组件是应用程序的主窗口。
我们首先回顾 Highlighter
类,看看如何自定义 QSyntaxHighlighter 类来满足您的喜好,然后我们将浏览 MainWindow
类的相关部分,看看您如何可以在应用程序中使用您自定义的高亮类。
Highlighter 类定义
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 类的子类时,您必须将父参数传递给基类构造函数。父类是指将要应用语法高亮的文本文档。在本例中,我们还选择在构造函数中定义我们的高亮规则
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 对象,并将对象追加到规则列表中。
我们还定义了使用相同方法为引文和函数定义突出显示规则:模式具有正则表达式的形式,并以关联的格式存储在突出显示规则对象中。
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++语言有两种注释变体:单行注释(《code translate="no">//》)和多行注释(《code translate="no">/*...*//
)。可以通过类似之前的规则轻松地为单行注释定义突出显示规则。但多行注释需要特别注意,因为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
列表中的语法突出显示规则。对于每个规则(即每个突出显示规则对象),我们使用QString::indexOf()函数在给定的文本块中查找模式。当找到模式的第一个出现时,我们使用QRegularExpressionMatch::capturedLength()函数来确定将被格式化的字符串。如果找不到匹配项,QRegularExpressionMatch::capturedLength()返回0。
要执行实际的格式化,QSyntaxHighlighter类提供了一个setFormat()函数。此函数作用于传递给highlightBlock()函数的文本块。指定的格式应用于从给定起始位置到给定长度的文本。在显示时间,设置在给定格式中的格式属性与直接存储在文档中的格式信息合并。请注意,通过此函数设置的格式不会修改文档本身。
重复此过程,直到当前文本块中找到模式的最后一个出现。
setCurrentBlockState(0);
为了处理可以跨越多个文本块的构造(如C++的多行注释),有必要知道前一个文本块的结束状态(例如,"在注释中")。你可以在你的highlightBlock()
实现中,使用QSyntaxHighlighter::previousBlockState()函数查询前一个文本块的结束状态。在解析块之后,你可以使用QSyntaxHighlighter::setCurrentBlockState()保存最后的状态。
previousBlockState()函数返回一个整数值。如果未设置状态,则返回值为-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,即“在注释中”。
这完成了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()函数轻松地将高亮器重新安装到另一个文档上。《a href="qsyntaxhighlighter.html" translate="no">QSyntaxHighlighter类还提供了一个document()函数,它返回当前设置的文档。
© 2024 Qt公司。在此包含的文档贡献的版权属于各自的所有者。在此提供的文档是根据自由软件基金会发布的GNU自由文档许可证第1.3版条款许可的。