구문 하이라이터 예제

구문 하이라이터 예제는 간단한 구문 하이라이팅을 수행하는 방법을 보여줍니다.

구문 하이라이터 애플리케이션은 사용자 지정 구문 하이라이팅이 적용된 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() 함수를 사용하여 글꼴 굵기와 색상만 정의하겠습니다.

하이라이터 클래스 구현

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 객체에 할당하고 해당 객체를 규칙 목록에 추가합니다.

또한 동일한 접근 방식을 사용하여 따옴표와 함수에 대한 강조 표시 규칙도 정의합니다: 패턴은 정규식 형식을 가지며 관련 형식과 함께 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, 즉 "댓글 중"으로 설정합니다.

이것으로 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(QWidget *parent)
    : QMainWindow(parent)
{
    setupFileMenu();
    setupHelpMenu();
    setupEditor();

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

개인 설정 에디터() 편의 함수에서 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() 함수도 제공합니다.

예제 프로젝트 @ 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.