Почему после выполнения события EVT_KILL_FOCUS исчезает мигающий курсор в TextCtrl?

У меня есть TextCtrl с событием EVT_KILL_FOCUS, которое я использую для проверки содержимого поля, предупреждая пользователя, когда значение неверно. После открытия MessageBox я очищаю поле и устанавливаю фокус на поле, которое я снова оставил для проверки. Проблема в том, что текстовый мигающий курсор, который должен появиться внутри поля, исчезает, и я не знаю, почему и как это исправить. Такое поведение заставляет пользователя не знать, в каком поле находится фокус.

У кого-нибудь есть идеи?

    ...
    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

Моя проверка не только для пустого поля, но, например, может быть только пустое поле.

Важно: В событии EVT_CHAR_HOOK такое поведение тоже происходит.

Я тоже пытался использовать это:

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

Но это не сработало.


person Community    schedule 24.01.2018    source источник


Ответы (2)


Вы не можете вызвать SetFocus() из обработчика KILL_FOCUS. Самый прямой обходной путь — использовать CallAfter(), чтобы вызвать его немного позже, но даже если это «сработает», это очень плохая идея, потому что вы не должны препятствовать выходу пользователя из окна — и нет никакого способа полностью предотвратить это. от происходящего в любом случае.

Просто отметьте код как недействительный (например, измените его фон), но не пытайтесь сохранить фокус, когда вы его теряете.

P.S. Вызов MessageBox() из обработчика событий фокуса также может быть плохой идеей во многих случаях, лучше использовать wxLogWarning() или CallAfter(), чтобы отложить отображение окна сообщения до следующей итерации цикла обработки событий.

person VZ.    schedule 24.01.2018
comment
Я согласен, в подобных обстоятельствах я просто меняю цвет фона и выдаю предупреждение wx.MessageBox() (не уверен, почему вы думаете, что это плохая идея, по моему опыту, это работает безупречно). - person Rolf of Saxony; 25.01.2018

В дополнение к тому, что Вадим уже сказал:

  • Вы можете выполнить проверку, нажав кнопку «ОК» или когда диалоговое окно/панель вот-вот закроется.

  • Вы можете использовать wxValidator для проверки ввода пользователя в зависимости от вашей задачи.

person Igor    schedule 24.01.2018