Python TkInter按键,按键释放事件

Python TkInter按键,按键释放事件,python,tkinter,Python,Tkinter,我知道Tk keypress和keyrelease事件应该只在实际按下或释放按键时触发 然而,对于下面的简单代码,如果我按下“a”键,我会得到一系列连续的交替按键/按键释放事件 是我做错了什么还是TkInter车坏了?这是LinuxMint上的Python2.7 from Tkinter import * def keyup(e): print 'up', e.char def keydown(e): print 'down', e.char root = Tk() frame

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

然而,对于下面的简单代码,如果我按下“a”键,我会得到一系列连续的交替按键/按键释放事件

是我做错了什么还是TkInter车坏了?这是LinuxMint上的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()

好的,更多的研究发现这是因为X的自动回复行为。您可以使用

os.system('xset r off')
然后在脚本末尾使用“on”重置它。
问题是这是全局行为,而不仅仅是我的脚本,这并不好,所以我希望有人能想出更好的方法。

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

down a
down a
down a
...
down a
up a
这不到一秒钟。

怎么样

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()
从Tkinter导入*
wn=Tk()
wn.title('KeyDetect')
m=0
def下降(e):
如果m==0:
打印'Down',e.char',\n',e
全球m
m=1
def up(e):
如果m==1:
打印'Up\n',e.char',\n',e
全球m
m=0
wn.bind(“”,向下)
wn.绑定(“”,向上)
wn.mainloop()

现在它不会重复了。

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

基本上,我制作了一个记录按键时间的类。我说,当一个键在最近的短时间内被按下时(这里是,.1ms)。要按下按钮,很容易:如果按键未注册为按下,则触发事件。对于释放,逻辑更难:如果有可疑的释放事件,则设置一个短时间的计时器(此处为0.1s),然后检查以确保键未按下

验证按下或释放后,在代码中调用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')
key\u tracker=KeyTracker()
窗口。绑定所有(“”,键跟踪。报告键按)
window.bind_all(“”,key_tracker.report_key_release)
钥匙跟踪器轨迹(“空格”)
这是我的自定义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()
class KeyTracker():
键=“”
上次按下时间=0
上次发布时间=0
def轨迹(自、钥匙):
self.key=key
按下def(自):
返回时间.time()-self.last_按时间<.1
def报告键按(自身,事件):
如果event.keysym==self.key:
如果不是self.is_pressed():
按按键(事件)
self.last_press_time=time.time()
def报告钥匙释放(自身、事件):
如果event.keysym==self.key:
timer=threading.timer(.1,self.report\u key\u release\u callback,args=[event])
timer.start()
def报告\按键\释放\回调(自我,事件):
如果不是self.is_pressed():
按键释放时(事件)
self.last\u release\u time=time.time()

诀窍在于跟踪一个键有一个键按下的事实、当前按下的键以及不再按下键的事实。同时忽略键盘中继器

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

#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()
#按键原型
#在按下时跟踪按键,忽略键盘中继器
#当前的向下键保存在字典中。
#将标记按下的键,并跟踪最后按下的键
进口tkinter
winWid=640
温黑=480
keyDown=False
lastKey=“无”
keyChange=keyDown
keyList={}
def onKeyDown(事件):
全局键关闭、最后一个键、键列表
如果(键列表中的event.char)!=正确:
keyList[event.char]=“向下”
打印(密钥列表)
keyDown=True
lastKey=event.char
def onKeyUp(事件):
全局键控
如果(键列表中的event.char)=True:
keyList.pop(event.char)
如果len(keyList)==0:
keyDown=False
打印(密钥列表)
#onTimer出现时显示键盘操作。
#不需要跟踪关键更改,而且可以
#删除。
def onTimer():
全局键更改,timerhandle
如果按下键!=关键字更改:
keyChange=keyDown
如果按下键:
打印(“向下键,最后一个按下键-”+最后一个键)
其他:
打印(“向上键,最后一个按下的键-”+最后一个键)
timerhandle=window.after(20,onTimer)
def onShutdown():
取消后的窗口(timerhandle)
window.destroy()
window=tkinter.Tk()
frame=tkinter.Canvas(窗口,宽度=winWid,高度=winHei,bg=“黑色”)
frame.pack()
frame.bind(“”,onKeyDown)
frame.bind(“,onKeyUp)
frame.focus_set()
timerhandle=window.after(20,onTimer)
协议(“WM_删除_窗口”,onShutdown)
window.mainloop()

这并不能解决这个问题,即在实际释放密钥之前调用两个处理程序。你开始按住一个键,它会叫按,然后释放,然后按,然后释放,反复直到你放开。哈,基本上是软件去抖动!这是一个很好的主意,尽管我们不得不这么做似乎很疯狂,但这似乎与上面的“软件去盎司”程序基本相同?我看不到燃烧CPU周期来对抗自动恢复功能特别优雅!同样的基本想法,是的。这个版本更适合观众中的新手。它避免了类结构和线程库。您需要的唯一导入是tkinter。timed after事件的出现只是为了显示键盘中发生的状态变化。它不需要关键点跟踪工作,没有它,这是相当轻的处理器。
#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()