tags:

views:

329

answers:

2

How can I temporarily disable all accelerators in a wxPython app?

For example, I have a special TextCtrl that I want to use to record a single keypress, and I don't want that keypress to be treated like an accelerator. While that widget has focus I want to disable all accelerators.

On the wxPython mailing list it was suggested I bind EVT_UPDATE_UI to all the menu ids and enable or disable the event based on what widget has focused. That implies I have to know all my menu ids, but I do not. This is for an application that lets users define their own menu items and potentially their own accelerators.

A: 

To disable the accelerators you can set the accelerator table to the predifined wx.NullAcceleratorTable

ctrl.SetAcceleratorTable(wx.NullAcceleratorTable)

I've done this to permanently disable the accelerator table in frames, and I assume if you want to do this temporarily, you could just swap the tables as needed.

tom10
That doesn't work. I've tried setting a null accelerator table for the control, for the panel that the control is in, the frame that the panel is in, and the menubar that is part of the frame. In all cases, accelerators tied to the menubar will fire.
Bryan Oakley
I see, my bad... I missed the menu issue stated in your question, and the NullAcceleratorTable does not change the menu shortcuts.
tom10
A: 

If you want to disable accelerator keys, SetAcceleratorTable does work e.g. here I use SetAcceleratorTable to disable accelerator keys, to disable shortcut keys is bit difficult, i am using a hacky way of changing all menu texts, but top menu still opens on alt-m but rest are disabled.

Aletrnatively i think you can catch EVT_CHAT/EVT_KEY_UP for frame and its child and easily block events, but there you will need to be careful.

import wx

class MainFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, style=wx.DEFAULT_FRAME_STYLE|wx.WS_EX_PROCESS_UI_UPDATES,
                    parent=None,size=(400,300))
        self._setMenu()
        self.Show()
        self._menuTextMap = {}
        self.btn = wx.Button(self, -1, "Disable Accel")
        self.btn.Bind(wx.EVT_BUTTON, self._toggleAccel)

    def _enableMenuShortcuts(self, enable):
        if enable and not self._menuTextMap:
            return

        def enableMenu(menuItem, i=-1):
            if i==-1:#item
                text = menuItem.GetText()
            else:#menu
                text = self.menuBar.GetLabelTop(i)

            if enable:
                print text,
                newText = self._menuTextMap[text]
                print newText
            else:
                newText = text.replace('&','')+" disabled"
                self._menuTextMap[newText] = text

            if i:
                menuItem.SetText(newText)
            else:
                self.menuBar.SetLabelTop(i,newText)

        for i in range(self.menuBar.GetMenuCount()):
            menu = self.menuBar.GetMenu(i)
            enableMenu(menu, i)
            for menuItem in menu.GetMenuItems():
                enableMenu(menuItem)

    def _toggleAccel(self, event):
        self.accelOn = not self.accelOn
        if self.accelOn:
            self.SetAcceleratorTable(self.aTable)
            self._enableMenuShortcuts(True)
            self.btn.SetLabel("Disable Accel")
        else:
            self.SetAcceleratorTable(wx.NullAcceleratorTable)
            self._enableMenuShortcuts(False)
            self.btn.SetLabel("Enable Accel")
        #self.menuBar.SetAcceleratorTable(wx.NullAcceleratorTable)

    def _setMenu(self):
        self.menuBar = wx.MenuBar()

        myMenu= wx.Menu()
        accelList = []

        for i in range(9):
            ID = wx.NewId()
            myMenu.Append(ID, "Item &%s"%(i+1))
            wx.EVT_MENU(self, ID, self._onMenu)
            accelList.append((wx.ACCEL_CTRL, ord('A')+i, ID))

        self.menuBar.Append(myMenu, "&My Menu")
        self.SetMenuBar( self.menuBar)
        self.accelOn=True
        self.aTable = wx.AcceleratorTable(accelList)
        self.SetAcceleratorTable(self.aTable);

    def  _onMenu(self, event):
        item = self.menuBar.GetMenu(0).FindItemById(event.GetId())
        wx.MessageBox("Menu -> %s"%(item.GetItemLabel(),))

app = wx.PySimpleApp()
app.SetTopWindow(MainFrame())
app.MainLoop()

You said EVT_UPADTE_UI may work if you know all ids, for that you can just loop thru all menu items

for i in range(self.menuBar.GetMenuCount()):
    menu = self.menuBar.GetMenu(i)
    enableMenu(menu, i)
    for menuItem in menu.GetMenuItems():
        enableMenu(menuItem)
Anurag Uniyal