WordUnderCursor 无法正常工作(当不在任何单词之上时检测到单词)

WordUnderCursor not working as it should (word being detected when not on top of any words)

为什么检测到一个词在光标下?图像中的红色箭头从光标实际所在的位置开始。不管我把它放在哪里,只要它在window里面,程序就认为一个词被选中了。如果光标在文本下方,则默认为最后一个。如果光标在上面,则默认为第一个。

图片:

我的所有代码:

from PyQt5.QtWidgets import QTextEdit, QMainWindow, QApplication
from PyQt5.QtGui import QMouseEvent, QTextCursor


class Editor(QTextEdit):
    def __init__(self):
        super(Editor, self).__init__()
        # make sure this widget is tracking the mouse position at all times
        self.setMouseTracking(True)

    def mouseMoveEvent(self, mouse_event: QMouseEvent) -> None:
        if self.underMouse():
            # create a QTextCursor at that position and select text
            text_cursor = self.cursorForPosition(mouse_event.pos())
            text_cursor.select(QTextCursor.WordUnderCursor)

            word_under_cursor = text_cursor.selectedText()
            print(word_under_cursor)

            # replace substring with placeholder so that repeat occurrences aren't highlighted as well
            selected_word_placeholder = self.replace_selected_text_with_placeholder(text_cursor)

            word_under_cursor = '<span style="background-color: #FFFF00;font-weight:bold;">' + word_under_cursor + '</span>'

            # replace the sentence with the new formatting
            self.setHtml(self.toPlainText().replace(selected_word_placeholder, word_under_cursor))

    def replace_in_html(self, old_string, new_string):
        old_html = self.toHtml()
        new_html = old_html.replace(old_string, new_string)
        self.setHtml(new_html)

    # use placeholder so that repeat occurrences of the word are not highlighted
    def replace_selected_text_with_placeholder(self, text_cursor):
        # remove the selected word to be replaced by the placeholder
        text_cursor.removeSelectedText()

        # create a placeholder with as many characters as the original word
        word_placeholder = ''
        for char in range(10):
            word_placeholder += '@'
        text_cursor.insertText(word_placeholder)

        return word_placeholder


def set_up(main_window):
    title_editor = Editor()
    title_editor.setText('Venda quente original xiaomi redmi airdots 2 tws fones de ouvido sem fio bluetooth fones controle ai gaming headset come')

    main_window.setCentralWidget(title_editor)
    main_window.show()


application = QApplication([])
window = QMainWindow()

set_up(window)

application.exec()

问题是由于 select() 总是试图 select 某些东西,即使鼠标实际上没有 文本块上, 它会得到最接近的单词。

解决方法是检查鼠标是否实际上在文本块的矩形内:

if self.underMouse():
    pos = mouse_event.pos()
    # create a QTextCursor at that position and select text
    text_cursor = self.cursorForPosition(pos)
    text_cursor.select(QTextCursor.WordUnderCursor)

    start = text_cursor.selectionStart()
    end = text_cursor.selectionEnd()
    length = end - start

    block = text_cursor.block()
    blockRect = self.document().documentLayout().blockBoundingRect(block)
    # translate by the offset caused by the scroll bar
    blockRect.translate(0, -self.verticalScrollBar().value())
    if not pos in blockRect:
        # clear the selection since the mouse is not over the block
        text_cursor.setPosition(text_cursor.position())
    elif length:
        # ensure that the mouse is actually over a word
        startFromBlock = start - block.position()
        textLine = block.layout().lineForTextPosition(startFromBlock)
        endFromBlock = startFromBlock + length
        x, _ = textLine.cursorToX(endFromBlock)
        if pos.x() > blockRect.x() + x:
            # mouse cursor is not over a word, clear the selection
            text_cursor.setPosition(text_cursor.position())

请考虑,正如您对上一个问题的建议,使用 setHtml 突出显示文本 不是 一个好的选择,因为它总是会重置编辑内容;这不仅是性能问题,也是可用性问题(甚至忽略滚动条问题):setHtml 总是重置撤消堆栈,因此用户不能再使用撤消操作。