Key event propagation changed in 2.8?

Hello,

I have another minor problem with transition from 2.6 to 2.8. I would
like to catch key events on a dialog with simple controls, such as
TextCtrls and Buttons. In wx 2.6, the key events are propagated from
the controls (which have the keyboard focus) to the parent window. So
if the event was not processed by the control itself, it was
automatically passed to the key handler of the containing Dialog.
This doesn't happen in wx 2.8. Is this an intended change? Am I
missing something? The code below demonstrates the problem. If run
with the argument "2.6" (in wx 2.6), the events are printed to the
terminal, with "2.8", nothing gets printed. This happens on Linux
GTK.

Thanks for any hints.

Best regards, Tomas

···

--

import sys, os

if len(sys.argv) == 2:
    import wxversion
    wxversion.select(sys.argv[1])
import wx

def on_key_down(event):
    print "***", event

if __name__ == '__main__':
    appl = wx.PySimpleApp()
    dialog = wx.Dialog(None, style=wx.DIALOG_MODAL|
                       wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER,
title="Dialog")
    sizer = wx.BoxSizer(wx.HORIZONTAL)
    sizer.Add(wx.TextCtrl(dialog, -1), 0, wx.ALL)
    sizer.Add(wx.Button(dialog, -1, "Ok"), 0, wx.ALIGN_CENTER|wx.ALL)
    dialog.SetSizer(sizer)
    sizer.Fit(dialog)
    dialog.Bind(wx.EVT_KEY_DOWN, on_key_down)
    dialog.Show(True)
    appl.MainLoop()

Isn’t “event.Skip()” needed here, otherwise you tell it that the
event is handled. Strange so, as this should work the same way in
2.6 and 2.8.

Skip(self, skip=True)

  This method can be used inside an event handler to control

whether
further event handlers bound to this event will be called after
the
current one returns. Without Skip() (or equivalently if
Skip(False) is
used), the event will not be processed any more. If Skip(True) is
called, the event processing system continues searching for a
further
handler function for this event, even though it has been processed
already in the current handler.

Parameters:
**skip**
(type=bool)
Werner

···

On 10/08/2010 15:45, TC wrote:


Hello,
I have another minor problem with transition from 2.6 to 2.8. I would
like to catch key events on a dialog with simple controls, such as
TextCtrls and Buttons. In wx 2.6, the key events are propagated from
the controls (which have the keyboard focus) to the parent window. So
if the event was not processed by the control itself, it was
automatically passed to the key handler of the containing Dialog.
This doesn't happen in wx 2.8. Is this an intended change? Am I
missing something? The code below demonstrates the problem. If run
with the argument "2.6" (in wx 2.6), the events are printed to the
terminal, with "2.8", nothing gets printed. This happens on Linux
GTK.
Thanks for any hints.
Best regards, Tomas
--
import sys, os
if len(sys.argv) == 2:
import wxversion
wxversion.select(sys.argv[1])
import wx
def on_key_down(event):
print "***", event

It is not needed here to demonstrate my problem, so I didn't include
it. The problem is, that the handler is not called at all in wx 2.8.

···

On 10 srp, 16:00, werner <wbru...@free.fr> wrote:

Isn't "event.Skip()" needed here, otherwise you tell it that the event
is handled. Strange so, as this should work the same way in 2.6 and 2.8.

If that works with 2.6 then it is a bug. Key events are not derived from wx.ComamndEvent and so they are not supposed to propagate to the parent windows. This has been true at least since version 2.0. Are you sure that the dialog does not have the focus?

BTW, when I run your sample with 2.6 the on_key_down handler is not called.

···

On 8/10/10 6:45 AM, TC wrote:

Hello,

I have another minor problem with transition from 2.6 to 2.8. I would
like to catch key events on a dialog with simple controls, such as
TextCtrls and Buttons. In wx 2.6, the key events are propagated from
the controls (which have the keyboard focus) to the parent window. So
if the event was not processed by the control itself, it was
automatically passed to the key handler of the containing Dialog.
This doesn't happen in wx 2.8. Is this an intended change? Am I
missing something? The code below demonstrates the problem. If run
with the argument "2.6" (in wx 2.6), the events are printed to the
terminal, with "2.8", nothing gets printed. This happens on Linux
GTK.

--
Robin Dunn
Software Craftsman

If that works with 2.6 then it is a bug. Key events are not derived
from wx.ComamndEvent and so they are not supposed to propagate to the
parent windows. This has been true at least since version 2.0.

Ok, how would I then handle for example Escape key on a dialog (I
actually need more than just Escape)? Do I need to handle
EVT_KEY_DOWN on every single widget inside the dialog?

Are you sure that the dialog does not have the focus?

Yes, I am. For example I can type letters inside the TextCtrl and
these key presses don't propagate. They write text in the box. But
when I press Escape, it is not processed by the control and propagates
to on_key_down.

BTW, when I run your sample with 2.6 the on_key_down handler is not called.

Strange. We were relying on this behavior in our application since wx
2.2. And it worked (and still works) fine in all instalations
(currently on 2.6).

Thank you for your comments.

Tomas Cerha

···

On 10 srp, 20:03, Robin Dunn <ro...@alldunn.com> wrote:

I actually have this problem as well right now. I've set global
shortcut keys in my `wx.Frame`, and they're not getting invoked when
the focus is on a child widget.

Does anyone have an answer?

Ram.

···

On Aug 10, 9:40 pm, TC <t.ce...@gmail.com> wrote:

On 10 srp, 20:03, Robin Dunn <ro...@alldunn.com> wrote:

> If that works with 2.6 then it is a bug. Key events are not derived
> from wx.ComamndEvent and so they are not supposed to propagate to the
> parent windows. This has been true at least since version 2.0.

Ok, how would I then handle for example Escape key on a dialog (I
actually need more than just Escape)? Do I need to handle
EVT_KEY_DOWN on every single widget inside the dialog?

[...]

Tomas Cerha

If that works with 2.6 then it is a bug. Key events are not derived
from wx.ComamndEvent and so they are not supposed to propagate to the
parent windows. This has been true at least since version 2.0.

Ok, how would I then handle for example Escape key on a dialog (I
actually need more than just Escape)? Do I need to handle
EVT_KEY_DOWN on every single widget inside the dialog?

Try using an wx.AcceleratorTable or EVT_CHAR_HOOK.

Are you sure that the dialog does not have the focus?

Yes, I am. For example I can type letters inside the TextCtrl and
these key presses don't propagate. They write text in the box. But
when I press Escape, it is not processed by the control and propagates
to on_key_down.

BTW, when I run your sample with 2.6 the on_key_down handler is not called.

Strange. We were relying on this behavior in our application since wx
2.2. And it worked (and still works) fine in all instalations
(currently on 2.6).

You hadn't mentioned ESC before. I tried that and it does in fact make it to the key-down handler in 2.6, but like I said it shouldn't

···

On 8/10/10 12:40 PM, TC wrote:

On 10 srp, 20:03, Robin Dunn<ro...@alldunn.com> wrote:

--
Robin Dunn
Software Craftsman

Ok, how would I then handle for example Escape key on a dialog (I

actually need more than just Escape)? Do I need to handle

EVT_KEY_DOWN on every single widget inside the dialog?

Try using an wx.AcceleratorTable or EVT_CHAR_HOOK.

The wx.AcceleratorTable documentation says:

“”“An accelerator table allows the application to specify a table of
keyboard shortcuts for menus or other commands. On Windows, menu or
button commands are supported; on GTK, only menu commands are
supported.”“”

So on GTK we can’t use non-menu shortcuts? What about Mac? Is there any cross-platform way to make application-wide shortcut keys that work even when the focus is on an inner widget?

Regarding EVT_CHAR_HOOK: I saw a message of your from 9 years ago saying EVT_CHAR_HOOK is inconsistent and mostly there for legacy reasons. Has this changed?

[…]

Robin Dunn

Ram.

···

On Wed, Aug 11, 2010 at 8:23 PM, Robin Dunn robin@alldunn.com wrote:

On 8/10/10 12:40 PM, TC wrote:

        Ok, how would I then handle for example Escape key on a dialog (I
        actually need more than just Escape)? Do I need to handle
        EVT_KEY_DOWN on every single widget inside the dialog?

    Try using an wx.AcceleratorTable or EVT_CHAR_HOOK.

The wx.AcceleratorTable documentation says:

    """An accelerator table allows the application to specify a table of

    keyboard shortcuts for menus or other commands. On Windows, menu or

    button commands are supported; on GTK, only menu commands are

    supported."""

So on GTK we can't use non-menu shortcuts? What about Mac?

That doc is about as clear as mud. :frowning: Basically an entry in an AcceleratorTable generates a EVT_MENU event when the key is pressed while the focus is anywhere within the context of the window that has the accelerator table, just like what would happen if there was a menu item with that key combination defined in the label. A menubar is not required to have accelerators.[1] I'm not 100% sure that it will work in a dialog, but I see no reason why it shouldn't.

[1] There once was a bug that made this not true on Windows, so if you're not using the latest release please update.

Is there any
cross-platform way to make application-wide shortcut keys that work even
when the focus is on an inner widget?

An accelerator table is not an application-wide solution if you have more than one top-level window, but there is no reason you couldn't attach one to each top-level window in your application.

Regarding EVT_CHAR_HOOK: I saw a message of your from 9 years ago
saying EVT_CHAR_HOOK is inconsistent and mostly there for legacy
reasons. Has this changed?

It seems to be one of those things that will never die. It is still used by wx.Dialog to handle the default processing of the ESC key. I expect that whatever was making me not like it back then has been resolved by now.

http://trac.wxwidgets.org/browser/wxWidgets/trunk/src/common/dlgcmn.cpp#L413

···

On 8/11/10 12:04 PM, cool-RR wrote:

On Wed, Aug 11, 2010 at 8:23 PM, Robin Dunn <robin@alldunn.com > <mailto:robin@alldunn.com>> wrote:
    On 8/10/10 12:40 PM, TC wrote:

--
Robin Dunn
Software Craftsman

I tried it now. There’s a problem: The shortcut keys get caught in cases where they shouldn’t get caught. For example, I use spacebar as a shortcut key to play\pause. I set up an accelerator table now to catch it, but now it catches it even when it shouldn’t, for example when the focus is in a text input control!

What should I do?

Ram.

···

On Thu, Aug 12, 2010 at 3:05 AM, Robin Dunn robin@alldunn.com wrote:

On 8/11/10 12:04 PM, cool-RR wrote:

The wx.AcceleratorTable documentation says:

"""An accelerator table allows the application to specify a table of



keyboard shortcuts for menus or other commands. On Windows, menu or



button commands are supported; on GTK, only menu commands are



supported."""

So on GTK we can’t use non-menu shortcuts? What about Mac?

That doc is about as clear as mud. :frowning: Basically an entry in an AcceleratorTable generates a EVT_MENU event when the key is pressed while the focus is anywhere within the context of the window that has the accelerator table, just like what would happen if there was a menu item with that key combination defined in the label. A menubar is not required to have accelerators.[1] I’m not 100% sure that it will work in a dialog, but I see no reason why it shouldn’t.

[1] There once was a bug that made this not true on Windows, so if you’re not using the latest release please update.
[…]

Robin Dunn

I tried it now. There's a problem: The shortcut keys get caught in cases
where they shouldn't get caught. For example, I use spacebar as a
shortcut key to play\pause. I set up an accelerator table now to catch
it, but now it catches it even when it shouldn't, for example when the
focus is in a text input control!

Yes, that is the nature of accelerators. They trigger events whenever the focus is within the scope of the window that the accelerator table is assigned to.

What should I do?

They're not ideal solutions, but one of these would work:

* You can change the accelerator to Ctrl-Space or something else that will not have meaning in the textctrl

* You can restructure the UI such that the widgets where you do not want the accelerator(s) to be active are not (grand-)children of the widget where the accelerator table is assigned.

* You can switch back to EVT_KEY_DOWN and Bind() a handler to each widget that could have the focus when you want spacebar (or whatever) to activate the action.

···

On 8/12/10 3:17 AM, cool-RR wrote:

--
Robin Dunn
Software Craftsman

How about the first solution here:

http://wiki.wxwidgets.org/Catching_key_events_globally

That is, overriding App.FilterEvent? Will this work from wxPython?

Ram.

···

On Fri, Aug 13, 2010 at 10:11 PM, Robin Dunn robin@alldunn.com wrote:

On 8/12/10 3:17 AM, cool-RR wrote:

I tried it now. There’s a problem: The shortcut keys get caught in cases

where they shouldn’t get caught. For example, I use spacebar as a

shortcut key to play\pause. I set up an accelerator table now to catch

it, but now it catches it even when it shouldn’t, for example when the

focus is in a text input control!

Yes, that is the nature of accelerators. They trigger events whenever the focus is within the scope of the window that the accelerator table is assigned to.

What should I do?

They’re not ideal solutions, but one of these would work:

  • You can change the accelerator to Ctrl-Space or something else that will not have meaning in the textctrl

  • You can restructure the UI such that the widgets where you do not want the accelerator(s) to be active are not (grand-)children of the widget where the accelerator table is assigned.

  • You can switch back to EVT_KEY_DOWN and Bind() a handler to each widget that could have the focus when you want spacebar (or whatever) to activate the action.

Robin Dunn

Or a simpler idea: Making an EVT_KEY_DOWN handler for my App class?

Ram.

···

On Fri, Aug 13, 2010 at 11:55 PM, cool-RR cool-rr@cool-rr.com wrote:

On Fri, Aug 13, 2010 at 10:11 PM, Robin Dunn robin@alldunn.com wrote:

On 8/12/10 3:17 AM, cool-RR wrote:

I tried it now. There’s a problem: The shortcut keys get caught in cases

where they shouldn’t get caught. For example, I use spacebar as a

shortcut key to play\pause. I set up an accelerator table now to catch

it, but now it catches it even when it shouldn’t, for example when the

focus is in a text input control!

Yes, that is the nature of accelerators. They trigger events whenever the focus is within the scope of the window that the accelerator table is assigned to.

What should I do?

They’re not ideal solutions, but one of these would work:

  • You can change the accelerator to Ctrl-Space or something else that will not have meaning in the textctrl

  • You can restructure the UI such that the widgets where you do not want the accelerator(s) to be active are not (grand-)children of the widget where the accelerator table is assigned.

  • You can switch back to EVT_KEY_DOWN and Bind() a handler to each widget that could have the focus when you want spacebar (or whatever) to activate the action.

Robin Dunn

How about the first solution here:

http://wiki.wxwidgets.org/Catching_key_events_globally

That is, overriding App.FilterEvent? Will this work from wxPython?

Ram.

Okay, I thought about a solution but there seems to be a little problem with it, maybe you’ll have an idea.

I’ll bind EVT_KEY_DOWN in my App class, and put the logic for keyboard shortcuts there. Since even a non-propagating event like KeyEvent eventually gets processed by the App after it gets skipped, I will catch all the KeyEvents that weren’t processed by the widgets in which they happened.

The problem with this is EVT_CHAR. Because a text widget can do Skip on a key down event, in order to process it as a char event. But then my app will get the key down event and try to process it as a shortcut key, thinking that the original widget has no interest in it, when in fact it does, and it’s waiting for the EVT_CHAR to come around.

How do I solve this? I thought maybe I could have logic in my App.on_key_down that checks if this key down event will generate a char event, and if so to wait and catch the char event, and if it was sent then to activate the shortcut (because the original widget genuinely wants to skip the event) and if the anticipated char event didn’t arrive, not to activate the shortcut key, since the original widget did use the event.

Then of course, I will need (1) The ability to identify which char event corresponds to which key down event and (2) a consistent way to tell whether a key event is supposed to generate a char event.

Does this make sense? Does anyone have any suggestions or ideas?

Ram.

···

On Sat, Aug 14, 2010 at 12:34 PM, cool-RR cool-rr@cool-rr.com wrote:

On Fri, Aug 13, 2010 at 11:55 PM, cool-RR cool-rr@cool-rr.com wrote:

On Fri, Aug 13, 2010 at 10:11 PM, Robin Dunn robin@alldunn.com wrote:

On 8/12/10 3:17 AM, cool-RR wrote:

I tried it now. There’s a problem: The shortcut keys get caught in cases

where they shouldn’t get caught. For example, I use spacebar as a

shortcut key to play\pause. I set up an accelerator table now to catch

it, but now it catches it even when it shouldn’t, for example when the

focus is in a text input control!

Yes, that is the nature of accelerators. They trigger events whenever the focus is within the scope of the window that the accelerator table is assigned to.

What should I do?

They’re not ideal solutions, but one of these would work:

  • You can change the accelerator to Ctrl-Space or something else that will not have meaning in the textctrl

  • You can restructure the UI such that the widgets where you do not want the accelerator(s) to be active are not (grand-)children of the widget where the accelerator table is assigned.

  • You can switch back to EVT_KEY_DOWN and Bind() a handler to each widget that could have the focus when you want spacebar (or whatever) to activate the action.

Robin Dunn

How about the first solution here:

http://wiki.wxwidgets.org/Catching_key_events_globally

That is, overriding App.FilterEvent? Will this work from wxPython?

Ram.

Or a simpler idea: Making an EVT_KEY_DOWN handler for my App class?

Ram.

Yes. I added an additional method to wxPython's wx.App so FilterEvent does not take too much overhead in the applications where it is not used. So you'll need to call self.SetCallFilterEvent(True) to turn it on.

···

On 8/13/10 2:55 PM, cool-RR wrote:

How about the first solution here:

Catching key events globally - WxWiki

That is, overriding App.FilterEvent? Will this work from wxPython?

--
Robin Dunn
Software Craftsman

You can look at evt.GetEventObject() and decide what to do based on which widget it is, or perhaps just what its type is.

···

On 8/14/10 4:23 AM, cool-RR wrote:

    Or a simpler idea: Making an EVT_KEY_DOWN handler for my App class?

    Ram.

Okay, I thought about a solution but there seems to be a little problem
with it, maybe you'll have an idea.

I'll bind EVT_KEY_DOWN in my App class, and put the logic for keyboard
shortcuts there. Since even a non-propagating event like KeyEvent
eventually gets processed by the App after it gets skipped, I will catch
all the KeyEvents that weren't processed by the widgets in which they
happened.

The problem with this is EVT_CHAR. Because a text widget can do `Skip`
on a key down event, in order to process it as a char event. But then my
app will get the key down event and try to process it as a shortcut key,
thinking that the original widget has no interest in it, when in fact it
does, and it's waiting for the EVT_CHAR to come around.

How do I solve this?

--
Robin Dunn
Software Craftsman

But how can I decide based on what widget it is? I mean, I tried it for wx.py.shell.Shell, and my code detects that it has WANTS_CHARS, so now my code knows that if we got an EVT_KEY_DOWN but not an EVT_CHAR, it means we can process the received key as a shortcut key. (By the way, how do I convert from the uppercase key received by EVT_KEY_DOWN to the corresponding char? Can I access the wxPython/system function that does this?)

But then I tried it on SpinCtrl. For some reason, SpinCtrl seems to Skip its EVT_KEY_DOWN and EVT_CHAR events, even though I see that it does use the text that I type into it! I mean, these events get propagated to my App despite of the fact that SpinCtrl uses them. Also, shouldn’t SpinCtrl have WANTS_CHARS?

Yours confusedly,

Ram.

···

On Mon, Aug 16, 2010 at 9:00 PM, Robin Dunn robin@alldunn.com wrote:

On 8/14/10 4:23 AM, cool-RR wrote:

Or a simpler idea: Making an EVT_KEY_DOWN handler for my App class?

Ram.

Okay, I thought about a solution but there seems to be a little problem

with it, maybe you’ll have an idea.

I’ll bind EVT_KEY_DOWN in my App class, and put the logic for keyboard

shortcuts there. Since even a non-propagating event like KeyEvent

eventually gets processed by the App after it gets skipped, I will catch

all the KeyEvents that weren’t processed by the widgets in which they

happened.

The problem with this is EVT_CHAR. Because a text widget can do Skip

on a key down event, in order to process it as a char event. But then my

app will get the key down event and try to process it as a shortcut key,

thinking that the original widget has no interest in it, when in fact it

does, and it’s waiting for the EVT_CHAR to come around.

How do I solve this?

You can look at evt.GetEventObject() and decide what to do based on which widget it is, or perhaps just what its type is.

Robin Dunn

Does anyone have answers?

Ram.

···

On Mon, Aug 16, 2010 at 9:19 PM, cool-RR cool-rr@cool-rr.com wrote:

On Mon, Aug 16, 2010 at 9:00 PM, Robin Dunn robin@alldunn.com wrote:

On 8/14/10 4:23 AM, cool-RR wrote:

Or a simpler idea: Making an EVT_KEY_DOWN handler for my App class?

Ram.

Okay, I thought about a solution but there seems to be a little problem

with it, maybe you’ll have an idea.

I’ll bind EVT_KEY_DOWN in my App class, and put the logic for keyboard

shortcuts there. Since even a non-propagating event like KeyEvent

eventually gets processed by the App after it gets skipped, I will catch

all the KeyEvents that weren’t processed by the widgets in which they

happened.

The problem with this is EVT_CHAR. Because a text widget can do Skip

on a key down event, in order to process it as a char event. But then my

app will get the key down event and try to process it as a shortcut key,

thinking that the original widget has no interest in it, when in fact it

does, and it’s waiting for the EVT_CHAR to come around.

How do I solve this?

You can look at evt.GetEventObject() and decide what to do based on which widget it is, or perhaps just what its type is.

Robin Dunn

But how can I decide based on what widget it is? I mean, I tried it for wx.py.shell.Shell, and my code detects that it has WANTS_CHARS, so now my code knows that if we got an EVT_KEY_DOWN but not an EVT_CHAR, it means we can process the received key as a shortcut key. (By the way, how do I convert from the uppercase key received by EVT_KEY_DOWN to the corresponding char? Can I access the wxPython/system function that does this?)

But then I tried it on SpinCtrl. For some reason, SpinCtrl seems to Skip its EVT_KEY_DOWN and EVT_CHAR events, even though I see that it does use the text that I type into it! I mean, these events get propagated to my App despite of the fact that SpinCtrl uses them. Also, shouldn’t SpinCtrl have WANTS_CHARS?

Yours confusedly,

Ram.