A while ago I wrote an application which allows the user to create and edit journal entries. It uses a RichTextCtrl and has facilities for highlighting hash-tags, spelling mistakes, and search text, using a number of styles. The highlighting is updated dynamically as the user edits the entry.
Originally I used handlers for EVT_RICHTEXT_CHARACTER
, EVT_RICHTEXT_CONTENT_INSERTED
and EVT_RICHTEXT_CONTENT_DELETED
to update the highlighting. The problem was that updating the highlighting for each event was taking too long when keyboard autorepeat was triggered. Typically this would happen when holding down the backspace key to delete a chunk of text. This would cause the events to be buffered so that when the key was released, the events would continue to be processed, usually resulting in too many characters being deleted.
Recently I replaced those event handlers with a handler for EVT_KEY_UP
so that the highlighting would only be triggered when a key was released. This improved performance so that autorepeat was much less of a problem.
However, I noticed a strange behaviour in long lines that are soft wrapped by the RichTextCtrl. For example, if you position the caret and the end of a wrapped line and then press the right arrow key, the caret momentarily moves to the start of the next line, but then immediately jumps back to the end of the previous line. The reverse doesn’t apply - if you click to put the caret at the start of the second wrapped line and then press the left arrow key, the caret simply moves to the end of the first wrapped line. Another case where it does happen is when the caret is at the start of the first line and you press the down arrow key.
By a process of elimination I discovered that the problem occurs when the EVT_KEY_UP
event handler causes the RichTextCtrl’s SetStyle()
method to be called.
Below is a minimal application which reproduces the problem on wxPython 4.2.2 gtk3 (phoenix) wxWidgets 3.2.6 + Python 3.12.3 + Linux Mint 22.
import wx
import wx.lib.colourdb
import wx.richtext as rt
COLOURS = wx.lib.colourdb.getColourList()[:20]
TEXT = ', '.join(COLOURS).lower()
class MyFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent)
self.SetSize((300, 200))
self.SetTitle("Test Navigation Keys")
self.main_panel = wx.Panel(self, wx.ID_ANY)
main_sizer = wx.BoxSizer(wx.VERTICAL)
self.rtc = rt.RichTextCtrl(self.main_panel, wx.ID_ANY)
main_sizer.Add(self.rtc, 1, wx.EXPAND, 0)
self.main_panel.SetSizer(main_sizer)
self.Layout()
self.rtc.SetValue(TEXT)
self.rtc.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
attr = wx.TextAttr(wx.BLUE)
self.blue_style = rt.RichTextAttr(attr)
self.updateHighlighting()
def OnKeyUp(self, event):
self.updateHighlighting()
event.Skip()
def updateHighlighting(self):
self.rtc.SetStyle(0, 4, self.blue_style)
if __name__ == "__main__":
app = wx.App()
frame = MyFrame(None)
frame.Show()
app.MainLoop()
Fortunately I found a workaround when I realised that the highlighting doesn’t need to be updated when the caret is simply moved by the navigation keys (including Left-Arrow, Right-Arrow, Up-Arrow, Down-Arrow, Home, End, Page-Up and Page-Down).
Here is a modified example which uses the workaround: word_wrap_and_nav_keys_3.py (1.4 KB)
Does anybody have any ideas why calling SetStyle()
in an EVT_KEY_UP
handler should cause this behaviour? Does it also happen on Windows?