Skip to content Skip to sidebar Skip to footer

Tkinter Unbinding Key Event Issue

In the code below, pressing the space bar twice results in two successive beeps. I want to avoid this and instead disable the key while the first beep is happening. I thought unbin

Solution 1:

Here is a solution that takes the focus away from the widget, so the binding wont get triggered:

import winsound
from tkinter import *

def beep(event):
    dummy.focus_set() #setting focus to dummy
    winsound.Beep(440, 1000) #playing it 
    root.after(1000,frame.focus_set) #setting focus back after playing for 1000 ms

root = Tk()

dummy = Label() #making a dummy widget
dummy.pack()
frame = Frame(root, width=100, height=100)
frame.bind("<space>",beep)
frame.pack()
frame.focus_set()

root.mainloop()

I've commented it to understand better, but this is just a way around and its not that complicated to understand either.

Also keep in mind, in all cases of using winsound, as long as that beep has started and finished playing, the GUI will be unresponsive, that is, GUI will be unresponsive for 1 sec(in your case).

Solution 2:

This should fix it however you have to download keyboard module with pip install keyboard :

import winsound
from tkinter import *
import keyboard
from _thread import start_new_thread

def beep():
    while True:
        if keyboard.is_pressed('space'):
            winsound.Beep(440, 1000)

root = Tk()

frame = Frame(root, width=100, height=100)
start_new_thread(beep, ())
frame.pack()
frame.focus_set()

root.mainloop()

First start_new_thread()(syntax is important) makes beep() threaded (runs in background?) and its a while loop so it runs continuously and whenever you press space it will beep and even if you spam space it will still just run one beep. However there is a downside. It will run while script is not terminated so if you focus out it will still beep if you press spacebar

Solution 3:

You can use the elapsed time since the last successful keypress to decide if the beep should be produced or not.

maybe like this: I do not have access to winsound, so I am using an os feature to mimic a beep. You can comment this out, and uncomment the calls to winsound

# import winsoundimport os
import tkinter as tk
import time

             
defbeep(e, time_limit=1, timer=[0]):
    t0 = timer[0]
    t1 = time.time()
    delta_t = t1 - t0
    if delta_t < time_limit:
        return#     winsound.Beep(440, 1000)
    os.system('say "Beep"')    
    timer[0] = t1
    

root = tk.Tk()
frame = tk.Frame(root, width=100, height=100)
frame.bind("<space>", beep)
frame.pack()
frame.focus_set()
root.mainloop()

Solution 4:

You can bind back the event via after_idle():

defbeep(e):
    e.widget.unbind('<space>')
    winsound.Beep(440, 1000)
    e.widget.after_idle(e.widget.bind, '<space>', beep)

explanation:

The callback passed to after_idle() will be executed when tkinter mainloop is idle, i.e. no pending works/events to be handled. So if the spacebar is pressed many times, the first press triggers beep() in which tkinter unbinds the event, beep and then schedules the rebind. After beep() returns, tkinter keeps handling the pending tasks, i.e. handle the rest spacebar events (but at that moment, no bind is active) and then do the after_idle schedule task which is the rebind.

Post a Comment for "Tkinter Unbinding Key Event Issue"