【问题标题】:wxPython: Getting a EVT_KEY_DOWN event for CMD+[other key] on OSXwxPython:在 OSX 上获取 CMD+[其他键] 的 EVT_KEY_DOWN 事件
【发布时间】:2014-02-19 12:16:28
【问题描述】:

当用户在 OSX 上按下 COMMAND + [任何其他键] 时,我试图捕捉一个事件。由于这实际上是两次按键,我预计会发生两个事件:一个是在按下 COMMAND 时,另一个是在按下另一个键时(不释放 COMMAND 键)。这适用于除 COMMAND 之外的每个修饰符,我只得到第一个事件。为什么会这样,我该如何解决?

版本:wxPython3.0-osx-cocoa-py2.7

示例代码:

import wx

def OnKeyDown(e):
    print "Modifiers: {} Key Code: {}".format(e.GetModifiers(), e.GetKeyCode())

app = wx.App()
frame = wx.Frame(None)

textctrl = wx.TextCtrl(frame, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.WANTS_CHARS)
textctrl.Bind(wx.EVT_KEY_DOWN, OnKeyDown)

frame.Show()
app.MainLoop()

对于 ALT + d,输出为:

Modifiers: 1 Key Code: 307
Modifiers: 1 Key Code: 68

对于 SHIFT + d,输出为:

Modifiers: 4 Key Code: 306
Modifiers: 4 Key Code: 68

只有 COMMAND + d 的输出是:

Modifiers: 2 Key Code: 308

感谢您的帮助

其他信息:我在虚拟机上使用 OSX 10.8。正如 RobinDunn 指出的那样,它可以在他的笔记本电脑上运行。所以很可能这只是我的环境中的一个问题。 wnnmaw 提供了一个很好的解决方法,即使在虚拟环境中也对我有用。

【问题讨论】:

  • 您使用的是什么版本的 OSX?您是否使用任何非标准键盘映射、IME 或处理全局热键的系统插件?我在 10.8 上,我在这里获得了 Cmd+d 的关键事件。顺便说一句,您还应该尝试使用演示中的 KeyEvent 示例,以确保这不是关键事件被本机小部件窃取的问题。
  • 这绝对是你想用加速器表解决的问题
  • @RobinDunn:我也在使用 10.8,但我在 Windows 上的 VM 中运行它。这可能是原因,尽管我仍然持怀疑态度,因为 CMD+d 在其他非 wxpython 应用程序中工作时没有问题。我想我需要得到一台真正的苹果机器来做进一步的测试。
  • @wnnmaw:我正在尝试实现一个设置对话框,用户可以在其中选择他/她自己的快捷方式(类似于 ShortcutEditor,顺便说一句,在我的机器上也有同样的问题)。因为我不知道用户会选择哪个快捷方式,所以我不能使用快捷键表。
  • 我在 KeyEvent 演示中得到了相同的结果

标签: macos wxpython


【解决方案1】:

好的,所以我花了一些时间,但这里有一个工作代码块,可以满足您的需求。

注意事项:

  • 我是在 Windows 上执行此操作的(因此您可以进行一些更改,例如添加对 wx.ACCEL_RAW_CTRL 的支持,它对应于 Mac 上的实际控制键,而 wx.ACCCEL_CTRL 对应于命令键
  • 这段代码绝对可以(并且需要)清理
  • 在将其提供给用户之前,您必须添加更好的错误检查

不用再说了,就到这里

import wx
from wx.lib.scrolledpanel import ScrolledPanel

class TestWindow(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, 'Accelerator Table Test', size=(600, 400))

        self.panel = ScrolledPanel(parent=self, id=-1) 
        self.panel.SetupScrolling()

        #Create IDs to be used in initial acclerator table
        self.functionList = [self.func1, self.func2, self.func3, self.func4, self.func5, self.OnAdd, self.OnDel]
        self.functionListstr = ["self.func1", "self.func2", "self.func3", "self.func4", "self.func5", "self.OnAdd", "self.OnDel"]
        self.IDDict = {i: wx.NewId() for i in range(len(self.functionList))}
        self.IDDictrev = {val:key for key, val in self.IDDict.iteritems()}

        self.aTableList = [(wx.ACCEL_ALT, ord('S'), self.IDDict[0]),(wx.ACCEL_CTRL, ord('Q'), self.IDDict[1])]

        #Set up initial accelerator table
        aTable = wx.AcceleratorTable(self.aTableList)
        self.SetAcceleratorTable(aTable)

        #Bind inital accelerator table IDs to functions
        for i in range(len(self.functionList)):
            self.Bind(wx.EVT_MENU, self.functionList[i], id=self.IDDict[i]) 


        #Set up control widgets on GUI    
        self.flexgrid = wx.FlexGridSizer(cols=3, hgap=10, vgap=5)

        cmdkeylbl = wx.StaticText(self.panel, -1, "Command Key") 
        self.cmdkey = wx.ComboBox(self.panel, style=wx.CB_READONLY)
        cmdkeylist = ["Alt", "Control/Command", "Shift", "OSX Control", "None"]
        self.cmdkeyconstants = {"Alt":wx.ACCEL_ALT, "Control/Command":wx.ACCEL_CTRL, "Shift":wx.ACCEL_SHIFT, "None":wx.ACCEL_NORMAL}
        self.cmdkeyconstantsrev = {val:key for key, val in self.cmdkeyconstants.iteritems()}
        self.cmdkey.SetItems(cmdkeylist)

        hotkeylbl = wx.StaticText(self.panel, -1, "HotKey (single letter only)") 
        self.hotkey = wx.TextCtrl(self.panel, size=(50,-1))

        funclbl = wx.StaticText(self.panel, -1, "Function") 
        self.func = wx.ComboBox(self.panel, style=wx.CB_READONLY)
        self.func.SetItems(self.functionListstr)

        self.hbox = wx.BoxSizer(wx.HORIZONTAL)

        addBtn = wx.Button(self.panel, -1, "Add")
        delBtn = wx.Button(self.panel, -1, "Delete")

        self.Bind(wx.EVT_BUTTON, self.OnAdd, addBtn)
        self.Bind(wx.EVT_BUTTON, self.OnDel, delBtn)

        self.vbox = wx.BoxSizer(wx.VERTICAL)

        self.curATable = wx.StaticText(self.panel, -1, self._DisplayATable())

        self.flexgrid.Add(cmdkeylbl)
        self.flexgrid.Add(hotkeylbl)
        self.flexgrid.Add(funclbl)
        self.flexgrid.Add(self.cmdkey)
        self.flexgrid.Add(self.hotkey)
        self.flexgrid.Add(self.func)

        self.hbox.Add((20, 20), 0)
        self.hbox.Add(addBtn)
        self.hbox.Add((0, 0), 0)
        self.hbox.Add(delBtn)
        self.hbox.Add((20, 20), 0)

        self.vbox.Add(self.flexgrid, flag=wx.TOP|wx.BOTTOM|wx.LEFT|wx.RIGHT|wx.EXPAND, border = 5)
        self.vbox.Add(self.hbox, flag=wx.TOP|wx.BOTTOM|wx.LEFT|wx.RIGHT|wx.EXPAND, border = 5)
        self.vbox.Add(self.curATable, flag=wx.TOP|wx.BOTTOM|wx.LEFT|wx.RIGHT|wx.EXPAND, border = 5)

        self.panel.SetSizer(self.vbox)
        self.panel.Layout()

    #Class Functions
    def _DisplayATable(self):
        aTablelbl = ""
        for cmdKey, hotKey, Func in self.aTableList:
            aTablelbl += "{} + {} calls {}\n".format(self.cmdkeyconstantsrev[cmdKey], chr(hotKey), self.functionListstr[self.IDDictrev[Func]])

        return aTablelbl

    def OnAdd(self, event):
        self.aTableList.append((self.cmdkeyconstants[self.cmdkey.GetValue()], ord(self.hotkey.GetValue()[:1].title()), self.IDDict[self.func.GetSelection()]))
        print "Added {} + {} as a shortcut for {}!".format(self.cmdkey.GetValue(), self.hotkey.GetValue()[:1].title(), self.functionListstr[self.func.GetSelection()])

        aTable = wx.AcceleratorTable(self.aTableList)
        self.SetAcceleratorTable(aTable)
        self.curATable.SetLabel(self._DisplayATable())
        self.panel.Layout()

    def OnDel(self, event):
        if (self.cmdkeyconstants[self.cmdkey.GetValue()], ord(self.hotkey.GetValue()[:1].title()), self.IDDict[self.func.GetSelection()]) in self.aTableList:
            self.aTableList.remove((self.cmdkeyconstants[self.cmdkey.GetValue()], ord(self.hotkey.GetValue()[:1].title()), self.IDDict[self.func.GetSelection()]))

            aTable = wx.AcceleratorTable(self.aTableList)
            self.SetAcceleratorTable(aTable)
            self.curATable.SetLabel(self._DisplayATable())
            self.panel.Layout()

        else:
            dlg = wx.MessageDialog(self, "ERROR: That combination is not in the accelerator table!", "Error", style=wx.OK)
            dlg.ShowModal()
            dlg.Destroy()

    def func1(self, event):
        dlg = wx.MessageDialog(self, "Func1", "Func1", style=wx.OK)
        dlg.ShowModal()
        dlg.Destroy()

    def func2(self, event):
        dlg = wx.MessageDialog(self, "Func2", "Func2", style=wx.OK)
        dlg.ShowModal()
        dlg.Destroy()

    def func3(self, event):
        dlg = wx.MessageDialog(self, "Func3", "Func3", style=wx.OK)
        dlg.ShowModal()
        dlg.Destroy()

    def func4(self, event):
        dlg = wx.MessageDialog(self, "Func4", "Func4", style=wx.OK)
        dlg.ShowModal()
        dlg.Destroy()

    def func5(self, event):
        dlg = wx.MessageDialog(self, "Func5", "Func5", style=wx.OK)
        dlg.ShowModal()
        dlg.Destroy()

app = wx.App(False)
frame = TestWindow()
frame.Show()
app.MainLoop()

希望这就是您要找的,如果您有任何问题,请告诉我

【讨论】:

  • 非常感谢。这不完全是我想要的,但它会做,并且 CMD+x 可以完美地工作。我需要做更多的工作来支持特殊键(例如 F1-F12),但你让我走上了正确的轨道。理想情况下,我希望只有一个输入字段可以捕获用户输入的任何组合键(请参阅 ShortcutEditor)。
  • 这应该很容易修改你得到你想要的。我不确定如何使用 F 键,我会研究一下
猜你喜欢
  • 1970-01-01
  • 2011-04-03
  • 2023-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-01
  • 2023-03-20
  • 2015-10-16
相关资源
最近更新 更多