【问题标题】:TkInter keypress, keyrelease eventsTkInter 按键、按键释放事件
【发布时间】:2015-01-28 16:29:30
【问题描述】:

我知道 Tk keypress 和 keyrelease 事件应该只在实际按下或释放键时触发?

但是,使用以下简单代码,如果我按住“a”键,我会得到一系列交替的按键/按键释放事件。

是我做错了什么还是 TkInter 有问题?这是 Linux mint 上的 Python2.7。

from Tkinter import *
def keyup(e):
    print 'up', e.char
def keydown(e):
    print 'down', e.char

root = Tk()
frame = Frame(root, width=100, height=100)
frame.bind("<KeyPress>", keydown)
frame.bind("<KeyRelease>", keyup)
frame.pack()
frame.focus_set()
root.mainloop()

按住“a”时的输出:

down a
up a
down a
up a
down a
up a
down a
up a
etc...

【问题讨论】:

    标签: python tkinter


    【解决方案1】:

    好的,更多研究发现this helpful post 表明这是由于 X 的自动重复行为而发生的。您可以使用禁用此功能

    os.system('xset r off')
    

    然后在脚本末尾使用“on”重置它。 问题是这是全局行为——不仅仅是我的脚本——这不是很好,所以我希望有人能想出更好的方法。

    【讨论】:

      【解决方案2】:

      自动重复行为取决于系统。在 Win7 中,

      down a
      down a
      down a
      ...
      down a
      up a
      

      这不到一秒钟。

      【讨论】:

        【解决方案3】:

        嗯,现在有点晚了,但我有一个可行的解决方案。这不是很好,但它不需要 os.system 覆盖系统设置,这很好。

        基本上,我创建了一个记录按键时间的类。我说一个键在最后很短的时间内(这里是 0.1 毫秒)被按下时就被按下了。要获得按下,很容易:如果键未注册为按下,则触发事件。对于发布,逻辑更难:如果有可疑的发布事件,设置一个短时间的计时器(这里是 0.1 秒),然后检查以确保密钥没有关闭。

        验证按下或释放后,请在代码中调用 on_key_press 或 on_key_release 方法。至于那些,只需按照您最初想要的方式实现它们即可

        我知道这并不完美,但我希望它有所帮助!

        代码如下:

        您在哪里初始化按键事件:

        key_tracker = KeyTracker()
        window.bind_all('<KeyPress>', key_tracker.report_key_press)
        window.bind_all('<KeyRelease>', key_tracker.report_key_release)
        key_tracker.track('space')
        

        这是我的自定义 KeyTracker 类:

        class KeyTracker():
            key = ''
            last_press_time = 0
            last_release_time = 0
        
            def track(self, key):
                self.key = key
        
            def is_pressed(self):
                return time.time() - self.last_press_time < .1
        
            def report_key_press(self, event):
                if event.keysym == self.key:
                    if not self.is_pressed():
                        on_key_press(event)
                    self.last_press_time = time.time()
        
            def report_key_release(self, event):
                if event.keysym == self.key:
                    timer = threading.Timer(.1, self.report_key_release_callback, args=[event])
                    timer.start()
        
            def report_key_release_callback(self, event):
                if not self.is_pressed():
                    on_key_release(event)
                self.last_release_time = time.time()
        

        【讨论】:

        • 哈哈,基本上是软件去抖动!这是一个好主意,尽管我们必须这样做似乎很疯狂
        【解决方案4】:

        怎么样;

        from Tkinter import *
        
        wn = Tk()
        wn.title('KeyDetect')
        
        m = 0
        
        def down(e):
            if m == 0:
                print 'Down\n', e.char, '\n', e
                global m
                m = 1
        
        def up(e):
            if m == 1:
                print 'Up\n', e.char, '\n', e
                global m
                m = 0
        
        wn.bind('<KeyPress>', down)
        wn.bind('<KeyRelease>', up)
        
        wn.mainloop()
        

        现在它不会重复了。

        【讨论】:

        • 这并不能解决问题,即在密钥实际释放之前调用了两个处理程序。你开始按住一个键,它会调用 Press,然后是 Release,然后是 Press,然后是 Release,重复直到你松开。
        【解决方案5】:

        诀窍是跟踪有一个键按下的事实、当前按下的键以及不再按下某个键的事实。一直忽略键盘中继器。

        这个小原型应该涵盖所有的基础:

        #Key press prototype
        #Tracks keys as pressed, ignoring the keyboard repeater
        #Current keys down are kept in a dictionary.
        #That a key is pressed is flagged, and the last key pressed is tracked
        
        import tkinter
        
        winWid = 640
        winHei = 480
        keyDown = False
        lastKey = "none"
        keyChange = keyDown
        keyList = {}
        
        def onKeyDown(event):
            global keyDown, lastKey, keyList
            if (event.char in keyList) != True:
                keyList[event.char] = "down"
                print(keyList)
            keyDown = True
            lastKey = event.char
        
        def onKeyUp(event):
            global keyDown
            if (event.char in keyList) == True:
                keyList.pop(event.char)
            if len(keyList) == 0:
                keyDown = False
            print(keyList)
            
        #onTimer is present to show keyboard action as it happens. 
        #It is not needed to track the key changes, and it can be 
        #removed.
        def onTimer(): 
            global keyChange, timerhandle
            if keyDown != keyChange:
                keyChange = keyDown
                if keyDown:
                    print("Key down, last key pressed - " + lastKey)
                else:
                    print("Key up, last key pressed - " + lastKey)
            timerhandle = window.after(20,onTimer)
            
        def onShutdown():
            window.after_cancel(timerhandle)
            window.destroy()    
        
        window = tkinter.Tk()
        frame = tkinter.Canvas(window, width=winWid, height=winHei, bg="black")
        frame.pack()
        
        frame.bind("<KeyPress>", onKeyDown)
        frame.bind("<KeyRelease>", onKeyUp)
        frame.focus_set()
        
        timerhandle = window.after(20,onTimer)
        window.protocol("WM_DELETE_WINDOW",onShutdown)
        window.mainloop()
        

        【讨论】:

        • 这看起来和上面的“软件去抖”程序基本一样?我看不到消耗 CPU 周期来对抗自动重复功能特别优雅!
        • 同样的基本思想,是的。这个版本更适合观众中的新手。它避免了类结构和线程库。您需要的唯一导入是 tkinter。时间后事件的出现只是为了显示键盘中发生的状态变化。密钥跟踪不需要它来工作,没有它,这对处理器来说非常轻。
        【解决方案6】:

        如果您只想跟踪一个键,您可以这样做:

        import tkinter as tk
        
        class KeyTracker:
            def __init__(self, on_key_press, on_key_release):
                self.on_key_press = on_key_press
                self.on_key_release = on_key_release
                self._key_pressed = False
        
            def report_key_press(self, event):
                if not self._key_pressed:
                    self.on_key_press()
                self._key_pressed = True
        
            def report_key_release(self, event):
                if self._key_pressed:
                    self.on_key_release()
                self._key_pressed = False
        
        
        def start_recording(event=None):
            print('Recording right now!')
        
        
        def stop_recording(event=None):
            print('Stop recording right now!')
        
        
        if __name__ == '__main__':
            master = tk.Tk()
        
            key_tracker = KeyTracker(start_recording, stop_recording)
            master.bind("<KeyPress-Return>", key_tracker.report_key_press)
            master.bind("<KeyRelease-Return>", key_tracker.report_key_release)
            master.mainloop()
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-05-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-11-12
          • 1970-01-01
          相关资源
          最近更新 更多