2
votes

I'm trying to do a GUI in python to control my robotic car. My question is how I do a function that determine a hold down button. I want to move the car when the button is pressed and held down and stop the car when the button is released.

from Tkinter import * 

hold_down = False 
root = Tk()

def button_hold(event):
      hold_down=true
      while hold_down== True: 
               print('test statement')
               hold_down = root.bind('<ButtonRelease-1>',stop_motor)

def stop_motor(event):
       hold_down= False
       print('button released')

button = Button(root, text ="forward")
button.pack(side=LEFT)
root.bind('<Button-1>',button_forward)
root.mainloop()

I'm trying to simulate what I found in this answer

I try to do it in a while loop with a boolean. When the user presses the button the boolean changes to True and code enters the while loop. When user releases the button the boolean changes to False and code exits from loop but in this code the boolean stay always true no matter if I released the button or not.

Edit: I want a function to be called until a condition occurs.The function to be called is hold_down() and the condition to check is the button is released.

Update: I found a way to make it work.

5
can you share your solution here? I have the same problem. - USC.Trojan

5 Answers

10
votes

Set a flag when the button is pressed, unset the flag when the button is released. There's no need for a loop since you're already running a loop (mainloop)

from Tkinter import * 
running = False
root = Tk()
def start_motor(event):
    global running
    running = True
    print("starting motor...")

def stop_motor(event):
    global running
    print("stopping motor...")
    running = False

button = Button(root, text ="forward")
button.pack(side=LEFT)
button.bind('<ButtonPress-1>',start_motor)
button.bind('<ButtonRelease-1>',stop_motor)
root.mainloop()

Assuming that you actually want to do something while the key is pressed, you can set up an animation loop using after. For example, to call a print statement once a second while the button is pressed you can add a function that does the print statement and then arranges for itself to be called one second later. The stop button merely needs to cancel any pending job.

Here's an example. The main difference to the original code is the addition of a move function. I also added a second button to show how the same function can be used to go forward or backward.

from Tkinter import * 
running = False
root = Tk()
jobid = None

def start_motor(direction):
    print("starting motor...(%s)" % direction)
    move(direction)

def stop_motor():
    global jobid
    root.after_cancel(jobid)
    print("stopping motor...")

def move(direction):
    global jobid
    print("Moving (%s)" % direction)
    jobid = root.after(1000, move, direction)

for direction in ("forward", "backward"):
    button = Button(root, text=direction)
    button.pack(side=LEFT)
    button.bind('<ButtonPress-1>', lambda event, direction=direction: start_motor(direction))
    button.bind('<ButtonRelease-1>', lambda event: stop_motor())

root.mainloop()
1
votes

You might want to try the repeatinterval option. The way it works is a button will continually fire as long as the user holds it down. The repeatinterval parameter essentially lets the program know how often it should fire the button if so. Here is a link to the explanation:

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

Search in-page for "repeatinterval".

Another name for this parameter is repeatdelay.

1
votes

Building on Bryan Oakley's answer of using flags to simulate a press and hold button. The problem is that you can't have any while loops in your tkinter application to say while running move car forward.

Which is why I suggest using threads. This way you can have a while loop running in the background checking if the car should be moving foward.

from threading import Thread
from Tkinter import *    

running = False
root = Tk()

def start_motor(event):
    global running
    print("starting motor...")
    running = True

def stop_motor(event):
    global running
    running = False
    print("stopping motor...")

def move_forward():
    while True: # Thread will run infinitely in the background
        if running:
            print("Car is moving forward...\n")

button = Button(root, text ="forward")
button.pack(side=LEFT)
button.bind('<ButtonPress-1>',start_motor)
button.bind('<ButtonRelease-1>',stop_motor)

# Create and start the new thread
t = Thread(target = move_forward, args = ())
t.start()

root.mainloop()
0
votes

Try this...

from Tkinter import *  
root = Tk()
global hold_down

def button_hold(event):
    hold_down = True
    while hold_down: 
        print('test statement')

def stop_motor(event):
    hold_down = False
    print('button released')

button = Button(root, text ="forward")
button.pack(side=LEFT)
root.bind('<Button-1>',button_hold)
root.bind('<ButtonRelease-1>',stop_motor)
root.mainloop()
-1
votes

@ Danny Try the following code:

def stop_motor(event): print('button released') return False

This answer run print 'test statement' one time. The while loop run one time when the button is pressed.

@ Bryan Oakley Set a flag when the button is pressed, unset the flag when the button is released. There's no need for a loop since you're already running a loop (mainloop)

from Tkinter import * 
running = False

root = Tk()
def start_motor(event):
    global running
    running = True
    print("starting motor...")

def stop_motor(event):
    global running
    print("stopping motor...")
    running = False

button = Button(root, text ="forward")
button.pack(side=LEFT)
root.bind('<ButtonPress-1>',start_motor)
root.bind('<ButtonRelease-1>',stop_motor)
root.mainloop()

This answer above stays in a infinite loop when the button is pressed.

@ Joseph FarahYou might want to try the repeatinterval option. The way it works is a button will continually fire as long as the user holds it down. The repeatinterval parameter essentially lets the program know how often it should fire the button if so. Here is a link to the explanation:

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

Search in-page for "repeatinterval".

Another name for this parameter is repeatdelay.

I set the repeat interval in the parameter option for the button widget but it doesn't repeat the command.

Thanks for all the answer . Still looking to solve this problem.