首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >wxPython 4:检测双击击键

wxPython 4:检测双击击键
EN

Stack Overflow用户
提问于 2018-08-06 16:29:06
回答 3查看 290关注 0票数 -1

在wxPython 4中,有没有一种方法可以识别两次按下相同的键?特别是在我的例子中,我想要识别当快速连续按下SHIFT键两次时的情况。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2018-08-08 03:40:48

下面是一个更通用的解决方案

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

EVT_CHAR_HOOK面临的挑战是确定按键实际上是按了两次还是只按了两次。因此,RawKeyFlags被读出。位置30处的比特指示密钥是否被持有。

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

代码语言:javascript
复制
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()
票数 0
EN

Stack Overflow用户

发布于 2018-08-07 01:31:21

这一点并不像乍看起来那么明显。

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

我把它稍微复杂化了,允许使用Shift以外的键,而名称字典只是为了测试。

我应该指出的是,因为它必须检查每个键按键,所以效率不是很高,但我想你已经意识到了这一点,并愿意为此付出代价。

我确实有一个警告,我不知道在非Linux机器上按住一个键,比如Shift键,会有什么反应。如果事实证明它不像Linux,那么您应该将Bindwx.EVT_KEY_DOWN改为wx.EVT_KEY_UP

代码语言:javascript
复制
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上的第一个问题,而且你们似乎对评论有点不满。

如果你不提供你的代码,工作或失败的痛苦,你将吸引下投票。论坛的成员喜欢看到代码,特别是你已经尝试过的代码。它基本上只是一个基本的指标,表明你是否花了任何努力回答自己的问题,然后抛出一个一行的问题,并希望其他人为你做腿上的工作。

我最近发布了一个自我回答的问题,问题和答案放在一起。尽管我对我自己的问题提供了详细的编码答案,但我立即对这个问题投了反对票。所以不要把它放在心上。我怀疑有些人只是通过严格遵守“规则”来获得乐趣。

也就是说,如果你坚持一段时间,自己回答问题,你就会开始看到问题和MCVE中代码的优点。你会惊讶于有些人发布了什么,期待着一个答案。

票数 1
EN

Stack Overflow用户

发布于 2018-08-06 20:28:26

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

代码语言:javascript
复制
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()
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51703466

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档