【问题标题】:wxPython 4: detection of double keystrokewxPython 4:检测双击键
【发布时间】:2019-01-13 04:01:24
【问题描述】:

wxPython 4 有没有办法识别双按 same 键? 特别是在我的情况下,我想识别快速连续按两次 SHIFT 键的时间。

【问题讨论】:

标签: python-2.7 events keyboard-shortcuts wxpython


【解决方案1】:

这绝不像看起来那么明显。

下面的代码,使用“one-shot”wx.Timer在 250 毫秒后重新设置前一个键,解决“快速连续”的问题。您当然可以将其设置为任何合适的值。 对于旧版本的 wxPython,计时器没有 StartOnce 选项,您必须使用 Start(250, oneShot=True)

我通过允许Shift 以外的键将其稍微复杂化,并且名称字典仅用于测试目的。

我应该指出,因为这必须检查每个按键按下,它不是很有效,但我想你知道这一点并愿意付出代价。

我确实有一个警告,我不知道按住某个键(例如 shift 键)会如何在非 Linux 机器上做出反应。如果证明它不像Linux,那么你应该将Bindwx.EVT_KEY_DOWN更改为wx.EVT_KEY_UP

import wx
import time
class Frame(wx.Frame):

    def __init__(self, parent):
        wx.Frame.__init__ (self, parent, -1, title = "Test Frame", size = (500,360))

        self.text_window = wx.TextCtrl(self, wx.ID_ANY, "", size = (450,250), style = wx.TE_MULTILINE)
        self.text_window.Bind(wx.EVT_KEY_DOWN, self.key_info)

        #Define a timer to reset the key values
        self.key_timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.Ontimer, self.key_timer)

        #Define storage for the previous key
        self.prev_key = 0

        #Define the set of double keys we are looking for and a dict  of their names
        # Shift is 306 (on my keyboard), Alt is 307 and Ctrl is 308
        self.double_keys = (306,307,308)
        self.names = {'306':'Shift','307':'Alt','308':'Ctrl'}

        sizer1= wx.BoxSizer(wx.VERTICAL)
        sizer1.Add(self.text_window, 0, wx.ALL, 5)
        self.SetSizer(sizer1)
        self.Show()

    def key_info(self, event):
        self.key = event.GetKeyCode()
        if self.key in self.double_keys and self.prev_key == self.key:
            self.text_window.AppendText("Double key "+self.names[str(self.key)]+" used within a quarter second\n")
        self.prev_key = self.key
        #fire up the timer to execute once to reset the previous key
        if self.key in self.double_keys:
            self.key_timer.StartOnce(250)
        # Skip so this doesn't consume the key event itself
        event.Skip()

    def Ontimer(self,event):
        # Re-set the previous key after 250 milliseconds
        self.prev_key = 0

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

注意我从您在 cmets 中的谈话中注意到,这不仅是您在 Stack Overflow 上的第一个问题,而且您似乎已经被 cmets 稍微放过。

如果你不提供你的代码,工作或失败很惨,你会吸引反对票。论坛成员喜欢查看代码,特别是您已经尝试过的代码。它基本上只是一个初步指标,表明您是否已经努力自己回答问题,然后再提出一个单行问题并希望其他人为您做腿部工作。

我最近发布了一个自我回答的问题,其中问题和答案一起发布。尽管我为自己的问题提供了详细的编码答案,但我立即收到了对该问题的反对票。所以不要个人认为。我怀疑有些人只是通过严格遵守“规则”来获得成功。
也就是说,如果你坚持 SO 一段时间并自己回答问题,你将开始看到问题中的代码和 MCVE 的优点。您会惊讶于某些人发布的内容并期待答案。

【讨论】:

  • 感谢您的回答!这不是我所希望的,但根据你的例子,我现在创建了一个更通用地解决问题的类,这对我来说已经足够了:-)
  • @J.Doe 如果您认为我的答案不值得被接受或有用的投票,您可以随时发布自己的代码作为答案。这样,任何有类似问题的人都可以找到一个例子。毕竟,这就是 SO 的全部意义所在。
【解决方案2】:

这是一个更通用的解决方案

使用事件EVT_CHAR_HOOK 代替EVT_KEY_UPEVT_KEY_UP 的问题在于,当事件绑定到包含面板的框架时,它会被吞噬。

EVT_CHAR_HOOK 的挑战是确定该键是实际按下两次还是仅按住。因此,RawKeyFlags 被读出。第 30 位的位表示密钥是否被持有。

但请注意,此解决方案仅适用于 Windows 系统!

import wx

class DoubleKeyStrokeListener(object):
    def __init__(self, parent, keyCode, callback, timeout=500):
        self._monitoredKey = keyCode
        self._callback = callback
        self._timeout = timeout

        self._firstPressRecognized = False
        self._keyTimer = wx.Timer(parent)

        parent.Bind(wx.EVT_CHAR_HOOK, self._OnKeyPressed)
        parent.Bind(wx.EVT_TIMER, self._OnTimer, self._keyTimer)

    def _OnKeyPressed(self, event):
        event.Skip()

        pressedKey = event.GetKeyCode()

        if pressedKey == self._monitoredKey:
            rawFlags = event.GetRawKeyFlags()

            # bit at position 30 is "previous key state" flag
            prevStateBit = rawFlags >> 30 & 1

            if prevStateBit == 1:  # -> key is held
                return

            if self._firstPressRecognized:
                self._firstPressRecognized = False
                self._callback(event)
            else:
                self._firstPressRecognized = True
                self._keyTimer.StartOnce(self._timeout)
        else:
            self._firstPressRecognized = False

    def _OnTimer(self, event):
        self._firstPressRecognized = False
        event.Skip()

【讨论】:

    【解决方案3】:

    我认为问题在于,当几个键同时触发但又一个接一个时,他不想得到通知。我没有找到本地方法来做到这一点,所以我自己尝试了。我的解决方案在 TestFrame 中导致了丑陋的状态,所以如果 wxPython 中存在本机方法,我也会感兴趣。

    import wx
    import time
    
    class TestFrame(wx.Frame):
        def __init__(self, parent):
            wx.Frame.__init__(self, parent)
    
            self._keyCounter = 0
            self._lastKeyTs = -1
    
            sizer = wx.BoxSizer(wx.VERTICAL)
            self.Bind(wx.EVT_KEY_DOWN, self._OnKeyDown)
            self.SetSizer(sizer)
    
        def _OnKeyDown(self, event):
            self._keyCounter += 1
            if self._keyCounter % 2 == 0 and time.time() - self._lastKeyTs < 0.3:
                print "Trigger"
            self._lastKeyTs = time.time()
    
    class App(wx.App):
        def OnInit(self):
            frmMain = TestFrame(None)
            frmMain.SetSize(wx.Size(800, 600))
    
            frmMain.Show()
    
            return True
    
    
    if __name__ == '__main__':
        application = App(False)
        application.MainLoop()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-14
      • 2015-11-09
      相关资源
      最近更新 更多