pythonpython-2.7wxpythonwxwidgetswxtextctrl

After the EVT_KILL_FOCUS event is executed, why does the blinking cursor in TextCtrl disappear?


I have a TextCtrl that has an EVT_KILL_FOCUS event that I use to validate the contents of the field, alerting the user when the value is wrong. After opening the MessageBox, I clear the field and i set the focus to the field that I left validate again. Problem is that the text blinking cursor that should appear inside the field disappears and I do not know why or how to fix it. This behavior causes the user to not know in which field the focus is positioned.

Does anyone have any ideas?

    ...
    self.txtCode = wx.TextCtrl(self, value='')
    self.txtCode.Bind(wx.EVT_KILL_FOCUS, self.__onTxtCodeKillFocus)
    self.txtCode.Bind(wx.EVT_CHAR_HOOK, self.__onTxtCodeTabKey)

def __validateTxtCodeContent(self):
    if self.txtCode.GetValue() == "":
        self.MessageBox(self, "Error Text", _("Warning"))
        return False
    return True

def __onTxtCodeKillFocus(self, event):
    event.Skip()
    if self.__validateTxtCodeContent() == False:
        self.txtCode.SetValue("")
        self.txtCode.SetFocus()

def __onTxtCodeTabKey(self, event):
    key = event.GetKeyCode()
    shift = event.ShiftDown()

    # 9 = TAB, 13 = ENTER
    if key != 9 and key != 13:
        event.Skip()
        return
    elif key == 9:
        if self.__validateTxtCodeContent():
            if shift:
                self.btnSave.SetFocus()
            else:
                self.txtDescription.SetFocus()
        else:
            self.txtCode.SetValue("")
            self.txtCode.SetFocus()
    else:
        return False

My validation is not only for empty field, but for example only empty field can be.

Important: In the EVT_CHAR_HOOK event this behavior does occurs too.

I tried to use this too:

self.txtCode.SetValue("")
self.txtCode.SetFocus()
self.txtCode.SetInsertionPointEnd() 
self.txtCode.Refresh()

But it did not work well.


Solution

  • You can't call SetFocus() from KILL_FOCUS handler. The most direct workaround is to use CallAfter() to call it slightly later, but even though this would "work", it's a very bad idea because you should not prevent the user from leaving the window -- and there is no way to completely prevent it from happening anyhow.

    Just mark the code as being invalid (e.g. change its background), but don't try to retain focus when you're losing it.

    P.S. Calling MessageBox() from a focus event handler may be a bad idea too in many circumstances, it's better to use wxLogWarning() or CallAfter() to delay showing the message box until the next event loop iteration.