Пример Syntax Highlighter
Файлы:
Пример Syntax Highlighter показывает как исполнить простую подсветку синтаксиса наследованием класса QSyntaxHighlighter. Приложение Syntax Highlighter отображает файлы C++ с пользовательской подсветкой синтаксиса. Пример состоит из двух классов:
Сначала мы рассмотрим класс Highlighter что бы посмотреть как вы можете настроить класс QSyntaxHighlighter под свои требования, затем мы взглянем на важные части класса MainWindow что бы увидеть как вы можете использовать ваш собственный класс подсветки в приложении. Определение класса Highlighterclass Highlighter : public QSyntaxHighlighter { Q_OBJECT public: Highlighter(QTextDocument *parent = 0); protected: void highlightBlock(const QString &text); private: struct HighlightingRule { QRegExp pattern; QTextCharFormat format; }; QVector<HighlightingRule> highlightingRules; QRegExp commentStartExpression; QRegExp commentEndExpression; QTextCharFormat keywordFormat; QTextCharFormat classFormat; QTextCharFormat singleLineCommentFormat; QTextCharFormat multiLineCommentFormat; QTextCharFormat quotationFormat; QTextCharFormat functionFormat; }; Для обеспечения вашего собственного синтаксиса подсветки вы должны наследовать QSyntaxHighlighter, переопределить функцию highlightBlock() и определить собственные правила подсветки. Мы решили хранить наши правила подсветки с помощью приватной структуры: правило состоит из шаблона QRegExp и экземпляра QTextCharFormat. Различные правила хранится с использованием QVector. Класс QTextCharFormat предоставляет информацию о форматировании для символов в QTextDocument указывая визуальные свойства текста так же как и информацию о его роли в гипертекстовом документе. В этом примере мы определим только размер шрифта и цвет, используя функции QTextCharFormat::setFontWeight() и QTextCharFormat::setForeground(). Реализация класса HighlighterПри наследовании класса QSyntaxHighlighter вы должны передать параметр parent в конструктор базового класса. Родителем является текстовый документ на котором будет применена подсветка синтаксиса. В этом примере мы так же решили определить наши правила подсветки в конструкторе: Highlighter::Highlighter(QTextDocument *parent) : QSyntaxHighlighter(parent) { HighlightingRule rule; keywordFormat.setForeground(Qt::darkBlue); keywordFormat.setFontWeight(QFont::Bold); QStringList keywordPatterns; keywordPatterns << "\\bchar\\b" << "\\bclass\\b" << "\\bconst\\b" << "\\bdouble\\b" << "\\benum\\b" << "\\bexplicit\\b" << "\\bfriend\\b" << "\\binline\\b" << "\\bint\\b" << "\\blong\\b" << "\\bnamespace\\b" << "\\boperator\\b" << "\\bprivate\\b" << "\\bprotected\\b" << "\\bpublic\\b" << "\\bshort\\b" << "\\bsignals\\b" << "\\bsigned\\b" << "\\bslots\\b" << "\\bstatic\\b" << "\\bstruct\\b" << "\\btemplate\\b" << "\\btypedef\\b" << "\\btypename\\b" << "\\bunion\\b" << "\\bunsigned\\b" << "\\bvirtual\\b" << "\\bvoid\\b" << "\\bvolatile\\b"; foreach (QString pattern, keywordPatterns) { rule.pattern = QRegExp(pattern); rule.format = keywordFormat; highlightingRules.append(rule); } Сначала мы определяем правило ключевого слова которое распознает наиболее общие ключевые слова C++. Мы дали keywordFormat полужирный, темно-голубой шрифт. Для каждого ключевого слова мы назначаем ключевое слово в указанном формате для объекта HighlightingRule и добавляем объект в наш список правил. classFormat.setFontWeight(QFont::Bold); classFormat.setForeground(Qt::darkMagenta); rule.pattern = QRegExp("\\bQ[A-Za-z]+\\b"); rule.format = classFormat; highlightingRules.append(rule); quotationFormat.setForeground(Qt::darkGreen); rule.pattern = QRegExp("\".*\""); rule.format = quotationFormat; highlightingRules.append(rule); functionFormat.setFontItalic(true); functionFormat.setForeground(Qt::blue); rule.pattern = QRegExp("\\b[A-Za-z0-9_]+(?=\\()"); rule.format = functionFormat; highlightingRules.append(rule); Затем мы создаем формат который мы применим к именам классов Qt. Имена классов будут отображены темно-малиновым цветом и полужирным стилем. Мы указываем строку шаблона который на самом деле является регулярным выражением, охватывающим все имена классов Qt. Затем мы присваиваем это регулярное выражение и указанный формат объекту HighlightingRule и добавляем его в наш список правил. Мы так же определяем правила подсветки для цитат и функций используя тот же подход: шаблоны имеют форму регулярного выражения и хранятся в объектах HighlightingRule со связанными форматами. singleLineCommentFormat.setForeground(Qt::red);
rule.pattern = QRegExp("//[^\n]*");
rule.format = singleLineCommentFormat;
highlightingRules.append(rule);
multiLineCommentFormat.setForeground(Qt::red);
commentStartExpression = QRegExp("/\\*");
commentEndExpression = QRegExp("\\*/");
}
Язык C++ имеет два вида комментариев: однострочный комментарий (//) и многострочный (/*...*/). Однострочный комментарий может быть легко определен с помощью правила подсветки похожего на предыдущее. Но многострочные комментарии нуждаются в специальном обращении из-за дизайна класса QSyntaxHighlighter. После того как объект QSyntaxHighlighter создан, его функция highlightBlock() будет вызываться автоматически когда это необходимо движком форматированного текста, подсвечивая выделенный текстовый блок. Проблема появляется когда комментарий распространяется на несколько текстовых блоков. Мы взглянем ближе как эта проблема может быть решена пересмотром реализации функции Highlighter::highlightBlock(). В этой точке мы только укажем цвет многострочного комментария. void Highlighter::highlightBlock(const QString &text) { foreach (HighlightingRule rule, highlightingRules) { QRegExp expression(rule.pattern); int index = text.indexOf(expression); while (index >= 0) { int length = expression.matchedLength(); setFormat(index, length, rule.format); index = text.indexOf(expression, index + length); } } Функция highlightBlock() вызывается автоматически когда это необходимо движком форматированного текста, т.е. когда есть текстовые блоки которые изменились. Сначала мы применяем правила подсветки синтаксиса которые мы сохранили в векторе highlightingRules. Для каждого правила (т.е. для каждого объекта HighlightingRule) мы ищем шаблон в заданном текстовом блоке используя функцию QString::indexOf(). Когда первое вхождение шаблона найдено, мы используем функцию QRegExp::matchedLength() для определения строки которая будет отформатирована. QRegExp::matchedLength() возвращает длину последней подходящей строки или -1 если совпадений не найдено. Для выполнения фактического форматирования класс 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) { int endIndex = text.indexOf(commentEndExpression, startIndex); int commentLength; if (endIndex == -1) { setCurrentBlockState(1); commentLength = text.length() - startIndex; } else { commentLength = endIndex - startIndex + commentEndExpression.matchedLength(); } setFormat(startIndex, commentLength, multiLineCommentFormat); startIndex = text.indexOf(commentStartExpression, startIndex + commentLength); } } Когда завершающее выражение найдено, мы вычисляем длину комментария и применяем формат многострочного комментария. Затем мы ищем следующее вхождение начального выражения и повторяем процесс. Если завершающих выражений не может быть найдено в текущем текстовом блоке, мы устанавливаем состояние текущего текстового блока равным 1, т.е. "в комментарии". Это завершает реализацию класса Highlighter; теперь он готов к использованию. Определение класса MainWindowИспользовать подкласс QSyntaxHighlighter просто; просто предоставьте вашему приложению экземпляр класса и передайте ему документ, над которым вы хотите применить подсветку. class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = 0); 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")); } Мы инициализируем и устанавливаем объект Highlighter во вспомогательной приватной функции 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()); } Сначала мы создаем шрифт который мы хотим использовать в редакторе, затем мы создаем сам редактор который является экземпляром класса QTextEdit. Прежде чем мы инициализируем редактор с файлом определения класса MainWindow, мы создаем экземпляр Highlighter передавая документ редактора в качестве аргумента. Это документ для которого будет применена подсветка. Затем мы заканчиваем. Объект QSyntaxHighlighter может быть установлен только на один документ за раз, но вы можете легко переставить подсветку на другой документ используя функцию QSyntaxHighlighter::setDocument(). Класс QSyntaxHighlighter так же предоставляет функцию document() которая возвращает документ, установленный в текущий момент.
|
Попытка перевода Qt документации. Если есть желание присоединиться, или если есть замечания или пожелания, то заходите на форум: Перевод Qt документации на русский язык... Люди внесшие вклад в перевод: Команда переводчиков |