Python tkinter binding: how to prevent double events

I have a python program that uses tkinter and key bindings. The program calls the external program and then asks what to do with it, expecting n-key for "no" or y-key for "yes". Problem: If I double-click the allowed key, the second key is saved and processed later - thus for the wrong question.

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()

      

In the above code snippet, I replaced the external sleep () program and key bindings to space. If the python3 script run is started and the spacebar is pressed twice during the timeout period, I see outputs in the terminal, for example:

Step 1
Step  2
Step 1
Step  2

      

I would like to ignore any key event that was raised during the sleep routine. So I tried using a semaphore, but that also failed. The problem can be more complicated because if I run the space three times during sleep, I get the following result:

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

      

It looks like the function from the keyboard is being called multiple times in parallel. Is it possible to clear the event queue before accepting new events?

+3


source to share


2 answers


This requires one of the few known features of Tkinter, i.e. you need to return "break" from the function. The program below returns the key to the "ignore ()" function, which returns "break". I don’t understand how or why this happens and it is documented http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm and it works.



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()

      

+3


source


The time.sleep method is probably messing with the main Tkinter window. You must use the "after" method from the Tkinter module.

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

If you do not pass a callback argument, this method expects delay_ms for milliseconds, as in the .sleep () function of the standard Python timing unit.



Check this post for an example:

Python time.sleep

+1


source







All Articles