Beispiel für den Syntax-Highlighter

Das Syntax-Highlighter-Beispiel zeigt, wie man eine einfache Syntaxhervorhebung durchführt.

Die Syntax-Highlighter-Anwendung zeigt C++-Dateien mit benutzerdefinierter Syntaxhervorhebung an.

Das Beispiel besteht aus zwei Klassen:

  • Die Klasse Highlighter definiert und wendet die Hervorhebungsregeln an.
  • Das Widget MainWindow ist das Hauptfenster der Anwendung.

Wir werden zunächst die Klasse Highlighter betrachten, um zu sehen, wie Sie die Klasse QSyntaxHighlighter an Ihre Wünsche anpassen können. Anschließend werden wir uns die relevanten Teile der Klasse MainWindow ansehen, um zu sehen, wie Sie Ihre benutzerdefinierte Hervorhebungsklasse in einer Anwendung verwenden können.

Definition der Highlighter-Klasse

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;
};

Um eine eigene Syntaxhervorhebung zu erstellen, müssen Sie die Klasse QSyntaxHighlighter untergliedern, die Funktion highlightBlock() neu implementieren und Ihre eigenen Hervorhebungsregeln definieren.

Wir haben uns dafür entschieden, unsere Hervorhebungsregeln in einer privaten Struktur zu speichern: Eine Regel besteht aus einem QRegularExpression Muster und einer QTextCharFormat Instanz. Die verschiedenen Regeln werden dann in einer QList gespeichert.

Die Klasse QTextCharFormat bietet Formatierungsinformationen für Zeichen in einem QTextDocument, die die visuellen Eigenschaften des Textes sowie Informationen über seine Rolle in einem Hypertext-Dokument angeben. In diesem Beispiel werden wir nur die Schriftstärke und -farbe mit den Funktionen QTextCharFormat::setFontWeight() und QTextCharFormat::setForeground() definieren.

Implementierung der Highlighter-Klasse

Wenn Sie die Klasse QSyntaxHighlighter unterklassifizieren, müssen Sie den Parameter parent an den Konstruktor der Basisklasse übergeben. Das Elternteil ist das Textdokument, auf das die Syntaxhervorhebung angewendet werden soll. In diesem Beispiel haben wir uns auch dafür entschieden, unsere Hervorhebungsregeln im Konstruktor zu definieren:

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);
    }

Zunächst definieren wir eine Schlüsselwortregel, die die gängigsten C++-Schlüsselwörter erkennt. Wir geben der keywordFormat eine fette, dunkelblaue Schrift. Für jedes Schlüsselwort weisen wir das Schlüsselwort und das angegebene Format einem HighlightingRule-Objekt zu und hängen das Objekt an unsere Liste von Regeln an.

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

Dann erstellen wir ein Format, das wir auf Qt-Klassennamen anwenden werden. Die Klassennamen werden mit einer dunklen magentafarbenen Farbe und einem fetten Stil gerendert. Wir geben ein Stringmuster an, das eigentlich ein regulärer Ausdruck ist, der alle Qt-Klassennamen erfasst. Dann weisen wir den regulären Ausdruck und das angegebene Format einem HighlightingRule-Objekt zu und fügen das Objekt an unsere Liste von Regeln an.

Wir definieren auch Hervorhebungsregeln für Zitate und Funktionen mit dem gleichen Ansatz: Die Muster haben die Form von regulären Ausdrücken und werden in HighlightingRule-Objekten mit dem zugehörigen Format gespeichert.

    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("\\*/"));
}

Die Sprache C++ kennt zwei Varianten von Kommentaren: Den einzeiligen Kommentar (//) und den mehrzeiligen Kommentar (/*...*/ ). Der einzeilige Kommentar kann einfach durch eine Hervorhebungsregel definiert werden, die den vorherigen ähnlich ist. Der mehrzeilige Kommentar erfordert jedoch aufgrund des Designs der Klasse QSyntaxHighlighter besondere Sorgfalt.

Nachdem ein QSyntaxHighlighter -Objekt erstellt wurde, wird seine Funktion highlightBlock() automatisch aufgerufen, wann immer die Rich-Text-Engine dies für die Hervorhebung des angegebenen Textblocks benötigt. Das Problem tritt auf, wenn sich ein Kommentar über mehrere Textblöcke erstreckt. Wir werden uns genauer ansehen, wie dieses Problem gelöst werden kann, wenn wir uns die Implementierung der Funktion Highlighter::highlightBlock() ansehen. Zu diesem Zeitpunkt geben wir nur die Farbe des mehrzeiligen Kommentars an.

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);
        }
    }

Die Funktion highlightBlock() wird automatisch aufgerufen, wenn die Rich-Text-Engine sie benötigt, d. h. wenn sich Textblöcke geändert haben.

Zuerst wenden wir die Syntaxhervorhebungsregeln an, die wir in der Liste highlightingRules gespeichert haben. Für jede Regel (d.h. für jedes HighlightingRule-Objekt) suchen wir mit der Funktion QString::indexOf() nach dem Muster in dem angegebenen Textblock. Wenn das erste Vorkommen des Musters gefunden wird, verwenden wir die Funktion QRegularExpressionMatch::capturedLength(), um die zu formatierende Zeichenfolge zu bestimmen. QRegularExpressionMatch::capturedLength() gibt die Länge der zuletzt gefundenen Zeichenkette zurück, oder 0, wenn es keine Übereinstimmung gab.

Für die eigentliche Formatierung stellt die Klasse QSyntaxHighlighter die Funktion setFormat() zur Verfügung. Diese Funktion arbeitet mit dem Textblock, der als Argument an die Funktion highlightBlock() übergeben wird. Das angegebene Format wird auf den Text ab der angegebenen Startposition für die angegebene Länge angewendet. Die im angegebenen Format eingestellten Formatierungseigenschaften werden zur Anzeigezeit mit den direkt im Dokument gespeicherten Formatierungsinformationen zusammengeführt. Beachten Sie, dass das Dokument selbst durch das mit dieser Funktion eingestellte Format nicht verändert wird.

Dieser Vorgang wird so lange wiederholt, bis das letzte Vorkommen des Musters im aktuellen Textblock gefunden wird.

    setCurrentBlockState(0);

Um mit Konstrukten umzugehen, die sich über mehrere Textblöcke erstrecken können (wie der mehrzeilige C++-Kommentar), ist es notwendig, den Endzustand des vorherigen Textblocks zu kennen (z.B. "in comment"). Innerhalb Ihrer highlightBlock() -Implementierung können Sie den Endzustand des vorherigen Textblocks mit der Funktion QSyntaxHighlighter::previousBlockState() abfragen. Nach dem Parsen des Blocks können Sie den letzten Zustand mit QSyntaxHighlighter::setCurrentBlockState() speichern.

Die Funktion previousBlockState() gibt einen int-Wert zurück. Wenn kein Status gesetzt ist, ist der zurückgegebene Wert -1. Mit der Funktion setCurrentBlockState() können Sie einen beliebigen anderen Wert zur Kennzeichnung eines bestimmten Zustands angeben. Sobald der Status gesetzt ist, behält QTextBlock diesen Wert, bis er erneut gesetzt wird oder bis der entsprechende Textabschnitt gelöscht wird.

In diesem Beispiel haben wir den Wert 0 für den Zustand "nicht im Kommentar" und den Wert 1 für den Zustand "im Kommentar" verwendet. Wenn die gespeicherten Syntaxhervorhebungsregeln angewendet werden, wird der aktuelle Blockstatus auf 0 gesetzt.

    int startIndex = 0;
    if (previousBlockState() != 1)
        startIndex = text.indexOf(commentStartExpression);

Wenn der vorherige Blockstatus "in comment" war (previousBlockState() == 1), beginnen wir die Suche nach einem Endausdruck am Anfang des Textblocks. Gibt previousBlockState() den Wert 0 zurück, beginnt die Suche an der Stelle, an der zum ersten Mal ein Start-Ausdruck vorkommt.

    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);
    }
}

Wenn ein Endausdruck gefunden wird, wird die Länge des Kommentars berechnet und das mehrzeilige Kommentarformat angewendet. Dann suchen wir nach dem nächsten Vorkommen des Anfangsausdrucks und wiederholen den Vorgang. Wenn im aktuellen Textblock kein Endausdruck gefunden wird, setzen wir den aktuellen Blockstatus auf 1, d. h. "im Kommentar".

Damit ist die Implementierung der Klasse Highlighter abgeschlossen; sie ist nun einsatzbereit.

Definition der MainWindow-Klasse

Die Verwendung einer Unterklasse von QSyntaxHighlighter ist einfach: Geben Sie Ihrer Anwendung eine Instanz der Klasse und übergeben Sie ihr das Dokument, auf das die Hervorhebung angewendet werden soll.

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;
};

In diesem Beispiel deklarieren wir einen Zeiger auf eine Highlighter -Instanz, die wir später in der privaten Funktion setupEditor() initialisieren werden.

Implementierung der Klasse MainWindow

Der Konstruktor des Hauptfensters ist sehr einfach. Wir richten zunächst die Menüs ein, dann initialisieren wir den Editor und machen ihn zum zentralen Widget der Anwendung. Schließlich setzen wir den Titel des Hauptfensters.

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    setupFileMenu();
    setupHelpMenu();
    setupEditor();

    setCentralWidget(editor);
    setWindowTitle(tr("Syntax Highlighter"));
}

Wir initialisieren und installieren das Objekt Highlighter in der privaten Komfortfunktion setupEditor():

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

Zuerst erstellen wir die Schriftart, die wir im Editor verwenden wollen, dann erstellen wir den Editor selbst, der eine Instanz der Klasse QTextEdit ist. Bevor wir den Editor mit der Klassendefinitionsdatei MainWindow initialisieren, erstellen wir eine Instanz von Highlighter und übergeben das Dokument des Editors als Argument. Dies ist das Dokument, auf das die Hervorhebung angewendet werden soll. Dann sind wir fertig.

Ein QSyntaxHighlighter Objekt kann immer nur auf einem Dokument installiert werden, aber Sie können den Textmarker mit der Funktion QSyntaxHighlighter::setDocument() leicht auf einem anderen Dokument erneut installieren. Die Klasse QSyntaxHighlighter bietet auch die Funktion document(), die das aktuell eingestellte Dokument zurückgibt.

Beispielprojekt @ code.qt.io

© 2025 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.