How to dynamically update QSyntaxHighlighter keywords

I am making a text editor GUI that allows dynamic highlighting of a keyword given as input from the user. I am using QSyntaxHighlighter to highlight the keywords found within a QPlainTextEdit box. I am able to find the substrings within the text body along with the indexs of each occcurance. However the formatting is not being applied correctly.

Highlighter class:

class Highlighter(QSyntaxHighlighter):
    def __init__(self, parent):
        super().__init__(parent)

    def highlightBlock(self, text_block, kw):
        highlight_str = kw
        kw_format = QTextCharFormat()
        kw_format.setFontWeight(QFont.Bold)
        kw_format.setForeground(Qt.darkMagenta)
        for match in re.finditer(highlight_str, text_block):
            start, end = match.span()
            self.setFormat(start, end-start, kw_format)

Highlight text function that takes user input and passes to the highlighter:

    def highlight_text(self):
        input, pressed = QInputDialog.getText(self, "Highlight", "Enter keyword", QLineEdit.Normal, "")
        if pressed:
            highlight_str = str(input)
            highlighter = Highlighter(self)
            # rule for highlighting, can be expanded
            highlighter.highlightBlock(self.editor.document().toPlainText(), highlight_str)

I believe this is not working because the editor is not linked to the highlighter? How do I link the highlighter to the texteditor, but enable it to reassess the text body once a new keyword is given by user input?


Solution 1:

From the documentation about highlightBlock() (emphasis mine):

[...] This function is called when necessary by the rich text engine, i.e. on text blocks which have changed.

This means that it's not supposed to be called programmatically, but it is called by the text engine (the QTextDocument) whenever the document is changed.

The highlighter must be then created with the QPlainTextEdit's document() as argument, which automatically becomes enabled for it. You should keep a reference for the highlighter, so that you can update or remove it whenever necessary:

    def highlight_text(self):
        input, pressed = QInputDialog.getText(self, "Highlight", 
            "Enter keyword", QLineEdit.Normal, "")
        if pressed:
            self.highlighter = Highlighter(self.document(), input)

    def resetHighlight(self):
        self.highlighter.setDocument(None)


class Highlighter(QSyntaxHighlighter):
    def __init__(self, document, searchString):
        super().__init__(document)
        self.searchString = searchString

    def highlightBlock(self, text):
        kw_format = QTextCharFormat()
        kw_format.setFontWeight(QFont.Bold)
        kw_format.setForeground(Qt.darkMagenta)
        for match in re.finditer(self.searchString, text):
            start, end = match.span()
            self.setFormat(start, end - start, kw_format)