How to display graphics objects behind or foreground of text inside QTextEdit in Qt?
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;
I would like to display a rectangle behind a word I selected like Qt Creator does here:
I am experimenting with the example of QSyntaxHighlighter. I am able to change styles based on keyword patterns. I would like to have graphics or widgets for custom autocompletion lists.
c++ qt autocomplete syntax-highlighting qtextedit
|
show 1 more comment
I would like to display a rectangle behind a word I selected like Qt Creator does here:
I am experimenting with the example of QSyntaxHighlighter. I am able to change styles based on keyword patterns. I would like to have graphics or widgets for custom autocompletion lists.
c++ qt autocomplete syntax-highlighting qtextedit
Hi! What have you tried? How's your experimenting with QSyntaxHighlighter?
– TrebledJ
Nov 15 '18 at 12:50
Well, i have this example and so far i encountered no problem:doc.qt.io/qt-5/…
– kostas petsis
Nov 15 '18 at 19:48
The image above is how Qtcreator looks and also how i WANT my app to look, so how can i render rectangles or other shapes(can i?) inside QTextEdit?
– kostas petsis
Nov 15 '18 at 19:49
Have you checked out QTextBlock, QTextBlockFormat, QTextFrameFormat?
– TrebledJ
Nov 16 '18 at 1:49
No.I will check them out and see if they fit what i want to do.I'll keep the thread open for someone that has done this or was able to display graphics inside the QTextEdit
– kostas petsis
Nov 16 '18 at 14:37
|
show 1 more comment
I would like to display a rectangle behind a word I selected like Qt Creator does here:
I am experimenting with the example of QSyntaxHighlighter. I am able to change styles based on keyword patterns. I would like to have graphics or widgets for custom autocompletion lists.
c++ qt autocomplete syntax-highlighting qtextedit
I would like to display a rectangle behind a word I selected like Qt Creator does here:
I am experimenting with the example of QSyntaxHighlighter. I am able to change styles based on keyword patterns. I would like to have graphics or widgets for custom autocompletion lists.
c++ qt autocomplete syntax-highlighting qtextedit
c++ qt autocomplete syntax-highlighting qtextedit
edited Nov 18 '18 at 15:25
TrebledJ
3,66421328
3,66421328
asked Nov 15 '18 at 11:14
kostas petsiskostas petsis
136
136
Hi! What have you tried? How's your experimenting with QSyntaxHighlighter?
– TrebledJ
Nov 15 '18 at 12:50
Well, i have this example and so far i encountered no problem:doc.qt.io/qt-5/…
– kostas petsis
Nov 15 '18 at 19:48
The image above is how Qtcreator looks and also how i WANT my app to look, so how can i render rectangles or other shapes(can i?) inside QTextEdit?
– kostas petsis
Nov 15 '18 at 19:49
Have you checked out QTextBlock, QTextBlockFormat, QTextFrameFormat?
– TrebledJ
Nov 16 '18 at 1:49
No.I will check them out and see if they fit what i want to do.I'll keep the thread open for someone that has done this or was able to display graphics inside the QTextEdit
– kostas petsis
Nov 16 '18 at 14:37
|
show 1 more comment
Hi! What have you tried? How's your experimenting with QSyntaxHighlighter?
– TrebledJ
Nov 15 '18 at 12:50
Well, i have this example and so far i encountered no problem:doc.qt.io/qt-5/…
– kostas petsis
Nov 15 '18 at 19:48
The image above is how Qtcreator looks and also how i WANT my app to look, so how can i render rectangles or other shapes(can i?) inside QTextEdit?
– kostas petsis
Nov 15 '18 at 19:49
Have you checked out QTextBlock, QTextBlockFormat, QTextFrameFormat?
– TrebledJ
Nov 16 '18 at 1:49
No.I will check them out and see if they fit what i want to do.I'll keep the thread open for someone that has done this or was able to display graphics inside the QTextEdit
– kostas petsis
Nov 16 '18 at 14:37
Hi! What have you tried? How's your experimenting with QSyntaxHighlighter?
– TrebledJ
Nov 15 '18 at 12:50
Hi! What have you tried? How's your experimenting with QSyntaxHighlighter?
– TrebledJ
Nov 15 '18 at 12:50
Well, i have this example and so far i encountered no problem:doc.qt.io/qt-5/…
– kostas petsis
Nov 15 '18 at 19:48
Well, i have this example and so far i encountered no problem:doc.qt.io/qt-5/…
– kostas petsis
Nov 15 '18 at 19:48
The image above is how Qtcreator looks and also how i WANT my app to look, so how can i render rectangles or other shapes(can i?) inside QTextEdit?
– kostas petsis
Nov 15 '18 at 19:49
The image above is how Qtcreator looks and also how i WANT my app to look, so how can i render rectangles or other shapes(can i?) inside QTextEdit?
– kostas petsis
Nov 15 '18 at 19:49
Have you checked out QTextBlock, QTextBlockFormat, QTextFrameFormat?
– TrebledJ
Nov 16 '18 at 1:49
Have you checked out QTextBlock, QTextBlockFormat, QTextFrameFormat?
– TrebledJ
Nov 16 '18 at 1:49
No.I will check them out and see if they fit what i want to do.I'll keep the thread open for someone that has done this or was able to display graphics inside the QTextEdit
– kostas petsis
Nov 16 '18 at 14:37
No.I will check them out and see if they fit what i want to do.I'll keep the thread open for someone that has done this or was able to display graphics inside the QTextEdit
– kostas petsis
Nov 16 '18 at 14:37
|
show 1 more comment
2 Answers
2
active
oldest
votes
Note that this answer hasn't yet covered
I would like to have graphics or widgets for custom autocompletion lists.
but I'm looking into it.
Edit:
For autocompletion follow the Custom Completer Example or the Completer Example.
The code below follows the first one, which I blatantly, unashamedly copied and integrated into the BackgroundHighlighter class and main.cpp.
This answer will contain five files within a project along with a Qt Resource File.
highlighter.h(Highlighter Class for Syntax)highlighter.cppbackgroundHighlighter.h(BackgroundHighlighter Class)backgroundHighlighter.cppmain.cppres.qrc(optional, not needed, you can hardcode your text)res(directory) (optional)|- symbols.txt(optional, you can set your own default text)|- wordlist.txt(optional, copied from example but you could use your own line-delimited word list and set this inmain.cppwith aQStringListModel)
Note that the implementation of the Highlighter class for (1) and (2) can be found in the Qt Syntax Highlighter Example. I will leave its implementation as an exercise for the reader.
In calling the BackgroundHighlighter class, one can pass it a file name to load text from a file. (This wasn't in the OP's specification, but was convenient to implement due to the large amount of text I wanted to test.)
Also note that I integrated the Custom Completer Example into the class.
Here's backgroundHighlighter.h (3) (~45 lines, ~60 lines with completer):
#ifndef BACKGROUNDHIGHLIGHTER_H
#define BACKGROUNDHIGHLIGHTER_H
#include <QtWidgets>
#include <QtGui>
// this is the file to your highlighter
#include "myhighlighter.h"
class BackgroundHighlighter : public QTextEdit
Q_OBJECT
public:
BackgroundHighlighter(const QString &fileName = QString(), QWidget *parent = nullptr);
void loadFile(const QString &fileName);
void setCompleter(QCompleter *completer);
QCompleter *completer() const;
protected:
void keyPressEvent(QKeyEvent *e) override;
void focusInEvent(QFocusEvent *e) override;
public slots:
void onCursorPositionChanged();
private slots:
void insertCompletion(const QString &completion);
private:
// this is your syntax highlighter
Highlighter *syntaxHighlighter;
// stores the symbol being highlighted
QString highlightSymbol;
// stores the position (front of selection) where the cursor was originally placed
int mainHighlightPosition;
// stores character formats to be used
QTextCharFormat mainFmt;
QTextCharFormat subsidiaryFmt;
QTextCharFormat defaultFmt;
void setWordFormat(const int &position, const QTextCharFormat &format);
void runHighlight();
void clearHighlights();
void highlightMatchingSymbols(const QString &symbol);
// completer, copied from example
QString textUnderCursor() const;
QCompleter *c;
;
#endif // BACKGROUNDHIGHLIGHTER_H
And here's backgroundHighlighter.cpp (4) (~160 lines, ~250 lines with completer):
#include "backgroundhighlighter.h"
#include <QDebug>
// constructor
BackgroundHighlighter::BackgroundHighlighter(const QString &fileName, QWidget *parent) :
QTextEdit(parent)
// I like Monaco
setFont(QFont("Monaco"));
setMinimumSize(QSize(500, 200));
// load initial text from a file OR from a hardcoded default
if (!fileName.isEmpty())
loadFile(fileName);
else
QString defaultText = "This is a default text implemented by "
"a stackoverflow user. Please upvote his answer "
"at https://stackoverflow.com/a/53351512/10239789.";
setPlainText(defaultText);
// set the highlighter here
QTextDocument *doc = document();
syntaxHighlighter = new Highlighter(doc);
// TODO change brush/colours to match theme
mainFmt.setBackground(Qt::yellow);
subsidiaryFmt.setBackground(Qt::lightGray);
defaultFmt.setBackground(Qt::white);
// connect the signal to our handler
connect(this, &QTextEdit::cursorPositionChanged, this, &BackgroundHighlighter::onCursorPositionChanged);
// convenience function for reading a file
void BackgroundHighlighter::loadFile(const QString &fileName)
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly))
return;
// the file could be in Plain Text OR Html
setText(file.readAll());
void BackgroundHighlighter::setCompleter(QCompleter *completer)
if (c)
QObject::disconnect(c, 0, this, 0);
c = completer;
if (!c)
return;
c->setWidget(this);
c->setCompletionMode(QCompleter::PopupCompletion);
c->setCaseSensitivity(Qt::CaseInsensitive);
QObject::connect(c, SIGNAL(activated(QString)),
this, SLOT(insertCompletion(QString)));
QCompleter *BackgroundHighlighter::completer() const
return c;
void BackgroundHighlighter::keyPressEvent(QKeyEvent *e)
:"<>?,./;'\-="); // end of word
bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift;
QString completionPrefix = textUnderCursor();
if (!isShortcut && (hasModifier
void BackgroundHighlighter::focusInEvent(QFocusEvent *e)
if (c)
c->setWidget(this);
QTextEdit::focusInEvent(e);
// convenience function for setting a `charFmt` at a `position`
void BackgroundHighlighter::setWordFormat(const int &position, const QTextCharFormat &charFmt)
QTextCursor cursor = textCursor();
cursor.setPosition(position);
cursor.select(QTextCursor::WordUnderCursor);
cursor.setCharFormat(charFmt);
// this will handle the `QTextEdit::cursorPositionChanged()` signal
void BackgroundHighlighter::onCursorPositionChanged()
// if cursor landed on different format, the `currentCharFormat` will be changed
// we need to change it back to white
setCurrentCharFormat(defaultFmt);
// this is the function you're looking for
runHighlight();
void BackgroundHighlighter::insertCompletion(const QString &completion)
if (c->widget() != this)
return;
QTextCursor tc = textCursor();
int extra = completion.length() - c->completionPrefix().length();
tc.movePosition(QTextCursor::Left);
tc.movePosition(QTextCursor::EndOfWord);
tc.insertText(completion.right(extra));
setTextCursor(tc);
QString BackgroundHighlighter::textUnderCursor() const
QTextCursor tc = textCursor();
tc.select(QTextCursor::WordUnderCursor);
return tc.selectedText();
/**
* BRIEF
* Check if new highlighting is needed
* Clear previous highlights
* Check if the word under the cursor is a symbol (i.e. matches ^[A-Za-z0-9_]+$)
* Highlight all relevant symbols
*/
void BackgroundHighlighter::runHighlight()
// retrieve cursor
QTextCursor cursor = textCursor();
// retrieve word under cursor
cursor.select(QTextCursor::WordUnderCursor);
QString wordUnder = cursor.selectedText();
qDebug() << "Word Under Cursor:" << wordUnder;
// get front of cursor, used later for storing in `highlightPositions` or `mainHighlightPosition`
int cursorFront = cursor.selectionStart();
// if the word under cursor is the same, then save time
// by skipping the process
if (wordUnder == highlightSymbol)
// switch formats
setWordFormat(mainHighlightPosition, subsidiaryFmt); // change previous main to subsidiary
setWordFormat(cursorFront, mainFmt); // change position under cursor to main
// update main position
mainHighlightPosition = cursorFront;
// jump the gun
return;
// clear previous highlights
if (mainHighlightPosition != -1)
clearHighlights();
// check if selected word is a symbol
if (!wordUnder.contains(QRegularExpression("^[A-Za-z0-9_]+$")))
qDebug() << wordUnder << "is not a symbol!";
return;
// set the highlight symbol
highlightSymbol = wordUnder;
// store the cursor position to check later
mainHighlightPosition = cursorFront;
// highlight all relevant symbols
highlightMatchingSymbols(wordUnder);
qDebug() << "Highlight donenn";
// clear previously highlights
void BackgroundHighlighter::clearHighlights()
QTextCursor cursor = textCursor();
// wipe the ENTIRE document with the default background, this should be REALLY fast
// WARNING: this may have unintended consequences if you have other backgrounds you want to keep
cursor.select(QTextCursor::Document);
cursor.setCharFormat(defaultFmt);
// reset variables
mainHighlightPosition = -1;
highlightSymbol.clear();
// highlight all matching symbols
void BackgroundHighlighter::highlightMatchingSymbols(const QString &symbol)
// highlight background of congruent symbols
QString docText = toPlainText();
// use a regex with \b to look for standalone symbols
QRegularExpression regexp("\b" + symbol + "\b");
// loop through all matches in the text
int matchPosition = docText.indexOf(regexp);
while (matchPosition != -1)
// if the position
setWordFormat(matchPosition, matchPosition == mainHighlightPosition ? mainFmt : subsidiaryFmt);
// find next match
matchPosition = docText.indexOf(regexp, matchPosition + 1);
Finally, here's main.cpp (5) (~10 lines, ~45 lines with completer)
#include <QApplication>
#include <backgroundhighlighter.h>
QAbstractItemModel *modelFromFile(const QString& fileName, QCompleter *completer)
QFile file(fileName);
if (!file.open(QFile::ReadOnly))
return new QStringListModel(completer);
#ifndef QT_NO_CURSOR
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
#endif
QStringList words;
while (!file.atEnd())
QByteArray line = file.readLine();
if (!line.isEmpty())
words << line.trimmed();
#ifndef QT_NO_CURSOR
QApplication::restoreOverrideCursor();
#endif
return new QStringListModel(words, completer);
int main(int argc, char *argv)
QApplication a(argc, argv);
BackgroundHighlighter bh(":/res/symbols.txt");
QCompleter *completer = new QCompleter();
completer->setModel(modelFromFile(":/res/wordlist.txt", completer));
// use this and comment the above if you don't have or don't want to use wordlist.txt
// QStringListModel *model = new QStringListModel(QStringList() << "aaaaaaa" << "aaaaab" << "aaaabb" << "aaacccc",
completer);
// completer->setModel(model);
completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
completer->setCaseSensitivity(Qt::CaseInsensitive);
completer->setWrapAround(false);
bh.setCompleter(completer);
bh.show();
return a.exec();
In res.qrc add a / prefix and add files (res/symbols.txt, res/wordlist.txt) from the res/ subdirectory.
I have tried my best to
- provide sufficient explanation within the comments.
test my solution in the context of all test cases.
optimise the code (not really) for better runtime performance.
What Qt classes are used?
The code above makes use of the Qt Core Module, Qt Widgets Module, and the Qt Gui Module.
Among these, the main ones I used were
QTextEditfor subclassing theBackgroundHighlighterclass. This is a widget. The signalcursorPositionChangedcomes fromQTextEdit.QCharTextFormatfor formatting blocks of text. Especially with thesetBackground()inherited fromQTextFormat.QTextCursorfor manipulating the cursor (no visual changes), selecting words, and higlighting them.
Some other minor ones were
QRegularExpressionfor symbol matching.QTextDocumentfor initialising the syntax highlighter.QFilefor loading files.- and
QString(obviously).
Other classes may have been used by the Highlighter and the Custom Completer. These include QSyntaxHighlighter, QCompleter, and a couple event class.
Were there any previous attempts you tried that I should look out for so that I don't fall into the same manhole?
I spent a while trying to find how I might be able to implement it with QTextBlock and QTextBlockFormat. Apparently, QTextBlock only recognises one block as a line. I tried using several blocks per line to handle symbols and even tried using QTextTable to solve the issue. I failed miserably.
How "optimised" is this code?
Note that I didn't go all serious and write test/edge cases, profile it, or time it (I might time it).
When I said "optimise", I was alluding to the fact that I had slower, worse implementations before (using QList and whatnot).
I have tested with a symbols.txt file resembling
symbol1 symbol2 symbol3 symbol4 symbol5
symbol1 symbol2 symbol3 symbol4 symbol5
symbol1 symbol2 symbol3 symbol4 symbol5
// ... 500 lines
and I'm happy to say that I find the time reasonable (approx. 1 second for me?).
However, you might want to watch over for the line count as it grows. With the same text file at 1000 lines, the program will start to take approx. 3 seconds for highlighting.
Note that... I haven't optimised it entirely. There could possibly be a better implementation which formats only when the symbol scrolls into the user's view. This is just a suggestion. How to implement it I don't know. (But I might try 🤔.)
Are there any "side effects" I should be wary of?
Yes, at around line 133, under the BackgroundHighlighter::clearHighlights() method, I warn how the code might clear away any background highlights originally added as it sets the ENTIRE document's character background to the default format. This may be an unintended consequence of the result.
How does the completer work?
The words are loaded from a model in main.cpp. Once the user starts typing, with a word of at least 3 characters, the autocompleter shows up. If the completer isn't showing up for you, try to first implement the example on a standalone/separate app.
How can I change the background colour of the formats?
Go to lines 27 to 29 of backgroundhighlighter.cpp. There, you can see that I centralised the formatting. mainFmt refers to the formatting block directly under the cursor. subsidiaryFmt refers to the formatting blocks on congruent symbols. defaultFmt refers to the default format of the entire document which will be used in resetting the format.
Where can I get symbols.txt and wordlist.txt?
I've attached these on my github stackoverflow repository. You can download and copy them from there.
If anything is amiss, please comment below.
Could you provide me a symbols.txt and wordlist.txt cause i dont know what to put there.I tried: symbols.txt: "c cl cla clas class in inc incl inclu includ include class include" wordlist.txt: "class include typedef printf int mainn return " How is the completion list is triggered? with these files, i wasn't able to trigger it.The highlition works
– kostas petsis
Nov 17 '18 at 17:36
Here you go: Link to Github Repo. Err... completion is supposed to be triggered after entering at least 3 characters. If it doesn't work, maybe first try a separate implementation from the example?
– TrebledJ
Nov 17 '18 at 17:44
Edit: I didnt put right the paths of these 2 files.Now it gets triggered by itself.
– kostas petsis
Nov 17 '18 at 17:52
Hmm... so without typing, it pops up automatically? Inmain.cpptry to replace thesetModelimplementation withcompleter->setModel(QStringListModel(QStringList() << "aaaaaaa" << "aaaaab" << "aaaabb" << "aaacccc", completer));and inbackgroundhighlight.cppfill in the default text with something feasible.
– TrebledJ
Nov 17 '18 at 17:57
No i mean that now the completer gets triggered when i add 3 letters.It works well.Could you explain me the what does the symbols.txt does?Isn't the worldist the only thing i need for autocompleting?Whats the purpose of symbols.txt?
– kostas petsis
Nov 17 '18 at 18:04
|
show 8 more comments
For styled background of selected words like in the picture in the 1st post edit the backgroundhighlighter.cpp at line 30
QRadialGradient gradient(50, 50, 50, 50, 50);
gradient.setColorAt(0, QColor::fromRgbF(0, 1, 0, 1));
gradient.setColorAt(1, QColor::fromRgbF(0, 0, 0, 0));
QBrush brush(gradient);
subsidiaryFmt.setBackground(brush);

But after the first word the gradients gets lost..I'll try to fix this.Any ideas?
This should be in a separate question so that others can help along + debug. :-)
– TrebledJ
Nov 17 '18 at 18:47
1
Seems to me like the gradient is anchored at a certain center point.
– TrebledJ
Nov 17 '18 at 18:55
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53318233%2fhow-to-display-graphics-objects-behind-or-foreground-of-text-inside-qtextedit-in%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
Note that this answer hasn't yet covered
I would like to have graphics or widgets for custom autocompletion lists.
but I'm looking into it.
Edit:
For autocompletion follow the Custom Completer Example or the Completer Example.
The code below follows the first one, which I blatantly, unashamedly copied and integrated into the BackgroundHighlighter class and main.cpp.
This answer will contain five files within a project along with a Qt Resource File.
highlighter.h(Highlighter Class for Syntax)highlighter.cppbackgroundHighlighter.h(BackgroundHighlighter Class)backgroundHighlighter.cppmain.cppres.qrc(optional, not needed, you can hardcode your text)res(directory) (optional)|- symbols.txt(optional, you can set your own default text)|- wordlist.txt(optional, copied from example but you could use your own line-delimited word list and set this inmain.cppwith aQStringListModel)
Note that the implementation of the Highlighter class for (1) and (2) can be found in the Qt Syntax Highlighter Example. I will leave its implementation as an exercise for the reader.
In calling the BackgroundHighlighter class, one can pass it a file name to load text from a file. (This wasn't in the OP's specification, but was convenient to implement due to the large amount of text I wanted to test.)
Also note that I integrated the Custom Completer Example into the class.
Here's backgroundHighlighter.h (3) (~45 lines, ~60 lines with completer):
#ifndef BACKGROUNDHIGHLIGHTER_H
#define BACKGROUNDHIGHLIGHTER_H
#include <QtWidgets>
#include <QtGui>
// this is the file to your highlighter
#include "myhighlighter.h"
class BackgroundHighlighter : public QTextEdit
Q_OBJECT
public:
BackgroundHighlighter(const QString &fileName = QString(), QWidget *parent = nullptr);
void loadFile(const QString &fileName);
void setCompleter(QCompleter *completer);
QCompleter *completer() const;
protected:
void keyPressEvent(QKeyEvent *e) override;
void focusInEvent(QFocusEvent *e) override;
public slots:
void onCursorPositionChanged();
private slots:
void insertCompletion(const QString &completion);
private:
// this is your syntax highlighter
Highlighter *syntaxHighlighter;
// stores the symbol being highlighted
QString highlightSymbol;
// stores the position (front of selection) where the cursor was originally placed
int mainHighlightPosition;
// stores character formats to be used
QTextCharFormat mainFmt;
QTextCharFormat subsidiaryFmt;
QTextCharFormat defaultFmt;
void setWordFormat(const int &position, const QTextCharFormat &format);
void runHighlight();
void clearHighlights();
void highlightMatchingSymbols(const QString &symbol);
// completer, copied from example
QString textUnderCursor() const;
QCompleter *c;
;
#endif // BACKGROUNDHIGHLIGHTER_H
And here's backgroundHighlighter.cpp (4) (~160 lines, ~250 lines with completer):
#include "backgroundhighlighter.h"
#include <QDebug>
// constructor
BackgroundHighlighter::BackgroundHighlighter(const QString &fileName, QWidget *parent) :
QTextEdit(parent)
// I like Monaco
setFont(QFont("Monaco"));
setMinimumSize(QSize(500, 200));
// load initial text from a file OR from a hardcoded default
if (!fileName.isEmpty())
loadFile(fileName);
else
QString defaultText = "This is a default text implemented by "
"a stackoverflow user. Please upvote his answer "
"at https://stackoverflow.com/a/53351512/10239789.";
setPlainText(defaultText);
// set the highlighter here
QTextDocument *doc = document();
syntaxHighlighter = new Highlighter(doc);
// TODO change brush/colours to match theme
mainFmt.setBackground(Qt::yellow);
subsidiaryFmt.setBackground(Qt::lightGray);
defaultFmt.setBackground(Qt::white);
// connect the signal to our handler
connect(this, &QTextEdit::cursorPositionChanged, this, &BackgroundHighlighter::onCursorPositionChanged);
// convenience function for reading a file
void BackgroundHighlighter::loadFile(const QString &fileName)
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly))
return;
// the file could be in Plain Text OR Html
setText(file.readAll());
void BackgroundHighlighter::setCompleter(QCompleter *completer)
if (c)
QObject::disconnect(c, 0, this, 0);
c = completer;
if (!c)
return;
c->setWidget(this);
c->setCompletionMode(QCompleter::PopupCompletion);
c->setCaseSensitivity(Qt::CaseInsensitive);
QObject::connect(c, SIGNAL(activated(QString)),
this, SLOT(insertCompletion(QString)));
QCompleter *BackgroundHighlighter::completer() const
return c;
void BackgroundHighlighter::keyPressEvent(QKeyEvent *e)
:"<>?,./;'\-="); // end of word
bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift;
QString completionPrefix = textUnderCursor();
if (!isShortcut && (hasModifier
void BackgroundHighlighter::focusInEvent(QFocusEvent *e)
if (c)
c->setWidget(this);
QTextEdit::focusInEvent(e);
// convenience function for setting a `charFmt` at a `position`
void BackgroundHighlighter::setWordFormat(const int &position, const QTextCharFormat &charFmt)
QTextCursor cursor = textCursor();
cursor.setPosition(position);
cursor.select(QTextCursor::WordUnderCursor);
cursor.setCharFormat(charFmt);
// this will handle the `QTextEdit::cursorPositionChanged()` signal
void BackgroundHighlighter::onCursorPositionChanged()
// if cursor landed on different format, the `currentCharFormat` will be changed
// we need to change it back to white
setCurrentCharFormat(defaultFmt);
// this is the function you're looking for
runHighlight();
void BackgroundHighlighter::insertCompletion(const QString &completion)
if (c->widget() != this)
return;
QTextCursor tc = textCursor();
int extra = completion.length() - c->completionPrefix().length();
tc.movePosition(QTextCursor::Left);
tc.movePosition(QTextCursor::EndOfWord);
tc.insertText(completion.right(extra));
setTextCursor(tc);
QString BackgroundHighlighter::textUnderCursor() const
QTextCursor tc = textCursor();
tc.select(QTextCursor::WordUnderCursor);
return tc.selectedText();
/**
* BRIEF
* Check if new highlighting is needed
* Clear previous highlights
* Check if the word under the cursor is a symbol (i.e. matches ^[A-Za-z0-9_]+$)
* Highlight all relevant symbols
*/
void BackgroundHighlighter::runHighlight()
// retrieve cursor
QTextCursor cursor = textCursor();
// retrieve word under cursor
cursor.select(QTextCursor::WordUnderCursor);
QString wordUnder = cursor.selectedText();
qDebug() << "Word Under Cursor:" << wordUnder;
// get front of cursor, used later for storing in `highlightPositions` or `mainHighlightPosition`
int cursorFront = cursor.selectionStart();
// if the word under cursor is the same, then save time
// by skipping the process
if (wordUnder == highlightSymbol)
// switch formats
setWordFormat(mainHighlightPosition, subsidiaryFmt); // change previous main to subsidiary
setWordFormat(cursorFront, mainFmt); // change position under cursor to main
// update main position
mainHighlightPosition = cursorFront;
// jump the gun
return;
// clear previous highlights
if (mainHighlightPosition != -1)
clearHighlights();
// check if selected word is a symbol
if (!wordUnder.contains(QRegularExpression("^[A-Za-z0-9_]+$")))
qDebug() << wordUnder << "is not a symbol!";
return;
// set the highlight symbol
highlightSymbol = wordUnder;
// store the cursor position to check later
mainHighlightPosition = cursorFront;
// highlight all relevant symbols
highlightMatchingSymbols(wordUnder);
qDebug() << "Highlight donenn";
// clear previously highlights
void BackgroundHighlighter::clearHighlights()
QTextCursor cursor = textCursor();
// wipe the ENTIRE document with the default background, this should be REALLY fast
// WARNING: this may have unintended consequences if you have other backgrounds you want to keep
cursor.select(QTextCursor::Document);
cursor.setCharFormat(defaultFmt);
// reset variables
mainHighlightPosition = -1;
highlightSymbol.clear();
// highlight all matching symbols
void BackgroundHighlighter::highlightMatchingSymbols(const QString &symbol)
// highlight background of congruent symbols
QString docText = toPlainText();
// use a regex with \b to look for standalone symbols
QRegularExpression regexp("\b" + symbol + "\b");
// loop through all matches in the text
int matchPosition = docText.indexOf(regexp);
while (matchPosition != -1)
// if the position
setWordFormat(matchPosition, matchPosition == mainHighlightPosition ? mainFmt : subsidiaryFmt);
// find next match
matchPosition = docText.indexOf(regexp, matchPosition + 1);
Finally, here's main.cpp (5) (~10 lines, ~45 lines with completer)
#include <QApplication>
#include <backgroundhighlighter.h>
QAbstractItemModel *modelFromFile(const QString& fileName, QCompleter *completer)
QFile file(fileName);
if (!file.open(QFile::ReadOnly))
return new QStringListModel(completer);
#ifndef QT_NO_CURSOR
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
#endif
QStringList words;
while (!file.atEnd())
QByteArray line = file.readLine();
if (!line.isEmpty())
words << line.trimmed();
#ifndef QT_NO_CURSOR
QApplication::restoreOverrideCursor();
#endif
return new QStringListModel(words, completer);
int main(int argc, char *argv)
QApplication a(argc, argv);
BackgroundHighlighter bh(":/res/symbols.txt");
QCompleter *completer = new QCompleter();
completer->setModel(modelFromFile(":/res/wordlist.txt", completer));
// use this and comment the above if you don't have or don't want to use wordlist.txt
// QStringListModel *model = new QStringListModel(QStringList() << "aaaaaaa" << "aaaaab" << "aaaabb" << "aaacccc",
completer);
// completer->setModel(model);
completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
completer->setCaseSensitivity(Qt::CaseInsensitive);
completer->setWrapAround(false);
bh.setCompleter(completer);
bh.show();
return a.exec();
In res.qrc add a / prefix and add files (res/symbols.txt, res/wordlist.txt) from the res/ subdirectory.
I have tried my best to
- provide sufficient explanation within the comments.
test my solution in the context of all test cases.
optimise the code (not really) for better runtime performance.
What Qt classes are used?
The code above makes use of the Qt Core Module, Qt Widgets Module, and the Qt Gui Module.
Among these, the main ones I used were
QTextEditfor subclassing theBackgroundHighlighterclass. This is a widget. The signalcursorPositionChangedcomes fromQTextEdit.QCharTextFormatfor formatting blocks of text. Especially with thesetBackground()inherited fromQTextFormat.QTextCursorfor manipulating the cursor (no visual changes), selecting words, and higlighting them.
Some other minor ones were
QRegularExpressionfor symbol matching.QTextDocumentfor initialising the syntax highlighter.QFilefor loading files.- and
QString(obviously).
Other classes may have been used by the Highlighter and the Custom Completer. These include QSyntaxHighlighter, QCompleter, and a couple event class.
Were there any previous attempts you tried that I should look out for so that I don't fall into the same manhole?
I spent a while trying to find how I might be able to implement it with QTextBlock and QTextBlockFormat. Apparently, QTextBlock only recognises one block as a line. I tried using several blocks per line to handle symbols and even tried using QTextTable to solve the issue. I failed miserably.
How "optimised" is this code?
Note that I didn't go all serious and write test/edge cases, profile it, or time it (I might time it).
When I said "optimise", I was alluding to the fact that I had slower, worse implementations before (using QList and whatnot).
I have tested with a symbols.txt file resembling
symbol1 symbol2 symbol3 symbol4 symbol5
symbol1 symbol2 symbol3 symbol4 symbol5
symbol1 symbol2 symbol3 symbol4 symbol5
// ... 500 lines
and I'm happy to say that I find the time reasonable (approx. 1 second for me?).
However, you might want to watch over for the line count as it grows. With the same text file at 1000 lines, the program will start to take approx. 3 seconds for highlighting.
Note that... I haven't optimised it entirely. There could possibly be a better implementation which formats only when the symbol scrolls into the user's view. This is just a suggestion. How to implement it I don't know. (But I might try 🤔.)
Are there any "side effects" I should be wary of?
Yes, at around line 133, under the BackgroundHighlighter::clearHighlights() method, I warn how the code might clear away any background highlights originally added as it sets the ENTIRE document's character background to the default format. This may be an unintended consequence of the result.
How does the completer work?
The words are loaded from a model in main.cpp. Once the user starts typing, with a word of at least 3 characters, the autocompleter shows up. If the completer isn't showing up for you, try to first implement the example on a standalone/separate app.
How can I change the background colour of the formats?
Go to lines 27 to 29 of backgroundhighlighter.cpp. There, you can see that I centralised the formatting. mainFmt refers to the formatting block directly under the cursor. subsidiaryFmt refers to the formatting blocks on congruent symbols. defaultFmt refers to the default format of the entire document which will be used in resetting the format.
Where can I get symbols.txt and wordlist.txt?
I've attached these on my github stackoverflow repository. You can download and copy them from there.
If anything is amiss, please comment below.
Could you provide me a symbols.txt and wordlist.txt cause i dont know what to put there.I tried: symbols.txt: "c cl cla clas class in inc incl inclu includ include class include" wordlist.txt: "class include typedef printf int mainn return " How is the completion list is triggered? with these files, i wasn't able to trigger it.The highlition works
– kostas petsis
Nov 17 '18 at 17:36
Here you go: Link to Github Repo. Err... completion is supposed to be triggered after entering at least 3 characters. If it doesn't work, maybe first try a separate implementation from the example?
– TrebledJ
Nov 17 '18 at 17:44
Edit: I didnt put right the paths of these 2 files.Now it gets triggered by itself.
– kostas petsis
Nov 17 '18 at 17:52
Hmm... so without typing, it pops up automatically? Inmain.cpptry to replace thesetModelimplementation withcompleter->setModel(QStringListModel(QStringList() << "aaaaaaa" << "aaaaab" << "aaaabb" << "aaacccc", completer));and inbackgroundhighlight.cppfill in the default text with something feasible.
– TrebledJ
Nov 17 '18 at 17:57
No i mean that now the completer gets triggered when i add 3 letters.It works well.Could you explain me the what does the symbols.txt does?Isn't the worldist the only thing i need for autocompleting?Whats the purpose of symbols.txt?
– kostas petsis
Nov 17 '18 at 18:04
|
show 8 more comments
Note that this answer hasn't yet covered
I would like to have graphics or widgets for custom autocompletion lists.
but I'm looking into it.
Edit:
For autocompletion follow the Custom Completer Example or the Completer Example.
The code below follows the first one, which I blatantly, unashamedly copied and integrated into the BackgroundHighlighter class and main.cpp.
This answer will contain five files within a project along with a Qt Resource File.
highlighter.h(Highlighter Class for Syntax)highlighter.cppbackgroundHighlighter.h(BackgroundHighlighter Class)backgroundHighlighter.cppmain.cppres.qrc(optional, not needed, you can hardcode your text)res(directory) (optional)|- symbols.txt(optional, you can set your own default text)|- wordlist.txt(optional, copied from example but you could use your own line-delimited word list and set this inmain.cppwith aQStringListModel)
Note that the implementation of the Highlighter class for (1) and (2) can be found in the Qt Syntax Highlighter Example. I will leave its implementation as an exercise for the reader.
In calling the BackgroundHighlighter class, one can pass it a file name to load text from a file. (This wasn't in the OP's specification, but was convenient to implement due to the large amount of text I wanted to test.)
Also note that I integrated the Custom Completer Example into the class.
Here's backgroundHighlighter.h (3) (~45 lines, ~60 lines with completer):
#ifndef BACKGROUNDHIGHLIGHTER_H
#define BACKGROUNDHIGHLIGHTER_H
#include <QtWidgets>
#include <QtGui>
// this is the file to your highlighter
#include "myhighlighter.h"
class BackgroundHighlighter : public QTextEdit
Q_OBJECT
public:
BackgroundHighlighter(const QString &fileName = QString(), QWidget *parent = nullptr);
void loadFile(const QString &fileName);
void setCompleter(QCompleter *completer);
QCompleter *completer() const;
protected:
void keyPressEvent(QKeyEvent *e) override;
void focusInEvent(QFocusEvent *e) override;
public slots:
void onCursorPositionChanged();
private slots:
void insertCompletion(const QString &completion);
private:
// this is your syntax highlighter
Highlighter *syntaxHighlighter;
// stores the symbol being highlighted
QString highlightSymbol;
// stores the position (front of selection) where the cursor was originally placed
int mainHighlightPosition;
// stores character formats to be used
QTextCharFormat mainFmt;
QTextCharFormat subsidiaryFmt;
QTextCharFormat defaultFmt;
void setWordFormat(const int &position, const QTextCharFormat &format);
void runHighlight();
void clearHighlights();
void highlightMatchingSymbols(const QString &symbol);
// completer, copied from example
QString textUnderCursor() const;
QCompleter *c;
;
#endif // BACKGROUNDHIGHLIGHTER_H
And here's backgroundHighlighter.cpp (4) (~160 lines, ~250 lines with completer):
#include "backgroundhighlighter.h"
#include <QDebug>
// constructor
BackgroundHighlighter::BackgroundHighlighter(const QString &fileName, QWidget *parent) :
QTextEdit(parent)
// I like Monaco
setFont(QFont("Monaco"));
setMinimumSize(QSize(500, 200));
// load initial text from a file OR from a hardcoded default
if (!fileName.isEmpty())
loadFile(fileName);
else
QString defaultText = "This is a default text implemented by "
"a stackoverflow user. Please upvote his answer "
"at https://stackoverflow.com/a/53351512/10239789.";
setPlainText(defaultText);
// set the highlighter here
QTextDocument *doc = document();
syntaxHighlighter = new Highlighter(doc);
// TODO change brush/colours to match theme
mainFmt.setBackground(Qt::yellow);
subsidiaryFmt.setBackground(Qt::lightGray);
defaultFmt.setBackground(Qt::white);
// connect the signal to our handler
connect(this, &QTextEdit::cursorPositionChanged, this, &BackgroundHighlighter::onCursorPositionChanged);
// convenience function for reading a file
void BackgroundHighlighter::loadFile(const QString &fileName)
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly))
return;
// the file could be in Plain Text OR Html
setText(file.readAll());
void BackgroundHighlighter::setCompleter(QCompleter *completer)
if (c)
QObject::disconnect(c, 0, this, 0);
c = completer;
if (!c)
return;
c->setWidget(this);
c->setCompletionMode(QCompleter::PopupCompletion);
c->setCaseSensitivity(Qt::CaseInsensitive);
QObject::connect(c, SIGNAL(activated(QString)),
this, SLOT(insertCompletion(QString)));
QCompleter *BackgroundHighlighter::completer() const
return c;
void BackgroundHighlighter::keyPressEvent(QKeyEvent *e)
:"<>?,./;'\-="); // end of word
bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift;
QString completionPrefix = textUnderCursor();
if (!isShortcut && (hasModifier
void BackgroundHighlighter::focusInEvent(QFocusEvent *e)
if (c)
c->setWidget(this);
QTextEdit::focusInEvent(e);
// convenience function for setting a `charFmt` at a `position`
void BackgroundHighlighter::setWordFormat(const int &position, const QTextCharFormat &charFmt)
QTextCursor cursor = textCursor();
cursor.setPosition(position);
cursor.select(QTextCursor::WordUnderCursor);
cursor.setCharFormat(charFmt);
// this will handle the `QTextEdit::cursorPositionChanged()` signal
void BackgroundHighlighter::onCursorPositionChanged()
// if cursor landed on different format, the `currentCharFormat` will be changed
// we need to change it back to white
setCurrentCharFormat(defaultFmt);
// this is the function you're looking for
runHighlight();
void BackgroundHighlighter::insertCompletion(const QString &completion)
if (c->widget() != this)
return;
QTextCursor tc = textCursor();
int extra = completion.length() - c->completionPrefix().length();
tc.movePosition(QTextCursor::Left);
tc.movePosition(QTextCursor::EndOfWord);
tc.insertText(completion.right(extra));
setTextCursor(tc);
QString BackgroundHighlighter::textUnderCursor() const
QTextCursor tc = textCursor();
tc.select(QTextCursor::WordUnderCursor);
return tc.selectedText();
/**
* BRIEF
* Check if new highlighting is needed
* Clear previous highlights
* Check if the word under the cursor is a symbol (i.e. matches ^[A-Za-z0-9_]+$)
* Highlight all relevant symbols
*/
void BackgroundHighlighter::runHighlight()
// retrieve cursor
QTextCursor cursor = textCursor();
// retrieve word under cursor
cursor.select(QTextCursor::WordUnderCursor);
QString wordUnder = cursor.selectedText();
qDebug() << "Word Under Cursor:" << wordUnder;
// get front of cursor, used later for storing in `highlightPositions` or `mainHighlightPosition`
int cursorFront = cursor.selectionStart();
// if the word under cursor is the same, then save time
// by skipping the process
if (wordUnder == highlightSymbol)
// switch formats
setWordFormat(mainHighlightPosition, subsidiaryFmt); // change previous main to subsidiary
setWordFormat(cursorFront, mainFmt); // change position under cursor to main
// update main position
mainHighlightPosition = cursorFront;
// jump the gun
return;
// clear previous highlights
if (mainHighlightPosition != -1)
clearHighlights();
// check if selected word is a symbol
if (!wordUnder.contains(QRegularExpression("^[A-Za-z0-9_]+$")))
qDebug() << wordUnder << "is not a symbol!";
return;
// set the highlight symbol
highlightSymbol = wordUnder;
// store the cursor position to check later
mainHighlightPosition = cursorFront;
// highlight all relevant symbols
highlightMatchingSymbols(wordUnder);
qDebug() << "Highlight donenn";
// clear previously highlights
void BackgroundHighlighter::clearHighlights()
QTextCursor cursor = textCursor();
// wipe the ENTIRE document with the default background, this should be REALLY fast
// WARNING: this may have unintended consequences if you have other backgrounds you want to keep
cursor.select(QTextCursor::Document);
cursor.setCharFormat(defaultFmt);
// reset variables
mainHighlightPosition = -1;
highlightSymbol.clear();
// highlight all matching symbols
void BackgroundHighlighter::highlightMatchingSymbols(const QString &symbol)
// highlight background of congruent symbols
QString docText = toPlainText();
// use a regex with \b to look for standalone symbols
QRegularExpression regexp("\b" + symbol + "\b");
// loop through all matches in the text
int matchPosition = docText.indexOf(regexp);
while (matchPosition != -1)
// if the position
setWordFormat(matchPosition, matchPosition == mainHighlightPosition ? mainFmt : subsidiaryFmt);
// find next match
matchPosition = docText.indexOf(regexp, matchPosition + 1);
Finally, here's main.cpp (5) (~10 lines, ~45 lines with completer)
#include <QApplication>
#include <backgroundhighlighter.h>
QAbstractItemModel *modelFromFile(const QString& fileName, QCompleter *completer)
QFile file(fileName);
if (!file.open(QFile::ReadOnly))
return new QStringListModel(completer);
#ifndef QT_NO_CURSOR
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
#endif
QStringList words;
while (!file.atEnd())
QByteArray line = file.readLine();
if (!line.isEmpty())
words << line.trimmed();
#ifndef QT_NO_CURSOR
QApplication::restoreOverrideCursor();
#endif
return new QStringListModel(words, completer);
int main(int argc, char *argv)
QApplication a(argc, argv);
BackgroundHighlighter bh(":/res/symbols.txt");
QCompleter *completer = new QCompleter();
completer->setModel(modelFromFile(":/res/wordlist.txt", completer));
// use this and comment the above if you don't have or don't want to use wordlist.txt
// QStringListModel *model = new QStringListModel(QStringList() << "aaaaaaa" << "aaaaab" << "aaaabb" << "aaacccc",
completer);
// completer->setModel(model);
completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
completer->setCaseSensitivity(Qt::CaseInsensitive);
completer->setWrapAround(false);
bh.setCompleter(completer);
bh.show();
return a.exec();
In res.qrc add a / prefix and add files (res/symbols.txt, res/wordlist.txt) from the res/ subdirectory.
I have tried my best to
- provide sufficient explanation within the comments.
test my solution in the context of all test cases.
optimise the code (not really) for better runtime performance.
What Qt classes are used?
The code above makes use of the Qt Core Module, Qt Widgets Module, and the Qt Gui Module.
Among these, the main ones I used were
QTextEditfor subclassing theBackgroundHighlighterclass. This is a widget. The signalcursorPositionChangedcomes fromQTextEdit.QCharTextFormatfor formatting blocks of text. Especially with thesetBackground()inherited fromQTextFormat.QTextCursorfor manipulating the cursor (no visual changes), selecting words, and higlighting them.
Some other minor ones were
QRegularExpressionfor symbol matching.QTextDocumentfor initialising the syntax highlighter.QFilefor loading files.- and
QString(obviously).
Other classes may have been used by the Highlighter and the Custom Completer. These include QSyntaxHighlighter, QCompleter, and a couple event class.
Were there any previous attempts you tried that I should look out for so that I don't fall into the same manhole?
I spent a while trying to find how I might be able to implement it with QTextBlock and QTextBlockFormat. Apparently, QTextBlock only recognises one block as a line. I tried using several blocks per line to handle symbols and even tried using QTextTable to solve the issue. I failed miserably.
How "optimised" is this code?
Note that I didn't go all serious and write test/edge cases, profile it, or time it (I might time it).
When I said "optimise", I was alluding to the fact that I had slower, worse implementations before (using QList and whatnot).
I have tested with a symbols.txt file resembling
symbol1 symbol2 symbol3 symbol4 symbol5
symbol1 symbol2 symbol3 symbol4 symbol5
symbol1 symbol2 symbol3 symbol4 symbol5
// ... 500 lines
and I'm happy to say that I find the time reasonable (approx. 1 second for me?).
However, you might want to watch over for the line count as it grows. With the same text file at 1000 lines, the program will start to take approx. 3 seconds for highlighting.
Note that... I haven't optimised it entirely. There could possibly be a better implementation which formats only when the symbol scrolls into the user's view. This is just a suggestion. How to implement it I don't know. (But I might try 🤔.)
Are there any "side effects" I should be wary of?
Yes, at around line 133, under the BackgroundHighlighter::clearHighlights() method, I warn how the code might clear away any background highlights originally added as it sets the ENTIRE document's character background to the default format. This may be an unintended consequence of the result.
How does the completer work?
The words are loaded from a model in main.cpp. Once the user starts typing, with a word of at least 3 characters, the autocompleter shows up. If the completer isn't showing up for you, try to first implement the example on a standalone/separate app.
How can I change the background colour of the formats?
Go to lines 27 to 29 of backgroundhighlighter.cpp. There, you can see that I centralised the formatting. mainFmt refers to the formatting block directly under the cursor. subsidiaryFmt refers to the formatting blocks on congruent symbols. defaultFmt refers to the default format of the entire document which will be used in resetting the format.
Where can I get symbols.txt and wordlist.txt?
I've attached these on my github stackoverflow repository. You can download and copy them from there.
If anything is amiss, please comment below.
Could you provide me a symbols.txt and wordlist.txt cause i dont know what to put there.I tried: symbols.txt: "c cl cla clas class in inc incl inclu includ include class include" wordlist.txt: "class include typedef printf int mainn return " How is the completion list is triggered? with these files, i wasn't able to trigger it.The highlition works
– kostas petsis
Nov 17 '18 at 17:36
Here you go: Link to Github Repo. Err... completion is supposed to be triggered after entering at least 3 characters. If it doesn't work, maybe first try a separate implementation from the example?
– TrebledJ
Nov 17 '18 at 17:44
Edit: I didnt put right the paths of these 2 files.Now it gets triggered by itself.
– kostas petsis
Nov 17 '18 at 17:52
Hmm... so without typing, it pops up automatically? Inmain.cpptry to replace thesetModelimplementation withcompleter->setModel(QStringListModel(QStringList() << "aaaaaaa" << "aaaaab" << "aaaabb" << "aaacccc", completer));and inbackgroundhighlight.cppfill in the default text with something feasible.
– TrebledJ
Nov 17 '18 at 17:57
No i mean that now the completer gets triggered when i add 3 letters.It works well.Could you explain me the what does the symbols.txt does?Isn't the worldist the only thing i need for autocompleting?Whats the purpose of symbols.txt?
– kostas petsis
Nov 17 '18 at 18:04
|
show 8 more comments
Note that this answer hasn't yet covered
I would like to have graphics or widgets for custom autocompletion lists.
but I'm looking into it.
Edit:
For autocompletion follow the Custom Completer Example or the Completer Example.
The code below follows the first one, which I blatantly, unashamedly copied and integrated into the BackgroundHighlighter class and main.cpp.
This answer will contain five files within a project along with a Qt Resource File.
highlighter.h(Highlighter Class for Syntax)highlighter.cppbackgroundHighlighter.h(BackgroundHighlighter Class)backgroundHighlighter.cppmain.cppres.qrc(optional, not needed, you can hardcode your text)res(directory) (optional)|- symbols.txt(optional, you can set your own default text)|- wordlist.txt(optional, copied from example but you could use your own line-delimited word list and set this inmain.cppwith aQStringListModel)
Note that the implementation of the Highlighter class for (1) and (2) can be found in the Qt Syntax Highlighter Example. I will leave its implementation as an exercise for the reader.
In calling the BackgroundHighlighter class, one can pass it a file name to load text from a file. (This wasn't in the OP's specification, but was convenient to implement due to the large amount of text I wanted to test.)
Also note that I integrated the Custom Completer Example into the class.
Here's backgroundHighlighter.h (3) (~45 lines, ~60 lines with completer):
#ifndef BACKGROUNDHIGHLIGHTER_H
#define BACKGROUNDHIGHLIGHTER_H
#include <QtWidgets>
#include <QtGui>
// this is the file to your highlighter
#include "myhighlighter.h"
class BackgroundHighlighter : public QTextEdit
Q_OBJECT
public:
BackgroundHighlighter(const QString &fileName = QString(), QWidget *parent = nullptr);
void loadFile(const QString &fileName);
void setCompleter(QCompleter *completer);
QCompleter *completer() const;
protected:
void keyPressEvent(QKeyEvent *e) override;
void focusInEvent(QFocusEvent *e) override;
public slots:
void onCursorPositionChanged();
private slots:
void insertCompletion(const QString &completion);
private:
// this is your syntax highlighter
Highlighter *syntaxHighlighter;
// stores the symbol being highlighted
QString highlightSymbol;
// stores the position (front of selection) where the cursor was originally placed
int mainHighlightPosition;
// stores character formats to be used
QTextCharFormat mainFmt;
QTextCharFormat subsidiaryFmt;
QTextCharFormat defaultFmt;
void setWordFormat(const int &position, const QTextCharFormat &format);
void runHighlight();
void clearHighlights();
void highlightMatchingSymbols(const QString &symbol);
// completer, copied from example
QString textUnderCursor() const;
QCompleter *c;
;
#endif // BACKGROUNDHIGHLIGHTER_H
And here's backgroundHighlighter.cpp (4) (~160 lines, ~250 lines with completer):
#include "backgroundhighlighter.h"
#include <QDebug>
// constructor
BackgroundHighlighter::BackgroundHighlighter(const QString &fileName, QWidget *parent) :
QTextEdit(parent)
// I like Monaco
setFont(QFont("Monaco"));
setMinimumSize(QSize(500, 200));
// load initial text from a file OR from a hardcoded default
if (!fileName.isEmpty())
loadFile(fileName);
else
QString defaultText = "This is a default text implemented by "
"a stackoverflow user. Please upvote his answer "
"at https://stackoverflow.com/a/53351512/10239789.";
setPlainText(defaultText);
// set the highlighter here
QTextDocument *doc = document();
syntaxHighlighter = new Highlighter(doc);
// TODO change brush/colours to match theme
mainFmt.setBackground(Qt::yellow);
subsidiaryFmt.setBackground(Qt::lightGray);
defaultFmt.setBackground(Qt::white);
// connect the signal to our handler
connect(this, &QTextEdit::cursorPositionChanged, this, &BackgroundHighlighter::onCursorPositionChanged);
// convenience function for reading a file
void BackgroundHighlighter::loadFile(const QString &fileName)
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly))
return;
// the file could be in Plain Text OR Html
setText(file.readAll());
void BackgroundHighlighter::setCompleter(QCompleter *completer)
if (c)
QObject::disconnect(c, 0, this, 0);
c = completer;
if (!c)
return;
c->setWidget(this);
c->setCompletionMode(QCompleter::PopupCompletion);
c->setCaseSensitivity(Qt::CaseInsensitive);
QObject::connect(c, SIGNAL(activated(QString)),
this, SLOT(insertCompletion(QString)));
QCompleter *BackgroundHighlighter::completer() const
return c;
void BackgroundHighlighter::keyPressEvent(QKeyEvent *e)
:"<>?,./;'\-="); // end of word
bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift;
QString completionPrefix = textUnderCursor();
if (!isShortcut && (hasModifier
void BackgroundHighlighter::focusInEvent(QFocusEvent *e)
if (c)
c->setWidget(this);
QTextEdit::focusInEvent(e);
// convenience function for setting a `charFmt` at a `position`
void BackgroundHighlighter::setWordFormat(const int &position, const QTextCharFormat &charFmt)
QTextCursor cursor = textCursor();
cursor.setPosition(position);
cursor.select(QTextCursor::WordUnderCursor);
cursor.setCharFormat(charFmt);
// this will handle the `QTextEdit::cursorPositionChanged()` signal
void BackgroundHighlighter::onCursorPositionChanged()
// if cursor landed on different format, the `currentCharFormat` will be changed
// we need to change it back to white
setCurrentCharFormat(defaultFmt);
// this is the function you're looking for
runHighlight();
void BackgroundHighlighter::insertCompletion(const QString &completion)
if (c->widget() != this)
return;
QTextCursor tc = textCursor();
int extra = completion.length() - c->completionPrefix().length();
tc.movePosition(QTextCursor::Left);
tc.movePosition(QTextCursor::EndOfWord);
tc.insertText(completion.right(extra));
setTextCursor(tc);
QString BackgroundHighlighter::textUnderCursor() const
QTextCursor tc = textCursor();
tc.select(QTextCursor::WordUnderCursor);
return tc.selectedText();
/**
* BRIEF
* Check if new highlighting is needed
* Clear previous highlights
* Check if the word under the cursor is a symbol (i.e. matches ^[A-Za-z0-9_]+$)
* Highlight all relevant symbols
*/
void BackgroundHighlighter::runHighlight()
// retrieve cursor
QTextCursor cursor = textCursor();
// retrieve word under cursor
cursor.select(QTextCursor::WordUnderCursor);
QString wordUnder = cursor.selectedText();
qDebug() << "Word Under Cursor:" << wordUnder;
// get front of cursor, used later for storing in `highlightPositions` or `mainHighlightPosition`
int cursorFront = cursor.selectionStart();
// if the word under cursor is the same, then save time
// by skipping the process
if (wordUnder == highlightSymbol)
// switch formats
setWordFormat(mainHighlightPosition, subsidiaryFmt); // change previous main to subsidiary
setWordFormat(cursorFront, mainFmt); // change position under cursor to main
// update main position
mainHighlightPosition = cursorFront;
// jump the gun
return;
// clear previous highlights
if (mainHighlightPosition != -1)
clearHighlights();
// check if selected word is a symbol
if (!wordUnder.contains(QRegularExpression("^[A-Za-z0-9_]+$")))
qDebug() << wordUnder << "is not a symbol!";
return;
// set the highlight symbol
highlightSymbol = wordUnder;
// store the cursor position to check later
mainHighlightPosition = cursorFront;
// highlight all relevant symbols
highlightMatchingSymbols(wordUnder);
qDebug() << "Highlight donenn";
// clear previously highlights
void BackgroundHighlighter::clearHighlights()
QTextCursor cursor = textCursor();
// wipe the ENTIRE document with the default background, this should be REALLY fast
// WARNING: this may have unintended consequences if you have other backgrounds you want to keep
cursor.select(QTextCursor::Document);
cursor.setCharFormat(defaultFmt);
// reset variables
mainHighlightPosition = -1;
highlightSymbol.clear();
// highlight all matching symbols
void BackgroundHighlighter::highlightMatchingSymbols(const QString &symbol)
// highlight background of congruent symbols
QString docText = toPlainText();
// use a regex with \b to look for standalone symbols
QRegularExpression regexp("\b" + symbol + "\b");
// loop through all matches in the text
int matchPosition = docText.indexOf(regexp);
while (matchPosition != -1)
// if the position
setWordFormat(matchPosition, matchPosition == mainHighlightPosition ? mainFmt : subsidiaryFmt);
// find next match
matchPosition = docText.indexOf(regexp, matchPosition + 1);
Finally, here's main.cpp (5) (~10 lines, ~45 lines with completer)
#include <QApplication>
#include <backgroundhighlighter.h>
QAbstractItemModel *modelFromFile(const QString& fileName, QCompleter *completer)
QFile file(fileName);
if (!file.open(QFile::ReadOnly))
return new QStringListModel(completer);
#ifndef QT_NO_CURSOR
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
#endif
QStringList words;
while (!file.atEnd())
QByteArray line = file.readLine();
if (!line.isEmpty())
words << line.trimmed();
#ifndef QT_NO_CURSOR
QApplication::restoreOverrideCursor();
#endif
return new QStringListModel(words, completer);
int main(int argc, char *argv)
QApplication a(argc, argv);
BackgroundHighlighter bh(":/res/symbols.txt");
QCompleter *completer = new QCompleter();
completer->setModel(modelFromFile(":/res/wordlist.txt", completer));
// use this and comment the above if you don't have or don't want to use wordlist.txt
// QStringListModel *model = new QStringListModel(QStringList() << "aaaaaaa" << "aaaaab" << "aaaabb" << "aaacccc",
completer);
// completer->setModel(model);
completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
completer->setCaseSensitivity(Qt::CaseInsensitive);
completer->setWrapAround(false);
bh.setCompleter(completer);
bh.show();
return a.exec();
In res.qrc add a / prefix and add files (res/symbols.txt, res/wordlist.txt) from the res/ subdirectory.
I have tried my best to
- provide sufficient explanation within the comments.
test my solution in the context of all test cases.
optimise the code (not really) for better runtime performance.
What Qt classes are used?
The code above makes use of the Qt Core Module, Qt Widgets Module, and the Qt Gui Module.
Among these, the main ones I used were
QTextEditfor subclassing theBackgroundHighlighterclass. This is a widget. The signalcursorPositionChangedcomes fromQTextEdit.QCharTextFormatfor formatting blocks of text. Especially with thesetBackground()inherited fromQTextFormat.QTextCursorfor manipulating the cursor (no visual changes), selecting words, and higlighting them.
Some other minor ones were
QRegularExpressionfor symbol matching.QTextDocumentfor initialising the syntax highlighter.QFilefor loading files.- and
QString(obviously).
Other classes may have been used by the Highlighter and the Custom Completer. These include QSyntaxHighlighter, QCompleter, and a couple event class.
Were there any previous attempts you tried that I should look out for so that I don't fall into the same manhole?
I spent a while trying to find how I might be able to implement it with QTextBlock and QTextBlockFormat. Apparently, QTextBlock only recognises one block as a line. I tried using several blocks per line to handle symbols and even tried using QTextTable to solve the issue. I failed miserably.
How "optimised" is this code?
Note that I didn't go all serious and write test/edge cases, profile it, or time it (I might time it).
When I said "optimise", I was alluding to the fact that I had slower, worse implementations before (using QList and whatnot).
I have tested with a symbols.txt file resembling
symbol1 symbol2 symbol3 symbol4 symbol5
symbol1 symbol2 symbol3 symbol4 symbol5
symbol1 symbol2 symbol3 symbol4 symbol5
// ... 500 lines
and I'm happy to say that I find the time reasonable (approx. 1 second for me?).
However, you might want to watch over for the line count as it grows. With the same text file at 1000 lines, the program will start to take approx. 3 seconds for highlighting.
Note that... I haven't optimised it entirely. There could possibly be a better implementation which formats only when the symbol scrolls into the user's view. This is just a suggestion. How to implement it I don't know. (But I might try 🤔.)
Are there any "side effects" I should be wary of?
Yes, at around line 133, under the BackgroundHighlighter::clearHighlights() method, I warn how the code might clear away any background highlights originally added as it sets the ENTIRE document's character background to the default format. This may be an unintended consequence of the result.
How does the completer work?
The words are loaded from a model in main.cpp. Once the user starts typing, with a word of at least 3 characters, the autocompleter shows up. If the completer isn't showing up for you, try to first implement the example on a standalone/separate app.
How can I change the background colour of the formats?
Go to lines 27 to 29 of backgroundhighlighter.cpp. There, you can see that I centralised the formatting. mainFmt refers to the formatting block directly under the cursor. subsidiaryFmt refers to the formatting blocks on congruent symbols. defaultFmt refers to the default format of the entire document which will be used in resetting the format.
Where can I get symbols.txt and wordlist.txt?
I've attached these on my github stackoverflow repository. You can download and copy them from there.
If anything is amiss, please comment below.
Note that this answer hasn't yet covered
I would like to have graphics or widgets for custom autocompletion lists.
but I'm looking into it.
Edit:
For autocompletion follow the Custom Completer Example or the Completer Example.
The code below follows the first one, which I blatantly, unashamedly copied and integrated into the BackgroundHighlighter class and main.cpp.
This answer will contain five files within a project along with a Qt Resource File.
highlighter.h(Highlighter Class for Syntax)highlighter.cppbackgroundHighlighter.h(BackgroundHighlighter Class)backgroundHighlighter.cppmain.cppres.qrc(optional, not needed, you can hardcode your text)res(directory) (optional)|- symbols.txt(optional, you can set your own default text)|- wordlist.txt(optional, copied from example but you could use your own line-delimited word list and set this inmain.cppwith aQStringListModel)
Note that the implementation of the Highlighter class for (1) and (2) can be found in the Qt Syntax Highlighter Example. I will leave its implementation as an exercise for the reader.
In calling the BackgroundHighlighter class, one can pass it a file name to load text from a file. (This wasn't in the OP's specification, but was convenient to implement due to the large amount of text I wanted to test.)
Also note that I integrated the Custom Completer Example into the class.
Here's backgroundHighlighter.h (3) (~45 lines, ~60 lines with completer):
#ifndef BACKGROUNDHIGHLIGHTER_H
#define BACKGROUNDHIGHLIGHTER_H
#include <QtWidgets>
#include <QtGui>
// this is the file to your highlighter
#include "myhighlighter.h"
class BackgroundHighlighter : public QTextEdit
Q_OBJECT
public:
BackgroundHighlighter(const QString &fileName = QString(), QWidget *parent = nullptr);
void loadFile(const QString &fileName);
void setCompleter(QCompleter *completer);
QCompleter *completer() const;
protected:
void keyPressEvent(QKeyEvent *e) override;
void focusInEvent(QFocusEvent *e) override;
public slots:
void onCursorPositionChanged();
private slots:
void insertCompletion(const QString &completion);
private:
// this is your syntax highlighter
Highlighter *syntaxHighlighter;
// stores the symbol being highlighted
QString highlightSymbol;
// stores the position (front of selection) where the cursor was originally placed
int mainHighlightPosition;
// stores character formats to be used
QTextCharFormat mainFmt;
QTextCharFormat subsidiaryFmt;
QTextCharFormat defaultFmt;
void setWordFormat(const int &position, const QTextCharFormat &format);
void runHighlight();
void clearHighlights();
void highlightMatchingSymbols(const QString &symbol);
// completer, copied from example
QString textUnderCursor() const;
QCompleter *c;
;
#endif // BACKGROUNDHIGHLIGHTER_H
And here's backgroundHighlighter.cpp (4) (~160 lines, ~250 lines with completer):
#include "backgroundhighlighter.h"
#include <QDebug>
// constructor
BackgroundHighlighter::BackgroundHighlighter(const QString &fileName, QWidget *parent) :
QTextEdit(parent)
// I like Monaco
setFont(QFont("Monaco"));
setMinimumSize(QSize(500, 200));
// load initial text from a file OR from a hardcoded default
if (!fileName.isEmpty())
loadFile(fileName);
else
QString defaultText = "This is a default text implemented by "
"a stackoverflow user. Please upvote his answer "
"at https://stackoverflow.com/a/53351512/10239789.";
setPlainText(defaultText);
// set the highlighter here
QTextDocument *doc = document();
syntaxHighlighter = new Highlighter(doc);
// TODO change brush/colours to match theme
mainFmt.setBackground(Qt::yellow);
subsidiaryFmt.setBackground(Qt::lightGray);
defaultFmt.setBackground(Qt::white);
// connect the signal to our handler
connect(this, &QTextEdit::cursorPositionChanged, this, &BackgroundHighlighter::onCursorPositionChanged);
// convenience function for reading a file
void BackgroundHighlighter::loadFile(const QString &fileName)
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly))
return;
// the file could be in Plain Text OR Html
setText(file.readAll());
void BackgroundHighlighter::setCompleter(QCompleter *completer)
if (c)
QObject::disconnect(c, 0, this, 0);
c = completer;
if (!c)
return;
c->setWidget(this);
c->setCompletionMode(QCompleter::PopupCompletion);
c->setCaseSensitivity(Qt::CaseInsensitive);
QObject::connect(c, SIGNAL(activated(QString)),
this, SLOT(insertCompletion(QString)));
QCompleter *BackgroundHighlighter::completer() const
return c;
void BackgroundHighlighter::keyPressEvent(QKeyEvent *e)
:"<>?,./;'\-="); // end of word
bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift;
QString completionPrefix = textUnderCursor();
if (!isShortcut && (hasModifier
void BackgroundHighlighter::focusInEvent(QFocusEvent *e)
if (c)
c->setWidget(this);
QTextEdit::focusInEvent(e);
// convenience function for setting a `charFmt` at a `position`
void BackgroundHighlighter::setWordFormat(const int &position, const QTextCharFormat &charFmt)
QTextCursor cursor = textCursor();
cursor.setPosition(position);
cursor.select(QTextCursor::WordUnderCursor);
cursor.setCharFormat(charFmt);
// this will handle the `QTextEdit::cursorPositionChanged()` signal
void BackgroundHighlighter::onCursorPositionChanged()
// if cursor landed on different format, the `currentCharFormat` will be changed
// we need to change it back to white
setCurrentCharFormat(defaultFmt);
// this is the function you're looking for
runHighlight();
void BackgroundHighlighter::insertCompletion(const QString &completion)
if (c->widget() != this)
return;
QTextCursor tc = textCursor();
int extra = completion.length() - c->completionPrefix().length();
tc.movePosition(QTextCursor::Left);
tc.movePosition(QTextCursor::EndOfWord);
tc.insertText(completion.right(extra));
setTextCursor(tc);
QString BackgroundHighlighter::textUnderCursor() const
QTextCursor tc = textCursor();
tc.select(QTextCursor::WordUnderCursor);
return tc.selectedText();
/**
* BRIEF
* Check if new highlighting is needed
* Clear previous highlights
* Check if the word under the cursor is a symbol (i.e. matches ^[A-Za-z0-9_]+$)
* Highlight all relevant symbols
*/
void BackgroundHighlighter::runHighlight()
// retrieve cursor
QTextCursor cursor = textCursor();
// retrieve word under cursor
cursor.select(QTextCursor::WordUnderCursor);
QString wordUnder = cursor.selectedText();
qDebug() << "Word Under Cursor:" << wordUnder;
// get front of cursor, used later for storing in `highlightPositions` or `mainHighlightPosition`
int cursorFront = cursor.selectionStart();
// if the word under cursor is the same, then save time
// by skipping the process
if (wordUnder == highlightSymbol)
// switch formats
setWordFormat(mainHighlightPosition, subsidiaryFmt); // change previous main to subsidiary
setWordFormat(cursorFront, mainFmt); // change position under cursor to main
// update main position
mainHighlightPosition = cursorFront;
// jump the gun
return;
// clear previous highlights
if (mainHighlightPosition != -1)
clearHighlights();
// check if selected word is a symbol
if (!wordUnder.contains(QRegularExpression("^[A-Za-z0-9_]+$")))
qDebug() << wordUnder << "is not a symbol!";
return;
// set the highlight symbol
highlightSymbol = wordUnder;
// store the cursor position to check later
mainHighlightPosition = cursorFront;
// highlight all relevant symbols
highlightMatchingSymbols(wordUnder);
qDebug() << "Highlight donenn";
// clear previously highlights
void BackgroundHighlighter::clearHighlights()
QTextCursor cursor = textCursor();
// wipe the ENTIRE document with the default background, this should be REALLY fast
// WARNING: this may have unintended consequences if you have other backgrounds you want to keep
cursor.select(QTextCursor::Document);
cursor.setCharFormat(defaultFmt);
// reset variables
mainHighlightPosition = -1;
highlightSymbol.clear();
// highlight all matching symbols
void BackgroundHighlighter::highlightMatchingSymbols(const QString &symbol)
// highlight background of congruent symbols
QString docText = toPlainText();
// use a regex with \b to look for standalone symbols
QRegularExpression regexp("\b" + symbol + "\b");
// loop through all matches in the text
int matchPosition = docText.indexOf(regexp);
while (matchPosition != -1)
// if the position
setWordFormat(matchPosition, matchPosition == mainHighlightPosition ? mainFmt : subsidiaryFmt);
// find next match
matchPosition = docText.indexOf(regexp, matchPosition + 1);
Finally, here's main.cpp (5) (~10 lines, ~45 lines with completer)
#include <QApplication>
#include <backgroundhighlighter.h>
QAbstractItemModel *modelFromFile(const QString& fileName, QCompleter *completer)
QFile file(fileName);
if (!file.open(QFile::ReadOnly))
return new QStringListModel(completer);
#ifndef QT_NO_CURSOR
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
#endif
QStringList words;
while (!file.atEnd())
QByteArray line = file.readLine();
if (!line.isEmpty())
words << line.trimmed();
#ifndef QT_NO_CURSOR
QApplication::restoreOverrideCursor();
#endif
return new QStringListModel(words, completer);
int main(int argc, char *argv)
QApplication a(argc, argv);
BackgroundHighlighter bh(":/res/symbols.txt");
QCompleter *completer = new QCompleter();
completer->setModel(modelFromFile(":/res/wordlist.txt", completer));
// use this and comment the above if you don't have or don't want to use wordlist.txt
// QStringListModel *model = new QStringListModel(QStringList() << "aaaaaaa" << "aaaaab" << "aaaabb" << "aaacccc",
completer);
// completer->setModel(model);
completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
completer->setCaseSensitivity(Qt::CaseInsensitive);
completer->setWrapAround(false);
bh.setCompleter(completer);
bh.show();
return a.exec();
In res.qrc add a / prefix and add files (res/symbols.txt, res/wordlist.txt) from the res/ subdirectory.
I have tried my best to
- provide sufficient explanation within the comments.
test my solution in the context of all test cases.
optimise the code (not really) for better runtime performance.
What Qt classes are used?
The code above makes use of the Qt Core Module, Qt Widgets Module, and the Qt Gui Module.
Among these, the main ones I used were
QTextEditfor subclassing theBackgroundHighlighterclass. This is a widget. The signalcursorPositionChangedcomes fromQTextEdit.QCharTextFormatfor formatting blocks of text. Especially with thesetBackground()inherited fromQTextFormat.QTextCursorfor manipulating the cursor (no visual changes), selecting words, and higlighting them.
Some other minor ones were
QRegularExpressionfor symbol matching.QTextDocumentfor initialising the syntax highlighter.QFilefor loading files.- and
QString(obviously).
Other classes may have been used by the Highlighter and the Custom Completer. These include QSyntaxHighlighter, QCompleter, and a couple event class.
Were there any previous attempts you tried that I should look out for so that I don't fall into the same manhole?
I spent a while trying to find how I might be able to implement it with QTextBlock and QTextBlockFormat. Apparently, QTextBlock only recognises one block as a line. I tried using several blocks per line to handle symbols and even tried using QTextTable to solve the issue. I failed miserably.
How "optimised" is this code?
Note that I didn't go all serious and write test/edge cases, profile it, or time it (I might time it).
When I said "optimise", I was alluding to the fact that I had slower, worse implementations before (using QList and whatnot).
I have tested with a symbols.txt file resembling
symbol1 symbol2 symbol3 symbol4 symbol5
symbol1 symbol2 symbol3 symbol4 symbol5
symbol1 symbol2 symbol3 symbol4 symbol5
// ... 500 lines
and I'm happy to say that I find the time reasonable (approx. 1 second for me?).
However, you might want to watch over for the line count as it grows. With the same text file at 1000 lines, the program will start to take approx. 3 seconds for highlighting.
Note that... I haven't optimised it entirely. There could possibly be a better implementation which formats only when the symbol scrolls into the user's view. This is just a suggestion. How to implement it I don't know. (But I might try 🤔.)
Are there any "side effects" I should be wary of?
Yes, at around line 133, under the BackgroundHighlighter::clearHighlights() method, I warn how the code might clear away any background highlights originally added as it sets the ENTIRE document's character background to the default format. This may be an unintended consequence of the result.
How does the completer work?
The words are loaded from a model in main.cpp. Once the user starts typing, with a word of at least 3 characters, the autocompleter shows up. If the completer isn't showing up for you, try to first implement the example on a standalone/separate app.
How can I change the background colour of the formats?
Go to lines 27 to 29 of backgroundhighlighter.cpp. There, you can see that I centralised the formatting. mainFmt refers to the formatting block directly under the cursor. subsidiaryFmt refers to the formatting blocks on congruent symbols. defaultFmt refers to the default format of the entire document which will be used in resetting the format.
Where can I get symbols.txt and wordlist.txt?
I've attached these on my github stackoverflow repository. You can download and copy them from there.
If anything is amiss, please comment below.
edited Nov 17 '18 at 18:51
answered Nov 17 '18 at 13:03
TrebledJTrebledJ
3,66421328
3,66421328
Could you provide me a symbols.txt and wordlist.txt cause i dont know what to put there.I tried: symbols.txt: "c cl cla clas class in inc incl inclu includ include class include" wordlist.txt: "class include typedef printf int mainn return " How is the completion list is triggered? with these files, i wasn't able to trigger it.The highlition works
– kostas petsis
Nov 17 '18 at 17:36
Here you go: Link to Github Repo. Err... completion is supposed to be triggered after entering at least 3 characters. If it doesn't work, maybe first try a separate implementation from the example?
– TrebledJ
Nov 17 '18 at 17:44
Edit: I didnt put right the paths of these 2 files.Now it gets triggered by itself.
– kostas petsis
Nov 17 '18 at 17:52
Hmm... so without typing, it pops up automatically? Inmain.cpptry to replace thesetModelimplementation withcompleter->setModel(QStringListModel(QStringList() << "aaaaaaa" << "aaaaab" << "aaaabb" << "aaacccc", completer));and inbackgroundhighlight.cppfill in the default text with something feasible.
– TrebledJ
Nov 17 '18 at 17:57
No i mean that now the completer gets triggered when i add 3 letters.It works well.Could you explain me the what does the symbols.txt does?Isn't the worldist the only thing i need for autocompleting?Whats the purpose of symbols.txt?
– kostas petsis
Nov 17 '18 at 18:04
|
show 8 more comments
Could you provide me a symbols.txt and wordlist.txt cause i dont know what to put there.I tried: symbols.txt: "c cl cla clas class in inc incl inclu includ include class include" wordlist.txt: "class include typedef printf int mainn return " How is the completion list is triggered? with these files, i wasn't able to trigger it.The highlition works
– kostas petsis
Nov 17 '18 at 17:36
Here you go: Link to Github Repo. Err... completion is supposed to be triggered after entering at least 3 characters. If it doesn't work, maybe first try a separate implementation from the example?
– TrebledJ
Nov 17 '18 at 17:44
Edit: I didnt put right the paths of these 2 files.Now it gets triggered by itself.
– kostas petsis
Nov 17 '18 at 17:52
Hmm... so without typing, it pops up automatically? Inmain.cpptry to replace thesetModelimplementation withcompleter->setModel(QStringListModel(QStringList() << "aaaaaaa" << "aaaaab" << "aaaabb" << "aaacccc", completer));and inbackgroundhighlight.cppfill in the default text with something feasible.
– TrebledJ
Nov 17 '18 at 17:57
No i mean that now the completer gets triggered when i add 3 letters.It works well.Could you explain me the what does the symbols.txt does?Isn't the worldist the only thing i need for autocompleting?Whats the purpose of symbols.txt?
– kostas petsis
Nov 17 '18 at 18:04
Could you provide me a symbols.txt and wordlist.txt cause i dont know what to put there.I tried: symbols.txt: "c cl cla clas class in inc incl inclu includ include class include" wordlist.txt: "class include typedef printf int mainn return " How is the completion list is triggered? with these files, i wasn't able to trigger it.The highlition works
– kostas petsis
Nov 17 '18 at 17:36
Could you provide me a symbols.txt and wordlist.txt cause i dont know what to put there.I tried: symbols.txt: "c cl cla clas class in inc incl inclu includ include class include" wordlist.txt: "class include typedef printf int mainn return " How is the completion list is triggered? with these files, i wasn't able to trigger it.The highlition works
– kostas petsis
Nov 17 '18 at 17:36
Here you go: Link to Github Repo. Err... completion is supposed to be triggered after entering at least 3 characters. If it doesn't work, maybe first try a separate implementation from the example?
– TrebledJ
Nov 17 '18 at 17:44
Here you go: Link to Github Repo. Err... completion is supposed to be triggered after entering at least 3 characters. If it doesn't work, maybe first try a separate implementation from the example?
– TrebledJ
Nov 17 '18 at 17:44
Edit: I didnt put right the paths of these 2 files.Now it gets triggered by itself.
– kostas petsis
Nov 17 '18 at 17:52
Edit: I didnt put right the paths of these 2 files.Now it gets triggered by itself.
– kostas petsis
Nov 17 '18 at 17:52
Hmm... so without typing, it pops up automatically? In
main.cpp try to replace the setModel implementation with completer->setModel(QStringListModel(QStringList() << "aaaaaaa" << "aaaaab" << "aaaabb" << "aaacccc", completer)); and in backgroundhighlight.cpp fill in the default text with something feasible.– TrebledJ
Nov 17 '18 at 17:57
Hmm... so without typing, it pops up automatically? In
main.cpp try to replace the setModel implementation with completer->setModel(QStringListModel(QStringList() << "aaaaaaa" << "aaaaab" << "aaaabb" << "aaacccc", completer)); and in backgroundhighlight.cpp fill in the default text with something feasible.– TrebledJ
Nov 17 '18 at 17:57
No i mean that now the completer gets triggered when i add 3 letters.It works well.Could you explain me the what does the symbols.txt does?Isn't the worldist the only thing i need for autocompleting?Whats the purpose of symbols.txt?
– kostas petsis
Nov 17 '18 at 18:04
No i mean that now the completer gets triggered when i add 3 letters.It works well.Could you explain me the what does the symbols.txt does?Isn't the worldist the only thing i need for autocompleting?Whats the purpose of symbols.txt?
– kostas petsis
Nov 17 '18 at 18:04
|
show 8 more comments
For styled background of selected words like in the picture in the 1st post edit the backgroundhighlighter.cpp at line 30
QRadialGradient gradient(50, 50, 50, 50, 50);
gradient.setColorAt(0, QColor::fromRgbF(0, 1, 0, 1));
gradient.setColorAt(1, QColor::fromRgbF(0, 0, 0, 0));
QBrush brush(gradient);
subsidiaryFmt.setBackground(brush);

But after the first word the gradients gets lost..I'll try to fix this.Any ideas?
This should be in a separate question so that others can help along + debug. :-)
– TrebledJ
Nov 17 '18 at 18:47
1
Seems to me like the gradient is anchored at a certain center point.
– TrebledJ
Nov 17 '18 at 18:55
add a comment |
For styled background of selected words like in the picture in the 1st post edit the backgroundhighlighter.cpp at line 30
QRadialGradient gradient(50, 50, 50, 50, 50);
gradient.setColorAt(0, QColor::fromRgbF(0, 1, 0, 1));
gradient.setColorAt(1, QColor::fromRgbF(0, 0, 0, 0));
QBrush brush(gradient);
subsidiaryFmt.setBackground(brush);

But after the first word the gradients gets lost..I'll try to fix this.Any ideas?
This should be in a separate question so that others can help along + debug. :-)
– TrebledJ
Nov 17 '18 at 18:47
1
Seems to me like the gradient is anchored at a certain center point.
– TrebledJ
Nov 17 '18 at 18:55
add a comment |
For styled background of selected words like in the picture in the 1st post edit the backgroundhighlighter.cpp at line 30
QRadialGradient gradient(50, 50, 50, 50, 50);
gradient.setColorAt(0, QColor::fromRgbF(0, 1, 0, 1));
gradient.setColorAt(1, QColor::fromRgbF(0, 0, 0, 0));
QBrush brush(gradient);
subsidiaryFmt.setBackground(brush);

But after the first word the gradients gets lost..I'll try to fix this.Any ideas?
For styled background of selected words like in the picture in the 1st post edit the backgroundhighlighter.cpp at line 30
QRadialGradient gradient(50, 50, 50, 50, 50);
gradient.setColorAt(0, QColor::fromRgbF(0, 1, 0, 1));
gradient.setColorAt(1, QColor::fromRgbF(0, 0, 0, 0));
QBrush brush(gradient);
subsidiaryFmt.setBackground(brush);

But after the first word the gradients gets lost..I'll try to fix this.Any ideas?
answered Nov 17 '18 at 18:39
kostas petsiskostas petsis
136
136
This should be in a separate question so that others can help along + debug. :-)
– TrebledJ
Nov 17 '18 at 18:47
1
Seems to me like the gradient is anchored at a certain center point.
– TrebledJ
Nov 17 '18 at 18:55
add a comment |
This should be in a separate question so that others can help along + debug. :-)
– TrebledJ
Nov 17 '18 at 18:47
1
Seems to me like the gradient is anchored at a certain center point.
– TrebledJ
Nov 17 '18 at 18:55
This should be in a separate question so that others can help along + debug. :-)
– TrebledJ
Nov 17 '18 at 18:47
This should be in a separate question so that others can help along + debug. :-)
– TrebledJ
Nov 17 '18 at 18:47
1
1
Seems to me like the gradient is anchored at a certain center point.
– TrebledJ
Nov 17 '18 at 18:55
Seems to me like the gradient is anchored at a certain center point.
– TrebledJ
Nov 17 '18 at 18:55
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53318233%2fhow-to-display-graphics-objects-behind-or-foreground-of-text-inside-qtextedit-in%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Hi! What have you tried? How's your experimenting with QSyntaxHighlighter?
– TrebledJ
Nov 15 '18 at 12:50
Well, i have this example and so far i encountered no problem:doc.qt.io/qt-5/…
– kostas petsis
Nov 15 '18 at 19:48
The image above is how Qtcreator looks and also how i WANT my app to look, so how can i render rectangles or other shapes(can i?) inside QTextEdit?
– kostas petsis
Nov 15 '18 at 19:49
Have you checked out QTextBlock, QTextBlockFormat, QTextFrameFormat?
– TrebledJ
Nov 16 '18 at 1:49
No.I will check them out and see if they fit what i want to do.I'll keep the thread open for someone that has done this or was able to display graphics inside the QTextEdit
– kostas petsis
Nov 16 '18 at 14:37