Python – Attempts to keep the function running while holding down the tkinter button

Attempts to keep the function running while holding down the tkinter button… here is a solution to the problem.

Attempts to keep the function running while holding down the tkinter button

I currently have a button in tkinter that runs a function when the button is released. I need buttons to be constantly added to a number at a certain rate throughout the holding of the button.

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 don’t necessarily need to run any function when the button is released, just run while holding down the button if it helps. Any help is greatly appreciated.

Solution

There’s no built-in thing to do this, but making your own Button can be easy. You’re also on the right track, the only thing you’re missing is that you need to use after to make loops and after_cancel to stop loops:

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 mentioned in @rioV8, the after() call is not very accurate. If you set the timeout to 100 milliseconds, you can typically expect an interval of 100 – 103 milliseconds between calls. The longer you hold down the button, the more these errors accumulate. If you want to accurately calculate when a button is pressed, you need a different method.

Related Problems and Solutions