flewis
flewis

Reputation: 41

Add syntax highlighting to parts of the text being entered in a QLineEdit

Using PySide/PyQT, I need to add some syntax highlighting to text being entered in a QLineEdit. I need to highlight specific key words.

I saw the post below with an example of how to do this in C++ but I'm trying to do it in Python. (Tried translating from C++ to Python but couldn't get it...) Anyone have a suggestion for how to do this in Python? Thanks.

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

Further info:

Currently, I'm changing the color of the entire QLineEdit as follows:

for dupLineEd in duplicates[1]:
    dupLineEd.setStyleSheet("QLineEdit{color:Khaki}")

This is not ideal. What I need to do is identify whether or not certain words within the QLineEdit meet a condition (in this case, whether they are duplicates of a word in another QLineEdit) and if so, then highlight only the word within the QLineEdit in a color, rather than the entire QLineEdit. This highlighting needs to be done live as the user is typing, so that for example as they complete a word, if the word meets the condition, the word turns yellow. The rest of the text in the QLineEdit does not change color.

Thanks in advance to anyone who may have any suggestions!

Upvotes: 2

Views: 1397

Answers (1)

flewis
flewis

Reputation: 41

The best workaround I could find so far was to make a new class of QTextEdit that behaves (partly) like a QLineEdit.

For now I'm borrowing a Highligter class that works with QTextEdit, written by igor-bogomolov, posted on github here: https://github.com/pyside/Examples/blob/master/examples/richtext/syntaxhighlighter/syntaxhighlighter.py

My resulting functional code is as follows. (You'll need to define main_window depending on your environment.)

class qTextEditTestUI(QtGui.QDialog):

    def __init__(self, parent=QtGui.QWidget):
        # Inherit __init__
        super(qTextEditTestUI, self).__init__(parent)


        # Set object name and window title
        self.setObjectName('qTextEditTestWindow')
        self.setWindowTitle('qTextEdit Test Window')

        # Window type (Qt.tool not Qt.Window)
        self.setWindowFlags(QtCore.Qt.Tool)


        # ATTRS

        self.highlighter = Highlighter()

        # CREATE WIDGETS

        variableFormat = QtGui.QTextCharFormat()
        #variableFormat.setFontWeight(QtGui.QFont.Bold)
        variableFormat.setForeground(QtGui.QColor('Khaki'))
        self.highlighter.addMapping('hello', variableFormat)


        # Make a TextEdit
        self.QTextEd = snglLnQTextEdit()

        # Add the TextEdit's document to the highlighter
        self.highlighter.addToDocument(self.QTextEd.document())


        # SET MASTER LAYOUT
        masterLayout = QtGui.QVBoxLayout()
        self.setLayout(masterLayout)

        # MASTER LAYOUT
        masterLayout.addWidget(self.QTextEd)
        masterLayout.addStretch()


## QLineEdit-Like QTextEdit
class snglLnQTextEdit(QtGui.QTextEdit):
    def __init__(self, parent=None):
        QtGui.QTextEdit.__init__(self, parent)

        QTextEdFontMetrics =  QtGui.QFontMetrics(self.font())
        self.QTextEdRowHeight = QTextEdFontMetrics.lineSpacing()
        self.setFixedHeight(2 * self.QTextEdRowHeight)
        self.setLineWrapMode(QtGui.QTextEdit.NoWrap)
        self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)

        # CONNECT WIDGET SIGNAL
        self.textChanged.connect(self.validateCharacters)

    def validateCharacters(self):
        badChars = ['\n']
        cursor = self.textCursor()
        curPos = cursor.position()
        for badChar in badChars:
            origText = self.toPlainText()
            for char in origText:
                if char in badChars:
                    cleanText = origText.replace(char, '')
                    self.blockSignals(True)
                    self.setText(cleanText)
                    self.blockSignals(False)
                    cursor.setPosition(curPos-1)
        self.setTextCursor(cursor)

## Highligher Class written by igor-bogomolov
class Highlighter(QtCore.QObject):
    def __init__(self, parent=None):
        QtCore.QObject.__init__(self, parent)

        self.mappings = {}

    def addToDocument(self, doc):
        self.connect(doc, QtCore.SIGNAL('contentsChange(int, int, int)'), self.highlight)

    def addMapping(self, pattern, format):
        self.mappings[pattern] = format

    def highlight(self, position, removed, added):
        doc = self.sender()

        block = doc.findBlock(position)
        if not block.isValid():
            return

        if added > removed:
            endBlock = doc.findBlock(position + added)
        else:
            endBlock = block

        while block.isValid() and not (endBlock < block):
            self.highlightBlock(block)
            block = block.next()

    def highlightBlock(self, block):
        layout = block.layout()
        text = block.text()

        overrides = []

        for pattern in self.mappings:
            for m in re.finditer(pattern,text):
                range = QtGui.QTextLayout.FormatRange()
                s,e = m.span()
                range.start = s
                range.length = e-s
                range.format = self.mappings[pattern]
                overrides.append(range)

        layout.setAdditionalFormats(overrides)
        block.document().markContentsDirty(block.position(), block.length())


## Test Usage:

panl = qTextEditTestUI(parent=main_window())
panl.show()

This will get the job done but I'm not entirely happy with it; it feels clunky. I'm still hoping to work out how to do this kind of highlighting with a QLineEdit.

Upvotes: 2

Related Questions