Search code examples
pythonwxwidgetswxpythonwxtextctrl

Why does the caret disappear from a TextCtrl widget after a focus event?


I'm writing a calculator application on MS Windows 8.1 using wxPython-Phoenix in which I'd like the calculation to be performed as soon as the user enters a value into one of the parameter fields. To achieve that I'm using wx.EVT_KILL_FOCUS generated by any parameter field to generate a command event which triggers the calculation method. Everything works fine but the appearance of the caret in the parameter fields (implemented by TextCtrl widgets).

After setting the focus either by the Tab key or the mouse on a certain field and moving it (again by the Tab key or the mouse) to a different field - the caret is gone from the TextCtrl widget never to return! when you call the GetCaret() method on that TextCtrl widget the return value is None.

See attached example. The widget still accepts input and displays it but without a caret.

How can I restore the caret in the right position? or not lose it in the first place?

I've tried setting a new caret in the TextCtrl but it does not follow the text input. Since the application is ment to be intensely interactive, I want the triggering event to be the lost focus and not a button (to minimize the number of clicks)

import wx

class MyFrame(wx.Frame):

    def __init__(self, *args, **kwargs):
        super(MyFrame, self).__init__(*args, **kwargs)

        self.InitUI()

    def InitUI(self):
        # setting up the Input panel
        panel = wx.Panel(self)
        self.lbl1 = wx.StaticText(panel,wx.ID_ANY,'Some Text:')
        self.txt1 = wx.TextCtrl(panel,wx.ID_ANY,style=wx.TE_RIGHT)
        self.lbl2 = wx.StaticText(panel,wx.ID_ANY,'Some Other Text:')
        self.txt2 = wx.TextCtrl(panel,wx.ID_ANY,style=wx.TE_RIGHT)
        infgsz = wx.FlexGridSizer(2,2,15,15)
        infgsz.AddMany([(self.lbl1,0,wx.ALIGN_LEFT),\ 
                     (self.txt1,0,wx.ALIGN_LEFT),\
                     (self.lbl2,0,wx.ALIGN_LEFT),\ 
                     (self.txt2,0,wx.ALIGN_LEFT)])
        self.txt1.Bind(wx.EVT_KILL_FOCUS,self.OnInput)
        self.txt2.Bind(wx.EVT_KILL_FOCUS,self.OnInput)
        box = wx.BoxSizer(wx.HORIZONTAL)
        box.Add(infgsz, flag= wx.EXPAND | wx.ALL, border=15)
        panel.SetSizer(box)
        self.SetSize((280, 140))
        self.SetTitle('TextCtrl Demo')
        self.Centre()

    def OnInput(self, e):
        if e.GetId() == self.txt1.GetId():
            self.lbl2.SetForegroundColour(wx.ColourDatabase().Find('RED'))
            self.lbl1.SetForegroundColour(wx.ColourDatabase().Find('BLACK'))
        else:
            self.lbl1.SetForegroundColour(wx.ColourDatabase().Find('BLUE'))
            self.lbl2.SetForegroundColour(wx.ColourDatabase().Find('BLACK'))
        self.Refresh()

def main():
    app = wx.App()
    frame = MyFrame(None)
    frame.Show()
    app.MainLoop()

if __name__ == '__main__':
    main()

The above application displays 2 lines of text. In each line there is a label (wx.StaticText widget) on the left and a TextCtrl widget on the right. when you enter text into the TextCtrl widget of either line and move the focus to the other TextCtrl widget the corresponding label foreground color (the text color) changes to RED or BLUE and the other label changes to BLACK. However, without any seen reason, the caret disappears from the top TextCtrl widget and never returns to it! (On MS Windows 8.1 at least).


Solution

  • The documentation explicitly tells you

    The focus event handlers should almost invariably call wxEvent::Skip() on their event argument to allow the default handling to take place.

    and goes on to explain that not doing it may result in various problems -- such as the one you're observing.

    Add e.Skip() to your event handler to fix it.