Vasaka
Vasaka

Reputation: 2022

How can I change color of part of the text in QLineEdit?

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

Answers (4)

SixenseMan
SixenseMan

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)

enter image description here

EDIT

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
example usage
self.color_edit = LabelEditPair("")
self.color_edit.add_style_for_regex("color: blue", "(?:HI|HELLO)")
main_layout.addWidget(self.color_edit)

enter image description here

Upvotes: 0

Reza Ebrahimi
Reza Ebrahimi

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

I L
I L

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

Vasaka
Vasaka

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

Related Questions