I\'m using a spinbox to control the size of fonts in real time, for a zooming effect. A spinbox widget can generate a lot of events. As long as you hold down a direction key
I used similar solutions for both kinds of spinboxes but the implementation is a bit different since they don't use the same events. The idea is
to create a Spinbox class with an _increment_lock
attribute which is set to True
when the Spinbox is incremented and after a delay is set back to False
. Then, the events that increment the spinbox are bound to a method that checks _increment_lock
before actually performing the increment. The principle is the same for the decrement.
For the tk.Spinbox
, I used the bindings to the <Up>
and <Down>
arrows to implement the above solution while I used the bindings to <<Increment>>
and <<Decrement>>
.
Here is the code:
import tkinter as tk
import tkinter.ttk as ttk
class MySpinbox(tk.Spinbox):
def __init__(self, master=None, delay=500, **kwargs):
kwargs.setdefault('repeatdelay', delay)
kwargs.setdefault('repeatinterval', delay)
tk.Spinbox.__init__(self, master, **kwargs)
self.delay = delay # repeatdelay in ms
self.bind('<Up>', self._on_increment)
self.bind('<Down>', self._on_decrement)
self._increment_lock = False
self._decrement_lock = False
def _unlock_increment(self):
self._increment_lock = False
def _on_increment(self, event):
if self._increment_lock:
return "break" # stop the increment
else:
self._increment_lock = True
self.after(self.delay, self._unlock_increment)
def _unlock_decrement(self):
self._decrement_lock = False
def _on_decrement(self, event):
if self._decrement_lock:
return "break" # stop the increment
else:
self._decrement_lock = True
self.after(self.delay, self._unlock_decrement)
class MyTtkSpinbox(ttk.Spinbox):
def __init__(self, master=None, delay=500, **kwargs):
ttk.Spinbox.__init__(self, master, **kwargs)
self.delay = delay # repeatdelay in ms
self.bind('<<Increment>>', self._on_increment)
self.bind('<<Decrement>>', self._on_decrement)
self._increment_lock = False
self._decrement_lock = False
def _unlock_increment(self):
self._increment_lock = False
def _on_increment(self, event):
if self._increment_lock:
return "break" # stop the increment
else:
# generate a virtual event corresponding to when the spinbox
# is actually incremented
self.event_generate('<<ActualIncrement>>')
self._increment_lock = True
self.after(self.delay, self._unlock_increment)
def _unlock_decrement(self):
self._decrement_lock = False
def _on_decrement(self, event):
if self._decrement_lock:
return "break" # stop the increment
else:
# generate a virtual event corresponding to when the spinbox
# is actually decremented
self.event_generate('<<ActualDecrement>>')
self._decrement_lock = True
self.after(self.delay, self._unlock_decrement)
class App(tk.Tk):
def __init__(self):
super().__init__()
self.rowconfigure(990, weight=1)
self.columnconfigure(0, weight=1)
self.title('Timed Events Demo')
self.geometry('420x200+20+20')
tk_spn1 = MySpinbox(self, value=0, values=list(range(0, 1000)))
tk_spn1.grid(row=0, pady=5)
tk_spn2 = MyTtkSpinbox(self, from_=0, to=1000)
tk_spn2.grid(row=1, pady=5)
def test(e):
print(e)
tk_spn2.bind('<<ActualIncrement>>', test)
if __name__ == '__main__':
app = App()
app.mainloop()