Search code examples
pythonmacoswxpythonwxwidgets

Unable to focus on widgets in OS/X


I'm running Python 2.7.5 with wxWidgets installed through homebrew.

My issue is that keyboard events aren't picked up at all when running on OS/X. Consider the following sample source:

noname.py:

import wx
import wx.xrc
class MyFrame2 ( wx.Frame ):
   def __init__( self, parent ):
      wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )
      self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )
      bSizer1 = wx.BoxSizer( wx.VERTICAL )
      self.m_button1 = wx.Button( self, wx.ID_ANY, u"MyButton", wx.DefaultPosition, wx.DefaultSize, 0 )
      bSizer1.Add( self.m_button1, 0, wx.ALL, 5 )
      self.SetSizer( bSizer1 )
      self.Layout()
      self.Centre( wx.BOTH )
      # Connect Events
      self.m_button1.Bind( wx.EVT_BUTTON, self.OnClick )
   def __del__( self ):
      pass
   # Virtual event handlers, overide them in your derived class
   def OnClick( self, event ):
      event.Skip()

main.py:

from noname import MyFrame2
class MyFrame2Impl(MyFrame2):
    def __init__(self, parent):
        MyFrame2.__init__(self, parent)
    def OnClick(self, event):
        self.m_button1.SetLabel(str(wx.Window.FindFocus() != None))

import wx
app = wx.App(False)  # Create a new app, don't redirect stdout/stderr to a window.
frame = MyFrame2Impl(None) # A Frame is a top-level window.
frame.m_button1.SetFocus()
frame.Show(True)     # Show the frame.
app.MainLoop()

On Ubuntu, once I click the button, it shows "True". Pressing spacebar clicks the button. On OS/X, once I click the button, it shows "False". Pressing spacebar achieves nothing.

At the bottom of the WxMac FAQ (http://www.wxwidgets.org/docs/faqmac.htm) it says this:

Why can't I set focus to my wxMac application? Because you didn't build your application as a bundle. You must create a bundle for Mac OS X applications, simply running the usual g++ wx-config --cxxflags --libs -o minimal minimal.cpp is not enough to build a correctly working Mac application. Please look at the samples makefiles which do build bundles under Mac OS X or read this wiki topic for more details.

I've made a bundle using this script:

from setuptools import setup
APP = ["main.py"]
OPTIONS = {"argv_emulation": True, "includes": ["wx"]}
setup(app=APP, options={"py2app": OPTIONS}, setup_requires=["py2app"])

which I use to build a bundle with the command:

python setup.py py2app

This makes an executable, but the focus problem still persists. As a consequence, I'm unable to use the keyboard to control the application. How can I get the application to detect keyboard events?


Solution

  • I ended up coding around the problem.

    Previously, my application was handling keyboard events globally (see http://wiki.wxwidgets.org/Catching_key_events_globally).

    I couldn't get this to work on OS/X, so I gave up and rewrote the application such that it captures keyboard events locally. More specifically, the application includes a menu bar, and that menu bar has items. I can handle keyboard events by assigning keyboard shortcuts to the menu bar items.

    I'm not sure why global handling doesn't work on OS/X, but I don't really care anymore.