Reputation: 2022
I want to add some syntax highlighting to text being written in QLineEdit, but it does not support rich text formatting, I can not change QlineEdit to something else, so I should find how to set color of text in this widget.
Is there a way to do this?
Upvotes: 26
Views: 18431
Reputation: 155
I was able to accomplish this by overlaying a QLabel
on top of a QLineEdit
then making the text color of the line edit white. When the textEdited
signal is emitted, use it to update the text of the QLabel
. The QLabel
accepts rich text so you can process the text in the QLineEdit
and replace key words with the HTML needed to display the text in the way you want it. I'm sure you could modify the code to change the text color of the current selection.
class LabelEditPair(QLineEdit):
"""
QLineEdit that changes the color of the word 'blue' to blue and
the changes the font weight of the word 'bold' to bold.
"""
def __init__(self):
super().__init__()
self.label = QLabel("starting out")
self.label.setParent(self)
self.label.move(3, 0)
self.label.setAttribute(Qt.WA_TransparentForMouseEvents)
self.setStyleSheet("QLineEdit{color: white}")
self.textEdited.connect(self.text_edited)
def resizeEvent(self, event):
self.label.setFixedHeight(self.height())
self.label.setFixedWidth(self.width())
super().resizeEvent(event)
def text_edited(self, text):
text = text.replace("blue", "<span style='color: blue'>blue</span>")
text = text.replace("bold", "<span style='font-weight: bold'>bold</span>")
self.label.setText(text)
The previous example doesn't work well in instances where the text overflows from the QLineEdit
widget. Here is a more comprehensive widget that uses the same idea but instead of making a subclass of QLineEdit
the widget is a subclass of QFrame
with a QLineEdit
and two QLabels
, one before the cursor and one after. The widget replaces regex matches with HTML to change the style of those characters.
class LabelEditPair(QFrame):
"""
QLineEdit that changes the color of the word 'blue' to blue and
the changes the font weight of the word 'bold' to bold.
"""
def __init__(self, initial_text: str):
super().__init__()
self.stylized_regex: List[Tuple[str, str]] = []
self.setFixedHeight(22)
self.setObjectName("frame")
self.setStyleSheet("QFrame#frame{background-color: white; border: 1px solid gray}"
"QFrame:hover#frame{border: 1px solid black}"
"QFrame:selected#frame{border: 1px solid blue}")
self.setFrameStyle(QFrame.Box)
self.line_edit = QLineEdit(initial_text)
self.line_edit.setParent(self)
self.line_edit.move(0, 2)
self.line_edit.setStyleSheet("border: 0px solid white; background-color:transparent")
self.line_edit.setFixedWidth(2**16)
self.left_label = QLabel()
self.left_label.setParent(self.line_edit)
self.left_label.move(1, 1)
self.left_label.setAlignment(Qt.AlignRight)
self.left_label.setAttribute(Qt.WA_TransparentForMouseEvents)
self.right_label = QLabel()
self.right_label. setParent(self.line_edit)
self.right_label.move(5, 1)
self.right_label.setAlignment(Qt.AlignLeft)
self.right_label.setAttribute(Qt.WA_TransparentForMouseEvents)
self.right_label.setFixedWidth(2**16)
self.offset = 0
self.line_edit.textEdited.connect(self.text_edited)
self.line_edit.cursorPositionChanged.connect(self.apply_shift)
self.line_edit.selectionChanged.connect(self.set_text_to_update)
self.update_text_needed = True
self.placeholder = ""
self.color = (0, 0, 0)
def text(self):
return self.line_edit.text()
def setReadOnly(self, read_only: bool):
self.line_edit.setReadOnly(read_only)
self.line_edit.setAttribute(Qt.WA_TransparentForMouseEvents, read_only)
self.line_edit.end(False)
def set_placeholder_text(self, text: str):
self.placeholder = text
def set_text_color(self, color: Tuple[int, int, int]):
self.color = color
def set_text_to_update(self):
self.update_text_needed = True
def text_edited(self, text: str):
if len(text) == 0:
self.left_label.setText(self.placeholder)
self.left_label.setStyleSheet("color: gray")
return
self.left_label.setStyleSheet(f"color: rbg{self.color}")
new = self.line_edit.cursorPosition()
left_text = text[:new]
right_text = text[new:]
self.left_label.setText(left_text)
self.right_label.setText(right_text)
for style, regex in self.stylized_regex:
matches = findall(regex, left_text)
for match in matches:
left_text = left_text.replace(match, f"<span style='{style}'>{match}</span>")
self.left_label.setText(left_text)
matches_right = findall(regex, right_text)
for match in matches_right:
right_text = right_text.replace(match, f"<span style='{style}'>{match}</span>")
self.right_label.setText(right_text)
self.update_text_needed = False
def add_style_for_regex(self, style: str, regex: str):
self.stylized_regex.append((style, regex))
def apply_shift(self, old=None, new=None):
text = self.line_edit.text()
rect = self.line_edit.cursorRect()
x_pos = rect.x()
if x_pos + self.offset > self.width() - 8 and new == old + 1:
self.offset = -1*(x_pos - (self.width()-8))
elif new + 1 == old and x_pos + self.offset < self.width() * 1/2:
self.offset += 5
self.offset = min(0, self.offset)
if len(text) == 0:
self.offset = 0
self.line_edit.move(self.offset, 2)
self.left_label.setFixedWidth(x_pos + 4)
self.right_label.move(x_pos + 5, 1)
if self.update_text_needed:
self.text_edited(text=text)
self.update_text_needed = True
self.color_edit = LabelEditPair("")
self.color_edit.add_style_for_regex("color: blue", "(?:HI|HELLO)")
main_layout.addWidget(self.color_edit)
Upvotes: 0
Reputation: 3689
You can change color of texts like this:
QLineEdit *line = new QLineEdit();
line->setText("this is a test");
line->setStyleSheet("foreground-color: blue;");
If it won't work, replace the last line with the following:
line->setStyleSheet("color: blue;");
Upvotes: 1
Reputation: 188
You can change the color with the use of style sheets.
QLineEdit* myLineEdit = new QLineEdit("Whatever");
//for whatever case you want to change the color
if(syntax_needs_to_highlighted)
myLineEdit->setStyleSheet("QLineEdit#myLineEdit{color:blue}");
You may want to consider using QTextBrowser
for this case.
Upvotes: 2
Reputation: 2022
Just found a neat trick for that.
static void setLineEditTextFormat(QLineEdit* lineEdit, const QList<QTextLayout::FormatRange>& formats)
{
if(!lineEdit)
return;
QList<QInputMethodEvent::Attribute> attributes;
foreach(const QTextLayout::FormatRange& fr, formats)
{
QInputMethodEvent::AttributeType type = QInputMethodEvent::TextFormat;
int start = fr.start - lineEdit->cursorPosition();
int length = fr.length;
QVariant value = fr.format;
attributes.append(QInputMethodEvent::Attribute(type, start, length, value));
}
QInputMethodEvent event(QString(), attributes);
QCoreApplication::sendEvent(lineEdit, &event);
}
static void clearLineEditTextFormat(QLineEdit* lineEdit)
{
setLineEditTextFormat(lineEdit, QList<QTextLayout::FormatRange>());
}
// Usage example:
QLineEdit* lineEdit = new QLineEdit;
lineEdit->setText(tr("Task Tracker - Entry"));
QList<QTextLayout::FormatRange> formats;
QTextCharFormat f;
f.setFontWeight(QFont::Bold);
QTextLayout::FormatRange fr_task;
fr_task.start = 0;
fr_task.length = 4;
fr_task.format = f;
f.setFontItalic(true);
f.setBackground(Qt::darkYellow);
f.setForeground(Qt::white);
QTextLayout::FormatRange fr_tracker;
fr_tracker.start = 5;
fr_tracker.length = 7;
fr_tracker.format = f;
formats.append(fr_task);
formats.append(fr_tracker);
setLineEditTextFormat(lineEdit, formats);
Upvotes: 29