python tkinter绑定:如何防止双重事件

时间:2015-05-16 08:53:49

标签: python tkinter keyboard-events key-bindings

我有一个使用tkinter和键绑定的python程序。该程序调用外部程序,然后询问,如何处理它,期望n键为" no"或者是"是"的y键。问题:如果我按下允许的键两次,则第二个键将被存储并稍后处理 - 因此出现了错误的问题。

import tkinter as tk
import time

class App:
    counter = 0

    def __init__(self, master): # Constructor
        # build GUI
        self.label = tk.Label(text="Press <space>", width=40)
        self.label.grid(row=1, column=1, sticky='w')

        # bind keys to buttons
        master.bind('<Key-space>', self.keyPressed)
        pass # __init__


    def keyPressed(self, event):
        print("Step 1")
        self.counter = self.counter + 1
        self.label.configure(text = str(self.counter))
        self.label.update()
        time.sleep(3)
        print("Step  2")
        pass # Key1Pressed
# End of class App

root = tk.Tk()
root.option_add('*font', ('Arial', 11, 'bold'))
# root.attributes("-toolwindow", 1)

posX = root.winfo_screenwidth() - 500
posY = 30
root.geometry("+%d+%d" % (posX, posY))

root.title("Bind tester")
display = App(root)
root.mainloop()

在上面的代码片段中,我用sleep()替换了外部程序,并用空格键替换了键。如果在睡眠期间启动了python3 skript并且按下了两次空间,我会看到终端中的输出如下:

Step 1
Step  2
Step 1
Step  2

我想忽略在睡眠例程中引发的任何关键事件。接下来我尝试使用信号量,但这也没有成功。问题可能更复杂,因为如果我在睡眠期间按空格三次,我将得到以下结果:

Step 1
Step  2
Step 1
Step 1
Step  2
Step  2

看起来好像并行多次调用函数keypressed。是否有可能在接受新事件之前清除事件队列?

2 个答案:

答案 0 :(得分:3)

这需要Tkinter的一个鲜为人知的怪癖,即你必须从一个函数返回“break”。下面的程序将键重新绑定到返回“break”的“ignore()”函数。我不明白这是怎么或为什么,并且记录在案http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm并确实有效。

import sys
if sys.version_info[0] < 3:
    import Tkinter as tk     ## Python 2.x
else:
    import tkinter as tk     ## Python 3.x

class App:
    def __init__(self, master): # Constructor
        self.master=master
        self.counter=0
        # build GUI
        self.label = tk.Label(text="Press <space>", width=40)
        self.label.grid(row=1, column=1, sticky='w')

        # bind keys to buttons
        master.bind('<Key-space>', self.keyPressed)


    def ignore(self, event):
        print "ignore"
        return "break"

    def keyPressed(self, event):
        self.master.bind('<Key-space>', self.ignore)
        print("Step 1")
        self.counter = self.counter + 1
        self.label["text"] = str(self.counter)
        self.master.after(3000, self.bindit)
        print("Step  2")

    def bindit(self):
        self.master.bind('<Key-space>', self.keyPressed)
        print("Step 3 = ready for more input")

root = tk.Tk()
root.option_add('*font', ('Arial', 11, 'bold'))
# root.attributes("-toolwindow", 1)

posX = root.winfo_screenwidth() - 500
posY = 30
root.geometry("+%d+%d" % (posX, posY))

root.title("Bind tester")
display = App(root)
root.mainloop()

答案 1 :(得分:1)

可能time.sleep方法正在搞乱Tkinter主循环。 您应该使用&#39;之后&#39;来自Tkinter模块的方法。

http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/universal.html

  

如果你没有传递一个回调参数,这个方法会等待delay_ms毫秒,就像在标准Python时间模块的.sleep()函数中一样。

查看此帖子的示例:

Python time.sleep