I'm looking for a way to dynamically color the remaining text of a QTextEdit if the length of the text exceeds n characters - that is I want all characters beyond the first n to be colored differently whenever the text changes.
I tried to use the textChanged-Signal, but unfortunately whenever I do something like this:
cursor = self.textCursor()
format = QTextCharFormat()
format.setForeground(QColor('#303030'))
cursor.setPosition(n)
cursor.movePosition(QTextCursor.End)
cursor.mergeCharFormat(format)
to change the color of the remaining text, it goes into an infinite loop because changing the format also seems to trigger the textChanged-Signal.
Another thing I tried was using the QSyntaxHighlighter, but that way I only get access to the current block of text and not the whole text.
I think I have to instead subclass QTextEdit, but I have no idea what to change in order to make it work.
You can temporary prevent signals from being emitted while reformatting your text by using blockSignals()
at the beginning and end of your slot.
Furthermore, merely merging the character format isn't enough to update the format of the text. You also need to set the text cursor of of the text edit to the modified text cursor. At the end of the slot you would then also need to reset the text cursor to the original text cursor to make sure that the current position and selection is maintained, e.g.
from PyQt5 import QtWidgets, QtGui, QtCore
class TextEdit(QtWidgets.QTextEdit):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setAcceptRichText(True)
self.textChanged.connect(self.text_has_changed)
# maximum number of characters is set to 10
self.max_count = 10
def text_has_changed(self):
# block signals, and save original text cursor
blocked = self.blockSignals(True)
old_cursor = self.textCursor()
# create new cursor for setting char format of part of text before the self.max_count'th character
cursor = QtGui.QTextCursor(self.document())
cursor.setPosition(self.max_count, mode=QtGui.QTextCursor.KeepAnchor)
format = QtGui.QTextCharFormat()
format.setForeground(QtGui.QColor('#000000'))
cursor.mergeCharFormat(format)
# change of text format doesn't take effect until text cursor of self is set to updated text cursor.
self.setTextCursor(cursor)
# if document contains more than self.max_count: reformat everything that comes after the self.max_count'th character.
if self.document().characterCount() > self.max_count:
cursor.setPosition(self.max_count)
cursor.movePosition(QtGui.QTextCursor.End, mode=QtGui.QTextCursor.KeepAnchor)
format.setForeground(QtGui.QColor('#a0a0a0'))
cursor.mergeCharFormat(format)
self.setTextCursor(cursor)
# reset text cursor to original cursor and unblock signals.
self.setTextCursor(old_cursor)
self.blockSignals(blocked)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
w = TextEdit()
w.show()
app.exec()