Accelerators not working on GTK, works on XP

I have the following code. I asked Robin on IRC and he suggested to use
capital letters, hence the .upper() - however, it still does not working.

        # Need to bind each item's hotkey to trigger change tool,
passing its ID
        # (position + 1 in the list, basically)
        ac = []
        for x, item in enumerate(self.util.items):
            blah = lambda evt, y=x + 1: self.on_change_tool(evt, y)
            _id = wx.NewId()
            ac.append((wx.ACCEL_NORMAL, ord(item.hotkey.upper()), _id))
            self.Bind(wx.EVT_MENU, blah, id=_id)

        tbl = wx.AcceleratorTable(ac)
        self.SetAcceleratorTable(tbl)

self is a Frame.
self.util.items is a list of classes, like: [Pen, Rectangle, Circle, Zoom]
each of which has a string "hotkey" class attribute, like "p", "r", "c", "z"

This works great on Windows, but on GTK nothing happens, my lambda
method is not called.

Similarly, I have the following code

class MyFrame(wx.Frame):
        .....
        self.Bind(wx.EVT_CHAR_HOOK, self.close_fullscreen)

    def close_fullscreen(self, event=None):
        """ Toggles fullscreen """
        if not event.GetKeyCode() in [wx.WXK_ESCAPE]:
            event.Skip() # propogate
        else:
            if self.IsFullScreen():
                flag = (wx.FULLSCREEN_NOBORDER | wx.FULLSCREEN_NOCAPTION |
                   wx.FULLSCREEN_NOSTATUSBAR)
                self.ShowFullScreen(False, flag)
                menu = self.menu.FindItemById(ID_FULLSCREEN)
                menu.Check(False)

Again, this works on Windows, but not on GTK. Placing a print statement
before "if not event.GetKeyCode()..." does not give any output,
regardless of which key of press.

Any ideas or suggestions would be welcomed, thanks.
Steven Sproat

Are you sure that some widget within the frame has the keyboard focus? You might try adding a timer event that prints the results of wx.Window.FindFocus() to verify where the focus is.

···

On 11/6/09 8:02 PM, Steven Sproat wrote:

I have the following code. I asked Robin on IRC and he suggested to use
capital letters, hence the .upper() - however, it still does not working.

         # Need to bind each item's hotkey to trigger change tool,
passing its ID
         # (position + 1 in the list, basically)
         ac =
         for x, item in enumerate(self.util.items):
             blah = lambda evt, y=x + 1: self.on_change_tool(evt, y)
             _id = wx.NewId()
             ac.append((wx.ACCEL_NORMAL, ord(item.hotkey.upper()), _id))
             self.Bind(wx.EVT_MENU, blah, id=_id)

         tbl = wx.AcceleratorTable(ac)
         self.SetAcceleratorTable(tbl)

self is a Frame.
self.util.items is a list of classes, like: [Pen, Rectangle, Circle, Zoom]
each of which has a string "hotkey" class attribute, like "p", "r", "c", "z"

This works great on Windows, but on GTK nothing happens, my lambda
method is not called.

--
Robin Dunn
Software Craftsman

Hi Rob,

This works great on Windows, but on GTK nothing happens, my lambda

method is not called.

Are you sure that some widget within the frame has the keyboard focus?

You might try adding a timer event that prints the results of

wx.Window.FindFocus() to verify where the focus is.

I just done some testing on Linux:

    self.timer = wx.Timer(self)  
    self.Bind(wx.EVT_TIMER, self.on_timer, self.timer)
    self.timer.Start(200)

def on_timer(self, event):

    print wx.Window.FindFocus()

and it seems that my GUI frame never receives focus. At first starting my program, without clicking anything, I get many “None” printed. I click on a toggle button in my left-hand panel and the focus is given to the GenericToggleBitmapButton, and then back to None

It seems that mouse-overing the bitmap buttons gives them focus, too. Anyway, I put self.SetFocus() into the last line of my Frame’s init method, and now the Frame is reported as having focus, but if I click on a button then it returns to None again.

but my accelerators are still not firing.

I also noticed that my EVT_HOOK doesn’t work:

    self.Bind(wx.EVT_CHAR_HOOK, self.hotkey)

def hotkey(self, event=None):
    """

    Processes a hotkey (escape / home / end / page up / page down)
    """
    print 'ey'  # never printed
    if event.GetKeyCode() == wx.WXK_ESCAPE:  # close fullscreen

        if self.IsFullScreen():
            pass
    elif event.GetKeyCode() == wx.WXK_HOME:
        pass
    elif event.GetKeyCode() == wx.WXK_END:
        pass
    elif event.GetKeyCode() == wx.WXK_PAGEUP:

        pass
    elif event.GetKeyCode() == wx.WXK_PAGEDOWN:
        pass
    else:
        event.Skip()   # propogate

I tried giving the Frame the wx.WANTS_CHARS style, but it made no difference. I’m pretty stumped with this, it’s the first an error is happening on Linux that works on Windows (it’s usually the other way around!)

thanks,
Steven Sproat

···

2009/11/7 Robin Dunn robin@alldunn.com

On 11/6/09 8:02 PM, Steven Sproat wrote:

Hi Rob,

2009/11/7 Robin Dunn <robin@alldunn.com <mailto:robin@alldunn.com>>

     >
     > This works great on Windows, but on GTK nothing happens, my lambda
     > method is not called.

    Are you sure that some widget within the frame has the keyboard focus?
    You might try adding a timer event that prints the results of
    wx.Window.FindFocus() to verify where the focus is.

I just done some testing on Linux:

         self.timer = wx.Timer(self)
         self.Bind(wx.EVT_TIMER, self.on_timer, self.timer)
         self.timer.Start(200)

     def on_timer(self, event):
         print wx.Window.FindFocus()

and it seems that my GUI frame never receives focus. At first starting
my program, without clicking anything, I get many "None" printed. I
click on a toggle button in my left-hand panel and the focus is given to
the GenericToggleBitmapButton, and then back to None

It seems that mouse-overing the bitmap buttons gives them focus, too.
Anyway, I put self.SetFocus() into the last line of my Frame's init
method, and now the Frame is reported as having focus, but if I click on
a button then it returns to None again.

but my accelerators are still not firing.

I also noticed that my EVT_HOOK doesn't work:

Probably the same issue with focus.

I tried giving the Frame the wx.WANTS_CHARS style, but it made no
difference. I'm pretty stumped with this, it's the first an error is
happening on Linux that works on Windows (it's usually the other way
around!)

The wx.WANTS_CHARS style won't have any effect, as it's only used on MSW.

Can you reduce this problem to a small sample? In every case I've tried once the focus is in the frame or some widget within the frame then it stays there.

···

On 11/11/09 4:52 AM, Steven Sproat wrote:

    On 11/6/09 8:02 PM, Steven Sproat wrote:

--
Robin Dunn
Software Craftsman

I’ve yet to actually try this on GTK, but the code is recreating my application’s layout in the same order as my app does it, and binds the events as it does, so it should be a faithful recreation. The problem’s in the GUI - you can ignore the other classes, they’re just there in case it’s some kind of focus problem. I’ve tried using SetFocus() on the Frame too, but no luck there.

#!/usr/bin/python
import wx
from wx.lib import scrolledpanel as scrolled

class GUI(wx.Frame):
def init(self, parent):
wx.Frame.init(self, parent, size=(1024, 786))
cpanel = ControlPanel(self)
spanel = SidePanel(self)

    box = wx.BoxSizer(wx.HORIZONTAL)  # position windows side-by-side
    box.Add(cpanel, 0, wx.EXPAND)
    box.Add((1, 1), 2, wx.EXPAND)
    box.Add(spanel, 0, wx.EXPAND)
    self.SetSizer(box)

    ac = []
    hotkeys = ["a", "b", "c", "d", "e", "f", "g"]

    for x, item in enumerate(hotkeys):
        _id = wx.NewId()
        ac.append((wx.ACCEL_NORMAL, ord(item.upper()), _id))
        self.Bind(wx.EVT_MENU, self.accel, id=_id)

    tbl = wx.AcceleratorTable(ac)
    self.SetAcceleratorTable(tbl)
    self.Bind(wx.EVT_CHAR_HOOK, self.hotkey)     
   

def hotkey(self, evt):
    print 'hotkey'
   
def accel(self, evt):
    print 'accelerator'

class SidePanel(wx.Panel):
def init(self, parent):
wx.Panel.init(self, parent, size=(170, -1), style=wx.RAISED_BORDER)
self.cp = wx.CollapsiblePane(self, style=wx.CP_DEFAULT_STYLE |
wx.CP_NO_TLW_RESIZE)
sizer = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(sizer)
csizer = wx.BoxSizer(wx.VERTICAL)
self.cp.GetPane().SetSizer(csizer)

    self.tabs = wx.Notebook(self.cp.GetPane())
    self.thumbs = Thumbs(self.tabs)
    self.tabs.AddPage(self.thumbs, "Thumbnails")
    csizer.Add(self.tabs, 1, wx.EXPAND)
    sizer.Add(self.cp, 1, wx.EXPAND)

    self.cp.Expand()

class Thumbs(scrolled.ScrolledPanel):
def init(self, parent):
scrolled.ScrolledPanel.init(self, parent, size=(170, -1),
style=wx.VSCROLL | wx.RAISED_BORDER)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(self.sizer)
self.SetScrollRate(0, 250)
btn = wx.BitmapButton(self, size=(150, 150))
text = wx.StaticText(self, label=“Tab 1”)

    self.sizer.Add(text, flag=wx.ALIGN_CENTER | wx.TOP, border=5)
    self.sizer.Add(btn, flag=wx.TOP | wx.LEFT, border=6)

class ControlPanel(wx.Panel):
def init(self, parent):
wx.Panel.init(self, parent)
sizer = wx.GridSizer(cols=1, hgap=1, vgap=2)
sizer.Add(wx.Button(self, label=“Thing”), 0)
sizer.Add(wx.Button(self, label=“Thing”), 0)

    box = wx.BoxSizer(wx.VERTICAL)
    box.Add(sizer, 0, wx.ALL, 4)
    self.SetSizer(box)
    self.SetAutoLayout(True)
    box.Fit(self)

class TestApp(wx.App):
def OnInit(self):
frame = GUI(None)
frame.Show(True)
return True

if name == ‘main’:
app = TestApp()
app.MainLoop()

···

2009/11/12 Robin Dunn robin@alldunn.com

On 11/11/09 4:52 AM, Steven Sproat wrote:

Hi Rob,

2009/11/7 Robin Dunn <robin@alldunn.com mailto:robin@alldunn.com>

On 11/6/09 8:02 PM, Steven Sproat wrote:
 >
 >
 > This works great on Windows, but on  GTK nothing happens, my lambda
 > method is not called.
Are you sure that some widget within the frame has the keyboard focus?
You might try adding a timer event that prints the results of
wx.Window.FindFocus() to verify where the focus is.

I just done some testing on Linux:

     self.timer = wx.Timer(self)
     self.Bind(wx.EVT_TIMER, self.on_timer, self.timer)
     self.timer.Start(200)
 def on_timer(self, event):
     print wx.Window.FindFocus()

and it seems that my GUI frame never receives focus. At first starting

my program, without clicking anything, I get many “None” printed. I

click on a toggle button in my left-hand panel and the focus is given to

the GenericToggleBitmapButton, and then back to None

It seems that mouse-overing the bitmap buttons gives them focus, too.

Anyway, I put self.SetFocus() into the last line of my Frame’s init

method, and now the Frame is reported as having focus, but if I click on

a button then it returns to None again.

but my accelerators are still not firing.

I also noticed that my EVT_HOOK doesn’t work:

Probably the same issue with focus.

I tried giving the Frame the wx.WANTS_CHARS style, but it made no

difference. I’m pretty stumped with this, it’s the first an error is

happening on Linux that works on Windows (it’s usually the other way

around!)

The wx.WANTS_CHARS style won’t have any effect, as it’s only used on MSW.

Can you reduce this problem to a small sample? In every case I’ve tried

once the focus is in the frame or some widget within the frame then it

stays there.

Robin Dunn

Software Craftsman

http://wxPython.org

Adding a cpanel.SetFocus() here seems to take care of it for me. (The panel will automatically pass the focus to its first child that accepts the focus.)

···

On 11/13/09 1:13 PM, Steven Sproat wrote:

I've yet to actually try this on GTK, but the code is recreating my
application's layout in the same order as my app does it, and binds the
events as it does, so it should be a faithful recreation. The problem's
in the GUI - you can ignore the other classes, they're just there in
case it's some kind of focus problem. I've tried using SetFocus() on the
Frame too, but no luck there.

#!/usr/bin/python
import wx
from wx.lib import scrolledpanel as scrolled

class GUI(wx.Frame):
     def __init__(self, parent):
         wx.Frame.__init__(self, parent, size=(1024, 786))
         cpanel = ControlPanel(self)
         spanel = SidePanel(self)

--
Robin Dunn
Software Craftsman

Robin Dunn wrote:

···

On 11/13/09 1:13 PM, Steven Sproat wrote:
  

I've yet to actually try this on GTK, but the code is recreating my
application's layout in the same order as my app does it, and binds the
events as it does, so it should be a faithful recreation. The problem's
in the GUI - you can ignore the other classes, they're just there in
case it's some kind of focus problem. I've tried using SetFocus() on the
Frame too, but no luck there.

#!/usr/bin/python
import wx
from wx.lib import scrolledpanel as scrolled

class GUI(wx.Frame):
     def __init__(self, parent):
         wx.Frame.__init__(self, parent, size=(1024, 786))
         cpanel = ControlPanel(self)
         spanel = SidePanel(self)

Adding a cpanel.SetFocus() here seems to take care of it for me. (The
panel will automatically pass the focus to its first child that accepts
the focus.)

Cheers Rob, I just figured this out earlier, but noticed a visual oddity
on the "toggle" button that the collapsiblepanel uses - so I'm instead
passing focus to another panel that's placed on the collapsible one.

This didn't solve my accelerator issue, but I made do by changing the
code so that it's caught in the EVT_CHAR_HOOK and processed there.

Thanks again :slight_smile: