Accelerators not working on GTK, works on XP

14 views
Skip to first unread message

Steven Sproat

unread,
Nov 6, 2009, 11:02:39 PM11/6/09
to wxpytho...@googlegroups.com
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

Robin Dunn

unread,
Nov 7, 2009, 1:10:24 PM11/7/09
to wxpytho...@googlegroups.com
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.

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.

--
Robin Dunn
Software Craftsman
http://wxPython.org

Steven Sproat

unread,
Nov 11, 2009, 7:52:51 AM11/11/09
to wxpytho...@googlegroups.com
Hi Rob,

2009/11/7 Robin Dunn <ro...@alldunn.com>


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

Robin Dunn

unread,
Nov 12, 2009, 12:54:38 PM11/12/09
to wxpytho...@googlegroups.com
On 11/11/09 4:52 AM, Steven Sproat wrote:
> Hi Rob,
>
> 2009/11/7 Robin Dunn <ro...@alldunn.com <mailto:ro...@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.

Steven Sproat

unread,
Nov 13, 2009, 4:13:30 PM11/13/09
to wxpytho...@googlegroups.com
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 <ro...@alldunn.com>

Robin Dunn

unread,
Nov 16, 2009, 1:31:50 PM11/16/09
to wxpytho...@googlegroups.com
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.)

Steven Sproat

unread,
Nov 16, 2009, 2:17:21 PM11/16/09
to wxpytho...@googlegroups.com
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 :)

Reply all
Reply to author
Forward
0 new messages