How do I set hotkeys (keyboard shortcuts) for a specific widget, that would work regardless of the active keyboard input language? Preferably, in a platform-independent way, but, at the very least, under Linux and wxGTK? Should be something simple, all reasonable software behave like that, but I couldn’t find how to do it.
I’ve tried with EVT_KEY_DOWN, but that required separate treatment of every individual letter attached to the hardware key. Perhaps the GetRawKeyFlags() could help, but how do I get the value it would return not for the event handler, but for a character (say, I have ‘Ctrl+Q’ written in some text config, or even hardcoded, how do I get the scan code of the hardware key the letter Q is assigned to)?
The menu shortcuts, actually, work regardless of the current input language (in the example below, ‘Ctrl+W’ triggers even when it’s in Cyrillic, so it’s kinda ‘Ctrl+Ц’). But, as I understand, you can only have one global menu attached to the main frame. What if I don’t want my frame to have a menu? What if I need the same hotkeys used by different widgets for different purposes?
I’ve also tried with accelerator tables. Despite reading somewhere on this forum that you can have only one global accelerator table, sort of like the menu, I was able to assign separate accelerators to separate widgets and they worked “correctly”, depending on the active focus. But, unlike with the menu, there I ran into the same problem again: in the example below the ‘Ctrl+E’ only triggers if the input is in Latin.
Windows doesn’t have any of those problems, ‘Ctrl+Q’, ‘Ctrl+W’ and ‘Ctrl+E’, respectively, get triggered regardless of the active input language.
import wx
class MainWindow(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, wx.ID_ANY, title, size=(500, 500))
#1 - Key event processed in OnKeyPressed
self.panel = wx.Panel(self)
self.panel.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed)
self.hotkey_lat = 'Q' # on the QWERTY layout
self.hotkey_cyr = 'й' # same key in Cyrillic layout (has to be the lower case letter, too)
self.hotkey_raw_key_flag = 24 # value of GetRawKeyFlags() under GTK
#2 - Menu event processed in OnMenu1
self.menu_bar = wx.MenuBar()
menu = wx.Menu()
menuitemid = wx.NewIdRef()
menu.Append(menuitemid, f"Menu {menuitemid}\tCtrl+W", "Do the Ctrl-W")
self.menu_bar.Append(menu, "Menu")
self.SetMenuBar(self.menu_bar)
self.Bind(wx.EVT_MENU, self.OnMenu1, id=menuitemid)
#3 - Accelerator processed in AccelOnE
accelitemid = wx.NewIdRef()
accel_entry_E = wx.AcceleratorEntry(wx.ACCEL_CTRL, ord('E'), accelitemid)
accel_table = wx.AcceleratorTable([accel_entry_E])
self.panel.SetAcceleratorTable(accel_table)
self.Bind(wx.EVT_MENU, self.AccelOnE, id=accelitemid)
def OnKeyPressed(self, evt):
is_caught = False
if evt.ControlDown():
# triggers when the keyboard is in Latin
if evt.GetUnicodeKey() == ord(self.hotkey_lat):
is_caught = True
print(f"Ctrl-{self.hotkey_lat} triggered!")
# triggers when the keyboard is in Cyrillic
if evt.GetUnicodeKey() == ord(self.hotkey_cyr):
is_caught = True
print(f"Ctrl-{self.hotkey_cyr} triggered!")
# triggers in both of the above cases
if evt.GetRawKeyFlags() == self.hotkey_raw_key_flag:
is_caught = True
print(f"Ctrl-{self.hotkey_raw_key_flag} triggered!")
evt.Skip(not is_caught)
def OnMenu1(self, evt):
print("Ctrl-W menu item triggered!")
def AccelOnE(self, evt):
print("Ctrl-E accelerator entry triggered!")
app = wx.App()
frame = MainWindow(None, -1, "Window")
frame.Show(1)
app.MainLoop()