Nasty Bug in my wx.EVT_KILL_FOCUS Method

In my app frame I have a panel for entering records that depend on the prior existence of a related record in another table. When the panel is opened, the cursor is initialized in the field that is the foreign key for the parent record. This field is required, so it must be completed before any other data entry can take place (data from the parent record determines particular starting values and choice list contents for the new record.)

So far so good – when the field is entered, it successfully checks for a matching parent record and gets the proper data, if it’s an invalid entry a dialog to that effect is displayed.

If the user tabs out of the field without entering a value, another dialog is displayed reminding them to enter something.

The problem occurs when the user doesn’t tab out, but clicks elsewhere with the mouse. If they click anywhere in the panel, there’s no issue – nothing happens. But if they attempt to grab the title bar of the window, for example, or click the cancel button, the wx.EVT_KILL_FOCUS event is generated, and the warning dialog comes up before they can take the action.

Worse, and fatal even, is when they click in some area outside the application window. The wx.EVT_KILL_FOCUS is generated, but the focus on the field isn’t really lost. It goes into an unstoppable loop, where at that point they can’t even click the ok button on the dialog without generating another event. They’re trapped unless they kill the process or find the terminal window and close it via that.

I’m attaching the problematic code. What I guess I need is a way to intercept mouse events from somewhere – I don’t know if that should be bound to the field or to the frame, and prevent it from catching unwanted events. In the code note that I put a self.Navigate() call at the beginning and an event.Skip() call at the end. Both are attempts to move the cursor out of that field so that it won’t generate the event again. But that was unsuccessful.

Thanks in advance for any insights one can give this addled brain.

troubleshoot.py (446 Bytes)

I don't see it based on the code you posted - maybe a MakingSampleApps - wxPyWiki

Only guess I have is maybe moving all this code out of the EVT_KILL_FOCUS handler and have it in a 'doKillFocus' and have some check that you don't get into the loop.

Werner

···

On 31/01/2013 05:24, llanitedave wrote:

In my app frame I have a panel for entering records that depend on the prior existence of a related record in another table. When the panel is opened, the cursor is initialized in the field that is the foreign key for the parent record. This field is required, so it must be completed before any other data entry can take place (data from the parent record determines particular starting values and choice list contents for the new record.)

So far so good -- when the field is entered, it successfully checks for a matching parent record and gets the proper data, if it's an invalid entry a dialog to that effect is displayed.
If the user tabs out of the field without entering a value, another dialog is displayed reminding them to enter something.

The problem occurs when the user doesn't tab out, but clicks elsewhere with the mouse. If they click anywhere in the panel, there's no issue -- nothing happens. But if they attempt to grab the title bar of the window, for example, or click the cancel button, the wx.EVT_KILL_FOCUS event is generated, and the warning dialog comes up before they can take the action.

Worse, and fatal even, is when they click in some area outside the application window. The wx.EVT_KILL_FOCUS is generated, but the focus on the field isn't really lost. It goes into an unstoppable loop, where at that point they can't even click the ok button on the dialog without generating another event. They're trapped unless they kill the process or find the terminal window and close it via that.

I'm attaching the problematic code. What I guess I need is a way to intercept mouse events from somewhere -- I don't know if that should be bound to the field or to the frame, and prevent it from catching unwanted events. In the code note that I put a self.Navigate() call at the beginning and an event.Skip() call at the end. Both are attempts to move the cursor out of that field so that it won't generate the event again. But that was unsuccessful.

Thanks in advance for any insights one can give this addled brain.

I have often found wx.EVT_KILL_FOCUS is more trouble than it’s worth - even when you do succeed in avoiding your looping problem, you still need to distinguish whether what caused the kill focus should call your validation routines or not i.e. if the user’s attention is going somewhere else inside or even outside the application you presumably don’t want your warning messages to appear. I don’t know how many data entry fields you have that require your key field to be entered first, but would it be possible to use wx.EVT_SET_FOCUS to check whether any of these fields are receiving the focus and, if so, then do your key-field validation?

···

On Thursday, 31 January 2013 04:24:34 UTC, llanitedave wrote:

In my app frame I have a panel for entering records that depend on the prior existence of a related record in another table. When the panel is opened, the cursor is initialized in the field that is the foreign key for the parent record. This field is required, so it must be completed before any other data entry can take place (data from the parent record determines particular starting values and choice list contents for the new record.)

So far so good – when the field is entered, it successfully checks for a matching parent record and gets the proper data, if it’s an invalid entry a dialog to that effect is displayed.

If the user tabs out of the field without entering a value, another dialog is displayed reminding them to enter something.

The problem occurs when the user doesn’t tab out, but clicks elsewhere with the mouse. If they click anywhere in the panel, there’s no issue – nothing happens. But if they attempt to grab the title bar of the window, for example, or click the cancel button, the wx.EVT_KILL_FOCUS event is generated, and the warning dialog comes up before they can take the action.

Worse, and fatal even, is when they click in some area outside the application window. The wx.EVT_KILL_FOCUS is generated, but the focus on the field isn’t really lost. It goes into an unstoppable loop, where at that point they can’t even click the ok button on the dialog without generating another event. They’re trapped unless they kill the process or find the terminal window and close it via that.


Regards,
David Hughes
Forestfield Software

llanitedave wrote:

In my app frame I have a panel for entering records that depend on the
prior existence of a related record in another table. When the panel is
opened, the cursor is initialized in the field that is the foreign key
for the parent record. This field is required, so it must be completed
before any other data entry can take place (data from the parent record
determines particular starting values and choice list contents for the
new record.)

So far so good -- when the field is entered, it successfully checks for
a matching parent record and gets the proper data, if it's an invalid
entry a dialog to that effect is displayed.
If the user tabs out of the field without entering a value, another
dialog is displayed reminding them to enter something.

The problem occurs when the user doesn't tab out, but clicks elsewhere
with the mouse. If they click anywhere in the panel, there's no issue --
nothing happens. But if they attempt to grab the title bar of the
window, for example, or click the cancel button, the wx.EVT_KILL_FOCUS
event is generated, and the warning dialog comes up before they can take
the action.

Worse, and fatal even, is when they click in some area outside the
application window. The wx.EVT_KILL_FOCUS is generated, but the focus on
the field isn't really lost. It goes into an unstoppable loop, where at
that point they can't even click the ok button on the dialog without
generating another event. They're trapped unless they kill the process
or find the terminal window and close it via that.

I'm attaching the problematic code. What I guess I need is a way to
intercept mouse events from somewhere -- I don't know if that should be
bound to the field or to the frame, and prevent it from catching
unwanted events. In the code note that I put a self.Navigate() call at
the beginning and an event.Skip() call at the end. Both are attempts to
move the cursor out of that field so that it won't generate the event
again. But that was unsuccessful.

Thanks in advance for any insights one can give this addled brain.

As you've seen doing things in a kill-focus event that can cause changes in the normal focus flow can be tricky and sometimes worse. (For example, what happens if you need to do a similar check in the field that the focus has moved to, showing the dialog for the first one causes the second to loose focus and want to also show a dialog, and then you've got two modal dialogs fighting each other...)

I've found that it is usually better to leave the kill-focus events alone, or at least make them super simple and not affect the flow in any way, and do whatever I need to do after it has finished. This sounds like a job for wx.CallAfter. For example:

  def onLeaveField1(self, evt):
    wx.CallAfter(self.checkField1)
    evt.Skip()

Then checkField1 will be called when there are no more focus changes about to happen and also the native focus changing behavior will already have been done. (Some platforms have real problems if Skip isn't done in kill-focus handlers.) In checkField1 you can do things like make sure that the focus is still in your frame and not show the dialog if it is somewhere else, etc.

···

--
Robin Dunn
Software Craftsman

Thanks Robin, and David. I see the point you’re getting at, and I’ll be experimenting with the wx.CallAfter approach. Could be a big job, because I have the wx.EVT_KILL_FOCUS binding scattered all through my application. It hasn’t caused me problems elsewhere yet, but that may be due more to a lack of stress testing than actual correct functionality.

I’ve got a week to get the Beta out, so I’ll be hitting this one pretty hard.

Thanks again, I appreciate it.

···

On Friday, February 1, 2013 9:57:52 AM UTC-8, Robin Dunn wrote:

llanitedave wrote:

In my app frame I have a panel for entering records that depend on the

prior existence of a related record in another table. When the panel is

opened, the cursor is initialized in the field that is the foreign key

for the parent record. This field is required, so it must be completed

before any other data entry can take place (data from the parent record

determines particular starting values and choice list contents for the

new record.)

So far so good – when the field is entered, it successfully checks for

a matching parent record and gets the proper data, if it’s an invalid

entry a dialog to that effect is displayed.

If the user tabs out of the field without entering a value, another

dialog is displayed reminding them to enter something.

The problem occurs when the user doesn’t tab out, but clicks elsewhere

with the mouse. If they click anywhere in the panel, there’s no issue –

nothing happens. But if they attempt to grab the title bar of the

window, for example, or click the cancel button, the wx.EVT_KILL_FOCUS

event is generated, and the warning dialog comes up before they can take

the action.

Worse, and fatal even, is when they click in some area outside the

application window. The wx.EVT_KILL_FOCUS is generated, but the focus on

the field isn’t really lost. It goes into an unstoppable loop, where at

that point they can’t even click the ok button on the dialog without

generating another event. They’re trapped unless they kill the process

or find the terminal window and close it via that.

I’m attaching the problematic code. What I guess I need is a way to

intercept mouse events from somewhere – I don’t know if that should be

bound to the field or to the frame, and prevent it from catching

unwanted events. In the code note that I put a self.Navigate() call at

the beginning and an event.Skip() call at the end. Both are attempts to

move the cursor out of that field so that it won’t generate the event

again. But that was unsuccessful.

Thanks in advance for any insights one can give this addled brain.

As you’ve seen doing things in a kill-focus event that can cause changes
in the normal focus flow can be tricky and sometimes worse. (For
example, what happens if you need to do a similar check in the field
that the focus has moved to, showing the dialog for the first one causes
the second to loose focus and want to also show a dialog, and then
you’ve got two modal dialogs fighting each other…)

I’ve found that it is usually better to leave the kill-focus events
alone, or at least make them super simple and not affect the flow in any
way, and do whatever I need to do after it has finished. This sounds
like a job for wx.CallAfter. For example:

    def onLeaveField1(self, evt):

            wx.CallAfter(self.checkField1)

            evt.Skip()

Then checkField1 will be called when there are no more focus changes
about to happen and also the native focus changing behavior will already
have been done. (Some platforms have real problems if Skip isn’t done
in kill-focus handlers.) In checkField1 you can do things like make
sure that the focus is still in your frame and not show the dialog if it
is somewhere else, etc.


Robin Dunn

Software Craftsman

http://wxPython.org

Hello Robin, could you help me?

I’m with this problem focus to validate content controls, but I can not make it work. Even separating the validation routine.

def onKillFocus( self, event ):

cText = event.EventObject.GetValue()

if event.EventObject.GetValue() != “” and event.EventObject.GetValue() <> ‘ADM’:

event.EventObject.SetFocus()

wx.CallAfter( event.EventObject.SetFocus() )

self.oTxtobs.SetValue(“Invalid…”)

else:

self.oTxtobs.SetValue(“”)

event.Skip()

···

Em sexta-feira, 1 de fevereiro de 2013 14h26min27s UTC-2, David Hughes escreveu:

On Thursday, 31 January 2013 04:24:34 UTC, llanitedave wrote:

In my app frame I have a panel for entering records that depend on the prior existence of a related record in another table. When the panel is opened, the cursor is initialized in the field that is the foreign key for the parent record. This field is required, so it must be completed before any other data entry can take place (data from the parent record determines particular starting values and choice list contents for the new record.)

So far so good – when the field is entered, it successfully checks for a matching parent record and gets the proper data, if it’s an invalid entry a dialog to that effect is displayed.

If the user tabs out of the field without entering a value, another dialog is displayed reminding them to enter something.

The problem occurs when the user doesn’t tab out, but clicks elsewhere with the mouse. If they click anywhere in the panel, there’s no issue – nothing happens. But if they attempt to grab the title bar of the window, for example, or click the cancel button, the wx.EVT_KILL_FOCUS event is generated, and the warning dialog comes up before they can take the action.

Worse, and fatal even, is when they click in some area outside the application window. The wx.EVT_KILL_FOCUS is generated, but the focus on the field isn’t really lost. It goes into an unstoppable loop, where at that point they can’t even click the ok button on the dialog without generating another event. They’re trapped unless they kill the process or find the terminal window and close it via that.

I have often found wx.EVT_KILL_FOCUS is more trouble than it’s worth - even when you do succeed in avoiding your looping problem, you still need to distinguish whether what caused the kill focus should call your validation routines or not i.e. if the user’s attention is going somewhere else inside or even outside the application you presumably don’t want your warning messages to appear. I don’t know how many data entry fields you have that require your key field to be entered first, but would it be possible to use wx.EVT_SET_FOCUS to check whether any of these fields are receiving the focus and, if so, then do your key-field validation?

Regards,
David Hughes
Forestfield Software

EdsonSil Sil wrote:

Hello Robin, could you help me?
I'm with this problem focus to validate content controls, but I can not
make it work. Even separating the validation routine.

def onKillFocus( self, event ):

cText = event.EventObject.GetValue()
if event.EventObject.GetValue() != "" and event.EventObject.GetValue()
<> 'ADM':
event.EventObject.SetFocus()
# wx.CallAfter( event.EventObject.SetFocus() )
self.oTxtobs.SetValue("Invalid...")

else:
self.oTxtobs.SetValue("")
event.Skip()

Please make a runnable, small as possible, sample application that
demonstrates the problem, and let us know the platform and wx version.
http://wiki.wxpython.org/MakingSampleApps

···

--
Robin Dunn
Software Craftsman