[wxPython] EVT_CHAR_HOOK on letters, or conditional accelerators

Hello,

I want to have single letter key (i.e. mnemonic and non-RSI inducing)
accelerators, which function if and only if the focus is not in a text control.
(You can create a letter-key accelerator, but then the keypress it is swallowed
before reaching the text control, even if that control has the focus.)
I have seen postings which imply similar desires, but to date, I have found no
way to get such behavior. I was hoping that I could use EVT_CHAR_HOOK to
implement the conditional accelerator, but alas, I am stymied at every turn...

It appears that you cannot get EVT_CHAR_HOOK to fire on letter key events
(at least on wxFrame/Panel/Dialog classes.) There have been numerous
postings and responses about this, e.g.:

Harm Van der Heijden wrote:

I had the exact same problem when I wrote hangman.py a couple of months ago.
The solution was to derive the window that was supposed to capture the key
events from a wxWindow, not from a wxPanel. (wxFrames also don't seem to
react to key events).)

And Robin Dunn wrote in response:

Right, this is because key and char events are not "command events" so the
don't travel up the containment hierarchy but only go to the window where
the event happened.

However, I don't understand this explanation, nor can I get Harm's supposed
workaround to function, e.g.:

Oops, forgot to clarify something. In the frame's init, don't use
EVT_CHAR(self, self.OnChar)
but do something like
wnd = wxWindow(self, -1) # take the whole frame client space by default
EVT_CHAR(wnd, self.OnChar)

(the OnChar *never* fires if I do this. This may be because the frame
contains a panel for laying out controls on, but it doesn't work if I try
the above trick on the panel either.)

But why are only "command events" "hookable?" Why aren't/(can't?) char
events (be) considered commands and "travel up the containment hierarchy?"
Couldn't there be a flag somewhere where you could say you want this
behavior??

Alternatively, is there any way to have an accelerator act conditionally
based on focus?

Any help or solution would be appreciated.
/Will Sadkin
wsadkin@nameconnector.com

It appears that you cannot get EVT_CHAR_HOOK to fire on letter key events
(at least on wxFrame/Panel/Dialog classes.) There have been numerous
postings and responses about this, e.g.:

As Vadim said in the other list, you shouldn't EVT_CHAR_HOOK. I've found it
to be unpredictable...

But why are only "command events" "hookable?" Why aren't/(can't?) char
events (be) considered commands and "travel up the containment hierarchy?"

Because if every event went up the hierarchy then wxPython would probably be
slower than Tkinter stuck in a tar pit on a cold day.

Couldn't there be a flag somewhere where you could say you want this
behavior??

It's been discussed and most people were favorable to it if it could be done
with minimal overhead.

Alternatively, is there any way to have an accelerator act conditionally
based on focus?

Python is more flexible than C++ so you can bind any callable object to any
event for any window, so you could do an EVT_CHAR for every control that you
want to be sensitive to this keystroke and bind it to a method in your frame
class. Or you could also do it the C++ way and derive a class from
wxEvtHandler that has EVT_CHAR hooked, and then for every control that you
want to handle it call control.PushEventHandler(MyEvtHandler(self))

···

--
Robin Dunn
Software Craftsman
robin@AllDunn.com Java give you jitters?
http://wxPython.org Relax with wxPython!

Robin Dunn wrote:

> But why are only "command events" "hookable?" Why aren't/(can't?) char
> events (be) considered commands and "travel up the containment hierarchy?"

Because if every event went up the hierarchy then wxPython would probably be
slower than Tkinter stuck in a tar pit on a cold day.

I actually doubt it; key events aren't that common; *mouse* events would be
bad, but people just don't type that fast.

> Alternatively, is there any way to have an accelerator act conditionally
> based on focus?

Python is more flexible than C++ so you can bind any callable object to any
event for any window, so you could do an EVT_CHAR for every control that you
want to be sensitive to this keystroke and bind it to a method in your frame
class.

Ok, this wouldn't be SO bad, but when I tried this, with both EVT_CHAR and
EVT_CHAR_HOOK, on the two buttons on my test panel, neither routine
*ever* fired on even so-called "command" events, let alone letters. My guess
is that the individual controls don't get key events unless they are
specifically built for them. There is a comment in the doc which says
EVT_CHAR_HOOK only applies to top-level windows, and since I've never gotten
EVT_CHAR to do anything useful, my guess is that this is the case with it
too... Am I all wet? Is there something I'm missing?

Regards,
/WS

Ok, this wouldn't be SO bad, but when I tried this, with both EVT_CHAR and
EVT_CHAR_HOOK, on the two buttons on my test panel, neither routine
*ever* fired on even so-called "command" events, let alone letters. My

guess

is that the individual controls don't get key events unless they are
specifically built for them. There is a comment in the doc which says
EVT_CHAR_HOOK only applies to top-level windows, and since I've never

gotten

EVT_CHAR to do anything useful, my guess is that this is the case with it
too... Am I all wet? Is there something I'm missing?

Does the control have the focus?

···

--
Robin Dunn
Software Craftsman
robin@AllDunn.com Java give you jitters?
http://wxPython.org Relax with wxPython!

> Ok, this wouldn't be SO bad, but when I tried this, with both EVT_CHAR

and

> EVT_CHAR_HOOK, on the two buttons on my test panel, neither routine
> *ever* fired on even so-called "command" events, let alone letters. My
guess
> is that the individual controls don't get key events unless they are
> specifically built for them. There is a comment in the doc which says
> EVT_CHAR_HOOK only applies to top-level windows, and since I've never
gotten
> EVT_CHAR to do anything useful, my guess is that this is the case with

it

> too... Am I all wet? Is there something I'm missing?
>

Does the control have the focus?

Oh, one more thing... If the control is handling the EVT_KEY_DOWN and/or
EVT_KEY_UP messages then there may never be a EVT_CHAR. This is probably
the case with what you are seeing. You can catch EVT_KEY_DOWN just as
easily as EVT_CHAR and do waht ever you need.

···

--
Robin Dunn
Software Craftsman
robin@AllDunn.com Java give you jitters?
http://wxPython.org Relax with wxPython!

Robin Dunn wrote:

Oh, one more thing... If the control is handling the EVT_KEY_DOWN and/or
EVT_KEY_UP messages then there may never be a EVT_CHAR. This is probably
the case with what you are seeing. You can catch EVT_KEY_DOWN just as
easily as EVT_CHAR and do waht ever you need.

Since you brought it up, maybe you can help me sort this out.

wxPython 2.2.5 + wxGTK 2.2.5 + Python 2.1 + KDE 2.0.1:

I created a wxGrid with a wxPyGridCellEditor. I wanted to create a
drop-down that redefines the EVT_KEY_UP and EVT_KEY_DOWN events, since
the default behavior once editing is active is to change the combo box
selection rather than move to the next cell in the grid. The Create()
method looks like this:

    def Create(self, parent, id, evtHandler):
        """
        Called to create the control, which must derive from wxControl.
        *Must Override*
        """
        self._box = box = wxComboBox(parent, id, "", choices=['a', 'b'],
                                     style=wxCB_DROPDOWN)
        box.SetInsertionPoint(0)
        self.SetControl(box)
        if evtHandler:
            box.PushEventHandler(evtHandler)
        EVT_KEY_DOWN(box, self.OnBoxKeyDown)

Unfortunately, the only key events that don't get passed to OnBoxKeyDown
are EVT_KEY_UP and EVT_KEY_DOWN (I used a "print" to discover this),
which invalidates the reason for creating the custom cell editor. Is
this caused by my program, wxGTK, wxPython, KDE, GTK, or something else?

Regardless of little things like this, BTW, may I say wxPython is one
fine toolkit. Great work!

Shane