Некоторое время назад я занимался изучением подсветки синтаксиса в QTextEdit и практиковался на подсветке для HTML кода. В результате удалось сделать довольно неплохой вариант подсветки синтаксиса HTML кода, но в связи с тем, что есть вероятность того, что этот код не будет мною применён где-нибудь, я решил выложить данный пример программного кода.
Подсветка синтаксиса HTML в QTextEdit будет выглядеть следующим образом:
Структура проекта
Проект состоит из следующих файлов:
- HTMLExample.pro - профайл проекта;
- main.cpp - стартовый файл проекта;
- mainwindow.h - заголовочный файл окна приложения;
- mainwindow.cpp - файл исходных кодов окна приложения;
- mainwindow.ui - файл интерфейса;
- HTMLHighLighter.h - заголовочный файл класса для подсветки HTML кода;
- HTMLHighLighter.cpp - файл исходных кодов класса для подсветка HTML кода;
main.cpp, HTMLExample.pro - создаются по умолчанию, в mainwindow.ui добавляем только QTextEdit .
mainwindow.h
Здесь подключаем заголовочный файл HTMLHighLighter и объявляем его объект.
- #ifndef MAINWINDOW_H
- #define MAINWINDOW_H
- #include <QMainWindow>
- #include "HTMLHighLighter.h"
- namespace Ui {
- class MainWindow;
- }
- class MainWindow : public QMainWindow
- {
- Q_OBJECT
- public:
- explicit MainWindow(QWidget *parent = 0);
- ~MainWindow();
- private:
- Ui::MainWindow *ui;
- HtmlHighLighter *m_htmlHightLighter;
- };
- #endif // MAINWINDOW_H
mainwindow.cpp
А в данном файле просто устанавливаем объект HTMLHighLighter в документ объекта QTextEdit .
- #include "mainwindow.h"
- #include "ui_mainwindow.h"
- MainWindow::MainWindow(QWidget *parent) :
- QMainWindow(parent),
- ui(new Ui::MainWindow)
- {
- ui->setupUi(this);
- m_htmlHightLighter = new HtmlHighLighter(ui->textEdit->document());
- }
- MainWindow::~MainWindow()
- {
- delete ui;
- }
HTMLHighLighter.h
Особенность подсветки синтаксиса кода или просто текста в QTextEdit заключается в том, что класс QSyntaxtHighLighter перебирает все текстовые блоки, на которые разделён текст (разбит с помощью символа перевода строки) и определяет, как подсвечивать текущий блок с самого начала в зависимости от состояния подсветки предыдущего текстового блока.
Естественно это необходимо реализовать. В конструкторе класса будут инициализированы правила подсветки различных частей кода и шаблоны, по которым будут определяться эти части кода. А в методе highlightBlock(const QString &text), будет реализована логика обработки текста.
- #ifndef HTMLHIGHLIGHTER_H
- #define HTMLHIGHLIGHTER_H
- #include <QSyntaxHighlighter>
- QT_BEGIN_NAMESPACE
- class QTextDocument;
- class QTextCharFormat;
- QT_END_NAMESPACE
- class HtmlHighLighter : public QSyntaxHighlighter
- {
- Q_OBJECT
- public:
- explicit HtmlHighLighter(QTextDocument *parent = 0);
- protected:
- void highlightBlock(const QString &text) Q_DECL_OVERRIDE;
- private:
- // Состояние подсветки, в которой находится текстовый блок на момент его закрытия
- enum States {
- None, // Без подсветки
- Tag, // Подсветка внутри тега
- Comment, // Внутри комментария
- Quote // Внутри кавычек, которые внутри тега
- };
- struct HighlightingRule
- {
- QRegExp pattern;
- QTextCharFormat format;
- };
- QVector<HighlightingRule> startTagRules; // Правила форматирования для открывающих тегов
- QVector<HighlightingRule> endTagRules; // Правила форматирования для закрывающих тегов
- QRegExp openTag; // Символ открытия тега - "<"
- QRegExp closeTag; // Символ закрытия тег - ">"
- QTextCharFormat edgeTagFormat; // Форматирование символов openTag и closeTag
- QTextCharFormat insideTagFormat; // Форматирование текста внутри тега
- QRegExp commentStartExpression; // Регулярка начала комментария
- QRegExp commentEndExpression; // Регулярка закрытия комментария
- QTextCharFormat multiLineCommentFormat; // Форматирование текста внутри комментария
- QRegExp quotes; // Регулярное выражение для текста в кавычках внутри тега
- QTextCharFormat quotationFormat; // Форматирование текста в кавычках внутри тега
- QTextCharFormat tagsFormat; // Форматирование самих тегов
- };
- #endif // HTMLHIGHLIGHTER_H
HTMLHighLighter.cpp
- #include "HTMLHighLighter.h"
- #include <QTextCharFormat>
- #include <QBrush>
- #include <QColor>
- HtmlHighLighter::HtmlHighLighter(QTextDocument *parent)
- : QSyntaxHighlighter(parent)
- {
- HighlightingRule rule;
- edgeTagFormat.setForeground(QBrush(QColor("#32a9dd")));
- insideTagFormat.setForeground(Qt::blue);
- insideTagFormat.setFontWeight(QFont::Bold);
- openTag = QRegExp("<");
- closeTag = QRegExp(">");
- tagsFormat.setForeground(Qt::darkBlue);
- tagsFormat.setFontWeight(QFont::Bold);
- QStringList keywordPatterns;
- keywordPatterns << "<\\ba\\b" << "<\\babbr\\b" << "<\\bacronym\\b" << "<\\baddress\\b" << "<\\bapplet\\b"
- << "<\\barea\\b" << "<\\barticle\\b" << "<\\baside\\b" << "<\\baudio\\b" << "<\\bb\\b"
- << "<\\bbase\\b" << "<\\bbasefont\\b" << "<\\bbdi\\b" << "<\\bbdo\\b" << "<\\bbgsound\\b"
- << "<\\bblockquote\\b" << "<\\bbig\\b" << "<\\bbody\\b" << "<\\bblink\\b" << "<\\bbr\\b"
- << "<\\bbutton\\b" << "<\\bcanvas\\b" << "<\\bcaption\\b" << "<\\bcenter\\b" << "<\\bcite\\b"
- << "<\\bcode\\b" << "<\\bcol\\b" << "<\\bcolgroup\\b" << "<\\bcommand\\b" << "<\\bcomment\\b"
- << "<\\bdata\\b" << "<\\bdatalist\\b" << "<\\bdd\\b" << "<\\bdel\\b" << "<\\bdetails\\b"
- << "<\\bdfn\\b" << "<\\bdialog\\b" << "<\\bdir\\b" << "<\\bdiv\\b" << "<\\bdl\\b"
- << "<\\bdt\\b" << "<\\bem\\b" << "<\\bembed\\b" << "<\\bfieldset\\b" << "<\\bfigcaption\\b"
- << "<\\bfigure\\b" << "<\\bfont\\b" << "<\\bfooter\\b" << "<\\bform\\b" << "<\\bframe\\b"
- << "<\\bframeset\\b" << "<\\bh1\\b" << "<\\bh2\\b" << "<\\bh3\\b" << "<\\bh4\\b"
- << "<\\bh5\\b" << "<\\bh6\\b" << "<\\bhead\\b" << "<\\bheader\\b" << "<\\bhgroup\\b"
- << "<\\bhr\\b" << "<\\bhtml\\b" << "<\\bi\\b" << "<\\biframe\\b" << "<\\bimg\\b"
- << "<\\binput\\b" << "<\\bins\\b" << "<\\bisindex\\b" << "<\\bkbd\\b" << "<\\bkeygen\\b"
- << "<\\blabel\\b" << "<\\blegend\\b" << "<\\bli\\b" << "<\\blink\\b" << "<\\blisting\\b"
- << "<\\bmain\\b" << "<\\bmap\\b" << "<\\bmarquee\\b" << "<\\bmark\\b" << "<\\bmenu\\b"
- << "<\\bamenuitem\\b" << "<\\bmeta\\b" << "<\\bmeter\\b" << "<\\bmulticol\\b" << "<\\bnav\\b"
- << "<\\bnobr\\b" << "<\\bnoembed\\b" << "<\\bnoindex\\b" << "<\\bnoframes\\b" << "<\\bnoscript\\b"
- << "<\\bobject\\b" << "<\\bol\\b" << "<\\boptgroup\\b" << "<\\boption\\b" << "<\\boutput\\b"
- << "<\\bp\\b" << "<\\bparam\\b" << "<\\bpicture\\b" << "<\\bplaintext\\b" << "<\\bpre\\b"
- << "<\\bprogress\\b" << "<\\bq\\b" << "<\\brp\\b" << "<\\brt\\b" << "<\\brtc\\b" << "<\\bruby\\b"
- << "<\\bs\\b" << "<\\bsamp\\b" << "<\\bscript\\b" << "<\\bsection\\b" << "<\\bselect\\b"
- << "<\\bsmall\\b" << "<\\bsource\\b" << "<\\bspacer\\b" << "<\\bspan\\b" << "<\\bstrike\\b"
- << "<\\bstrong\\b" << "<\\bstyle\\b" << "<\\bsub\\b" << "<\\bsummary\\b" << "<\\bsup\\b"
- << "<\\btable\\b" << "<\\btbody\\b" << "<\\btd\\b" << "<\\btemplate\\b" << "<\\btextarea\\b"
- << "<\\btfoot\\b" << "<\\bth\\b" << "<\\bthead\\b" << "<\\btime\\b" << "<\\btitle\\b"
- << "<\\btr\\b" << "<\\btrack\\b" << "<\\btt\\b" << "<\\bu\\b" << "<\\bul\\b" << "<\\bvar\\b"
- << "<\\bvideo\\b" << "<\\bwbr\\b" << "<\\bxmp\\b";
- for (const QString &pattern : keywordPatterns)
- {
- rule.pattern = QRegExp(pattern);
- rule.format = tagsFormat;
- startTagRules.append(rule);
- }
- QStringList keywordPatterns_end;
- keywordPatterns_end << "<!\\bDOCTYPE\\b" << "</\\ba\\b" << "</\\babbr\\b" << "</\\bacronym\\b" << "</\\baddress\\b" << "</\\bapplet\\b"
- << "</\\barea\\b" << "</\\barticle\\b" << "</\\baside\\b" << "</\\baudio\\b" << "</\\bb\\b"
- << "</\\bbase\\b" << "</\\bbasefont\\b" << "</\\bbdi\\b" << "</\\bbdo\\b" << "</\\bbgsound\\b"
- << "</\\bblockquote\\b" << "</\\bbig\\b" << "</\\bbody\\b" << "</\\bblink\\b" << "</\\bbr\\b"
- << "</\\bbutton\\b" << "</\\bcanvas\\b" << "</\\bcaption\\b" << "</\\bcenter\\b" << "</\\bcite\\b"
- << "</\\bcode\\b" << "</\\bcol\\b" << "</\\bcolgroup\\b" << "</\\bcommand\\b" << "</\\bcomment\\b"
- << "</\\bdata\\b" << "</\\bdatalist\\b" << "</\\bdd\\b" << "</\\bdel\\b" << "</\\bdetails\\b"
- << "</\\bdfn\\b" << "</\\bdialog\\b" << "</\\bdir\\b" << "</\\bdiv\\b" << "</\\bdl\\b"
- << "</\\bdt\\b" << "</\\bem\\b" << "</\\bembed\\b" << "</\\bfieldset\\b" << "</\\bfigcaption\\b"
- << "</\\bfigure\\b" << "</\\bfont\\b" << "</\\bfooter\\b" << "</\\bform\\b" << "</\\bframe\\b"
- << "</\\bframeset\\b" << "</\\bh1\\b" << "</\\bh2\\b" << "</\\bh3\\b" << "</\\bh4\\b"
- << "</\\bh5\\b" << "</\\bh6\\b" << "</\\bhead\\b" << "</\\bheader\\b" << "</\\bhgroup\\b"
- << "</\\bhr\\b" << "</\\bhtml\\b" << "</\\bi\\b" << "</\\biframe\\b" << "</\\bimg\\b"
- << "</\\binput\\b" << "</\\bins\\b" << "</\\bisindex\\b" << "</\\bkbd\\b" << "</\\bkeygen\\b"
- << "</\\blabel\\b" << "</\\blegend\\b" << "</\\bli\\b" << "</\\blink\\b" << "</\\blisting\\b"
- << "</\\bmain\\b" << "</\\bmap\\b" << "</\\bmarquee\\b" << "</\\bmark\\b" << "</\\bmenu\\b"
- << "</\\bamenuitem\\b" << "</\\bmeta\\b" << "</\\bmeter\\b" << "</\\bmulticol\\b" << "</\\bnav\\b"
- << "</\\bnobr\\b" << "</\\bnoembed\\b" << "</\\bnoindex\\b" << "</\\bnoframes\\b" << "</\\bnoscript\\b"
- << "</\\bobject\\b" << "</\\bol\\b" << "</\\boptgroup\\b" << "</\\boption\\b" << "</\\boutput\\b"
- << "</\\bp\\b" << "</\\bparam\\b" << "</\\bpicture\\b" << "</\\bplaintext\\b" << "</\\bpre\\b"
- << "</\\bprogress\\b" << "</\\bq\\b" << "</\\brp\\b" << "</\\brt\\b" << "</\\brtc\\b" << "</\\bruby\\b"
- << "</\\bs\\b" << "</\\bsamp\\b" << "</\\bscript\\b" << "</\\bsection\\b" << "</\\bselect\\b"
- << "</\\bsmall\\b" << "</\\bsource\\b" << "</\\bspacer\\b" << "</\\bspan\\b" << "</\\bstrike\\b"
- << "</\\bstrong\\b" << "</\\bstyle\\b" << "</\\bsub\\b" << "</\\bsummary\\b" << "</\\bsup\\b"
- << "</\\btable\\b" << "</\\btbody\\b" << "</\\btd\\b" << "</\\btemplate\\b" << "</\\btextarea\\b"
- << "</\\btfoot\\b" << "</\\bth\\b" << "</\\bthead\\b" << "</\\btime\\b" << "</\\btitle\\b"
- << "</\\btr\\b" << "</\\btrack\\b" << "</\\btt\\b" << "</\\bu\\b" << "</\\bul\\b" << "</\\bvar\\b"
- << "</\\bvideo\\b" << "</\\bwbr\\b" << "</\\bxmp\\b";
- for (const QString &pattern : keywordPatterns_end)
- {
- rule.pattern = QRegExp(pattern);
- rule.format = tagsFormat;
- endTagRules.append(rule);
- }
- multiLineCommentFormat.setForeground(Qt::darkGray);
- commentStartExpression = QRegExp("<!--");
- commentEndExpression = QRegExp("-->");
- quotationFormat.setForeground(Qt::darkGreen);
- quotes = QRegExp("\"");
- }
- void HtmlHighLighter::highlightBlock(const QString &text)
- {
- setCurrentBlockState(HtmlHighLighter::None);
- // TAG
- int startIndex = 0;
- // Если не находимся внутри тега,
- if (previousBlockState() != HtmlHighLighter::Tag && previousBlockState() != HtmlHighLighter::Quote)
- {
- // То пытаемся найти начало следующего тега
- startIndex = openTag.indexIn(text);
- }
- // Забираем состояние предыдущего текстового блока
- int subPreviousTag = previousBlockState();
- while (startIndex >= 0)
- {
- // ищем символ конца тега
- int endIndex = closeTag.indexIn(text, startIndex);
- int tagLength;
- // если конец тега не найден, то устанавливаем состояние блока
- if (endIndex == -1)
- {
- setCurrentBlockState(HtmlHighLighter::Tag);
- tagLength = text.length() - startIndex;
- }
- else
- {
- tagLength = endIndex - startIndex + closeTag.matchedLength();
- }
- // Устанавливаем форматирования для тега
- if (subPreviousTag != HtmlHighLighter::Tag)
- {
- // с начала тега и до конца, если предыдущее состояние не равнялось Tag
- setFormat(startIndex, 1, edgeTagFormat);
- setFormat(startIndex + 1, tagLength - 1, insideTagFormat);
- }
- else
- {
- // Если же находимся уже внутри тега с самого начала блока
- // и до конца тега
- setFormat(startIndex, tagLength, insideTagFormat);
- subPreviousTag = HtmlHighLighter::None;
- }
- // Форматируем символ конца тега
- setFormat(endIndex, 1, edgeTagFormat);
- /// QUOTES ///////////////////////////////////////
- int startQuoteIndex = 0;
- // Если не находимся в кавычках с предыдущего блока
- if (previousBlockState() != HtmlHighLighter::Quote)
- {
- // То пытаемся найти начало кавычек
- startQuoteIndex = quotes.indexIn(text, startIndex);
- }
- // Подсвечиваем все кавычки внутри тега
- while (startQuoteIndex >= 0 && ((startQuoteIndex < endIndex) || (endIndex == -1)))
- {
- int endQuoteIndex = quotes.indexIn(text, startQuoteIndex + 1);
- int quoteLength;
- if (endQuoteIndex == -1)
- {
- // Если закрывающая кавычка не найдена, то устанавливаем состояние Quote для блока
- setCurrentBlockState(HtmlHighLighter::Quote);
- quoteLength = text.length() - startQuoteIndex;
- }
- else
- {
- quoteLength = endQuoteIndex - startQuoteIndex + quotes.matchedLength();
- }
- if ((endIndex > endQuoteIndex) || endIndex == -1)
- {
- setFormat(startQuoteIndex, quoteLength, quotationFormat);
- startQuoteIndex = quotes.indexIn(text, startQuoteIndex + quoteLength);
- }
- else
- {
- break;
- }
- }
- //////////////////////////////////////////////////
- // Снова ищем начало тега
- startIndex = openTag.indexIn(text, startIndex + tagLength);
- }
- // EDGES OF TAGS
- // Обработка цвета саимх тегов, то есть подсветка слов div, p, strong и т.д.
- for (const HighlightingRule &rule : startTagRules)
- {
- QRegExp expression(rule.pattern);
- int index = expression.indexIn(text);
- while (index >= 0)
- {
- int length = expression.matchedLength();
- setFormat(index + 1, length - 1, rule.format);
- index = expression.indexIn(text, index + length);
- }
- }
- for (const HighlightingRule &rule : endTagRules)
- {
- QRegExp expression(rule.pattern);
- int index = expression.indexIn(text);
- while (index >= 0) {
- int length = expression.matchedLength();
- setFormat(index + 1, 1, edgeTagFormat);
- setFormat(index + 2, length - 2, rule.format);
- index = expression.indexIn(text, index + length);
- }
- }
- // COMMENT
- int startCommentIndex = 0;
- // Если предыдущее состояние тега не является комментарием
- if (previousBlockState() != HtmlHighLighter::Comment)
- {
- // то пытаемся найти начало комментария
- startCommentIndex = commentStartExpression.indexIn(text);
- }
- // Если комментарий найден
- while (startCommentIndex >= 0)
- {
- // Ищем конец комментария
- int endCommentIndex = commentEndExpression.indexIn(text, startCommentIndex);
- int commentLength;
- // Если конец не найден
- if (endCommentIndex == -1)
- {
- // То устанавливаем состояние Comment
- // Принцип аналогичен, что и для обычных тегов
- setCurrentBlockState(HtmlHighLighter::Comment);
- commentLength = text.length() - startCommentIndex;
- }
- else
- {
- commentLength = endCommentIndex - startCommentIndex
- + commentEndExpression.matchedLength();
- }
- setFormat(startCommentIndex, commentLength, multiLineCommentFormat);
- startCommentIndex = commentStartExpression.indexIn(text, startCommentIndex + commentLength);
- }
- }
Добрый день. Подскажите, как будет выглядеть метод , если вместо используется ?
Половина слов из предыдущего комментария куда-то исчезло. Суть: вместо QRegExp используется QRegularExport. Как в этом случае будет выглядеть highlightBlock?
Вы хотели сказать QRegularExpression?