wxPython 4:检测两次击键

时间:2018-08-06 08:29:06

标签: python-2.7 events wxpython keyboard-shortcuts wxpython-phoenix

wxPython 4是否可以识别双击 same 键的方法? 特别是在我的情况下,我想知道何时快速连续按两次SHIFT键。

3 个答案:

答案 0 :(得分:1)

这绝不像初看起来那样明显。

下面的代码使用“一次性” 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()

我从您的谈话中的注释中注意到,这不仅是您关于Stack Overflow的第一个问题,而且您似乎已被这些注释略微排除了。

如果您不提供代码,无法正常工作或失败,您将被否决。论坛成员喜欢查看代码,特别是您已经尝试的代码。基本上,这仅仅是一个基本指标,它表明您是否在做出单行的问题并希望其他人为您解决问题之前,已经做出自己的努力来回答问题。

我最近发布了一个自我回答的问题,该问题和答案一起发布。即使我为自己的问题提供了详细的编码答案,我也立即对此问题投了反对票。因此,请勿个人使用。我怀疑有些人只是简单地坚持“非常”遵守“规则”就可以了。
也就是说,如果您坚持使用SO一段时间并自己回答问题,您将开始看到问题和MCVE中代码的优点。您会惊讶于某些人发布的内容,期望得到答案。

答案 1 :(得分:0)

我认为问题在于,当一次触发多个键,但又又很快时,他不想被告知。我没有找到本机的方法,所以我自己尝试了。我的解决方案在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()

答案 2 :(得分:0)

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

使用事件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()