Reputation: 31
I currently have a button in tkinter to run a function when the button is released. I need the button to constantly add toa number at a certain rate the entire time the button is being held.
global var
var=1
def start_add(event,var):
global running
running = True
var=var+1
print(var)
return var
def stop_add(event):
global running
print("Released")
running = False
button = Button(window, text ="Hold")
button.grid(row=5,column=0)
button.bind('<ButtonPress-1>',start_add)
button.bind('<ButtonRelease-1>',stop_add)
i dont necessarily need any function to run when the button is released, just while the button is being held if this helps. Any help is much appreciated.
Upvotes: 3
Views: 1657
Reputation: 13729
There is nothing builtin that can do this, but it would be easy to make your own Button that can. You are on the right track too, only thing you are missing is that you need to use after
to make the loop and after_cancel
to stop the loop:
try:
import tkinter as tk
except ImportError:
import Tkinter as tk
class PaulButton(tk.Button):
"""
a new kind of Button that calls the command repeatedly while the Button is held
:command: the function to run
:timeout: the number of milliseconds between :command: calls
if timeout is not supplied, this Button runs the function once on the DOWN click,
unlike a normal Button, which runs on release
"""
def __init__(self, master=None, **kwargs):
self.command = kwargs.pop('command', None)
self.timeout = kwargs.pop('timeout', None)
tk.Button.__init__(self, master, **kwargs)
self.bind('<ButtonPress-1>', self.start)
self.bind('<ButtonRelease-1>', self.stop)
self.timer = ''
def start(self, event=None):
if self.command is not None:
self.command()
if self.timeout is not None:
self.timer = self.after(self.timeout, self.start)
def stop(self, event=None):
self.after_cancel(self.timer)
#demo code:
var=0
def func():
global var
var=var+1
print(var)
root = tk.Tk()
btn = PaulButton(root, command=func, timeout=100, text="Click and hold to repeat!")
btn.pack(fill=tk.X)
btn = PaulButton(root, command=func, text="Click to run once!")
btn.pack(fill=tk.X)
btn = tk.Button(root, command=func, text="Normal Button.")
btn.pack(fill=tk.X)
root.mainloop()
As @rioV8 mentioned, the after()
call is not extremely accurate. If you set the timeout to 100 milliseconds, you can usually expect anywhere from 100 - 103 milliseconds between calls. These errors will stack up the longer the button is held. If you are trying to time exactly how long the button has been held down you will need a different approach.
Upvotes: 3
Reputation: 2711
@Novel's answer should work I think, but here is something more along the lines of what you were trying, that doesn't require a whole new class:
from tkinter import *
INTERVAL=5 #miliseconds between runs
var=1
def run():
global running, var
if running:
var+=1
print(var)
window.after(INTERVAL, run)
def start_add(event):
global running
running = True
run()
def stop_add(event):
global running, var
print("Released")
running = False
window=Tk()
button = Button(window, text ="Hold")
button.grid(row=5,column=0)
button.bind('<ButtonPress-1>',start_add)
button.bind('<ButtonRelease-1>',stop_add)
mainloop()
Upvotes: 0