I'm trying to make a simple sqlite3 editor with pyqt5, including a feature (among other rules) to change reserved words to green, bold, uppercase with a syntax highlighter. Here's a MWC:
...
self.sql_box = QPlainTextEdit()
self.highlighter = SqlHighlighter(self.sql_box.document())
...
class SqlHighlighter(QSyntaxHighlighter):
def __init__(self, parent):
super(SqlHighlighter, self).__init__(parent)
self.rules = []
...
tx_pattern = r"("
for c in COMMANDS:
tx_pattern += c + "|"
tx_pattern = tx_pattern[0:-1] + ")"
char_format = QTextCharFormat()
char_format.setForeground(Qt.darkGreen)
char_format.setFontWeight(QFont.Bold)
char_format.setFontCapitalization(QFont.AllUppercase)
self.rules.append((QRegExp(tx_pattern, cs=Qt.CaseInsensitive), 0, char_format))
...
def highlightBlock(self, text):
for regexp, idx, chr_fmt in self.rules:
index = regexp.indexIn(text, 0)
while index >= 0:
length = regexp.matchedLength()
self.setFormat(index, length, chr_fmt)
index = regexp.indexIn(text, index + length)
self.setCurrentBlockState(0)
While color and weight works correctly it doesn't change to uppercase and I can't figure out what's wrong. Any help, please?
The problem is caused by a reported bug (QTBUG-90840) which should have been fixed since Qt 5.15.5 and 6.1.1.
Unfortunately, due to the cause of the issue (the way QTextLayout tokenizes its elements), it's extremely difficult if not impossible to work around this with previous versions. While this is probably not an issue for Qt6, it may be for Qt5 if you rely on an existing Qt installation. As far as I know, Qt luckily released an unexpected open-source build for 5.15.6, so you should eventually add that requirement to your program.
Note, though, that changing text case may not be a good idea. Your code didn't show what is the content of COMMANDS
, but in the rare case the user decides to use a variable/argument that matches any of them (but they still wanted their own caps), it would make the visual result unexpected and undesirable.
In general, word match should always consider the context of the command, since words may have different meaning, and while good practice tells to avoid such words (and syntax may even completely prevent them), you should always consider these aspects.
Finally, a suggestion about the building of tx_pattern
:
tx_pattern = r"(" + "|".join(COMMANDS) + ")"
# eventually:
tx_pattern = rf"({'|'.join(COMMANDS)})"