Некоторое время назад я занимался изучением подсветки синтаксиса в 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?