在 n 个字符后更改 QTextEdit 中的文本颜色

Change text color in QTextEdit after n characters

我正在寻找一种方法,如果文本的长度超过 n 个字符,则为 QTextEdit 的剩余文本动态着色 - 也就是说,我想要第一个字符以外的所有字符n 每当文本更改时颜色不同。

我尝试使用 textChanged-Signal,但不幸的是每当我这样做时:

cursor = self.textCursor()
format = QTextCharFormat()
format.setForeground(QColor('#303030'))

cursor.setPosition(n)
cursor.movePosition(QTextCursor.End)
cursor.mergeCharFormat(format)

要更改剩余文本的颜色,它会进入无限循环,因为更改格式似乎也会触发 textChanged-Signal。

我尝试的另一件事是使用 QSyntaxHighlighter,但那样我只能访问当前的文本块,而不是整个文本。

我想我必须改为子类化 QTextEdit,但我不知道要更改什么才能让它工作。

您可以通过在插槽的开头和结尾使用 blockSignals() 来临时阻止在重新格式化文本时发出信号。

此外,仅合并字符格式不足以更新文本格式。您还需要将文本编辑的文本光标设置为修改后的文本光标。在插槽的末尾,您还需要将文本光标重置为原始文本光标,以确保保持当前位置和选择,例如

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()