From what I remember in the bug tracker, the problem is that the widget's tooltip lookup doesn't get updated when the highlighter runs.
You can reimplement the tooltip lookup yourself like this (chopped down from some of my own code which uses QPlainTextEdit but I've confirmed to function the same if you %s/QPlainTextEdit/QTextEdit/g
):
from PyQt5.QtCore import Qt, QEvent
from PyQt5.QtGui import QSyntaxHighlighter, QTextCharFormat, QTextCursor
from PyQt5.QtWidgets import QApplication, QPlainTextEdit, QToolTip
from nlprule import Tokenizer, Rules
class TooltipPlainTextEdit(QPlainTextEdit):
def __init__(self, *args):
QPlainTextEdit.__init__(self, *args)
self.setMouseTracking(True)
self.highlighter = NlpRuleHighlighter(self.document())
def event(self, event) -> bool:
"""Reimplement tooltip lookup to get nlprule messages working"""
if event.type() == QEvent.ToolTip:
pos = event.pos()
pos.setX(pos.x() - self.viewportMargins().left())
pos.setY(pos.y() - self.viewportMargins().top())
cursor = self.cursorForPosition(pos)
# QTextCursor doesn't have a quicker way to get
# highlighter-applied formats
for fmt in cursor.block().layout().formats():
if (fmt.start <= cursor.position() and
fmt.start + fmt.length >= cursor.position()):
cursor.setPosition(fmt.start)
cursor.setPosition(fmt.start + fmt.length,
QTextCursor.KeepAnchor)
QToolTip.showText(event.globalPos(), fmt.format.toolTip(),
self, self.cursorRect(cursor))
return True
return super(TooltipPlainTextEdit, self).event(event)
class NlpRuleHighlighter(QSyntaxHighlighter):
grammar_format = QTextCharFormat()
grammar_format.setUnderlineColor(Qt.blue)
grammar_format.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline)
def __init__(self, *args):
QSyntaxHighlighter.__init__(self, *args)
self.nlprule_tokenizer = Tokenizer.load("en")
self.nlprule_rules = Rules.load("en", self.nlprule_tokenizer)
def highlightBlock(self, text: str):
for sugg in self.nlprule_rules.suggest(text):
self.grammar_format.setToolTip(sugg.message)
self.setFormat(sugg.start, sugg.end - sugg.start,
self.grammar_format)
if __name__ == '__main__': # pragma: nocover
import sys
app = QApplication(sys.argv)
edit = TooltipPlainTextEdit()
edit.show()
sys.exit(app.exec_())
In my preliminary "don't do more refactoring than I need to" testing, I found that this does preserve and assign the distinct messages like "Consider using the plural form here" and "Did you mean to have?" correctly, so I can only assume that QSyntaxHighlighter::setFormat
makes a copy of self.grammar_format
's state rather than taking a reference.
eyllanesc implemented something similar first, but he didn't actually do the lookup of what tooltip to display (thanks to user_none on QtCenter for pointing me in the right direction) and I didn't like how he was doing wordwise-select rather than working off the extents of the highlight. (Funny enough, in doing so, I answered the question in user7179690's comment here.)
I also did the margin correction that namezero pointed out the need for.