Reputation: 375
So, i have a LED controlled by a Raspy, and also a basic GUI with just a push button, I want it to behave as follows:
When I execute light starts blinking, but the button does not show up. When I interrupt the program, the button shows up. Why?
from tkinter import *
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BOARD)
GPIO.setup(8, GPIO.OUT)
GPIO.output(8, False)
buttonPressed = False
master = Tk()
def callback():
GPIO.output(8, True)
buttonPressed = True
w = Button(master, text="Turn on light", command = callback)
w.pack()
while True:
if buttonPressed == False:
GPIO.output(8, True)
time.sleep(0.5)
GPIO.output(8, False)
time.sleep(0.5)
else:
GPIO.output(8, True)
Upvotes: 1
Views: 1601
Reputation: 46669
A tkinter
application needs to call .mainloop()
.
Also you should use .after(...)
function to simulate the while loop:
import tkinter as tk
import RPi.GPIO as GPIO
def led_on(state):
global button_pressed
button_pressed = state
def blink_led(state=True):
# turn on LED if either state or button_pressed is True
GPIO.output(8, state or button_pressed)
master.after(500, blink_led, not state) # toggle the state half a second later
# setup the RPi board
GPIO.setmode(GPIO.BOARD)
GPIO.setup(8, GPIO.OUT)
GPIO.output(8, False)
button_pressed = False
master = tk.Tk()
w = tk.Button(master, text='Turn on light')
w.pack()
w.bind('<ButtonPress-1>', lambda e: led_on(True)) # set button_pressed to True
w.bind('<ButtonRelease-1>', lambda e: led_on(False)) # set button_pressed to False
blink_led() # start the LED blinking
master.mainloop()
Upvotes: 3
Reputation: 142631
I can't test it on RPi but I could be something like this.
Normal command=
can recognize only when you clicked button but it can't recognize when you released it - you need to bind events <ButtonPress>
and <ButtonRelease>
which will run functions on "mouse left button press"
and "mouse left button release"
I use after()
to run function with delay so I don't need sleep()
and while
which can block mainloop()
(and it can freeze all GUI).
And I don't need also while
loop because I run all inside on_press
and on_release
and later after()
runs turn_off_led
which use after()
to run turn_on_led
which use after()
to run again turn_off_led
so it works like loop.
import tkinter as tk # PEP8: `import *` is not preferred
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setup(8, GPIO.OUT)
GPIO.output(8, False)
button_pressed = False # PEP8: preferred `lower_case_names`
# --- functions ---
def on_press(event):
global button_pressed
button_pressed = True
GPIO.output(8, True)
def on_release(event):
global button_pressed
button_pressed = False
GPIO.output(8, True)
# run after 500ms (0.5s) instead of `sleep`
master.after(500, blink_off)
def blink_off():
if not button_pressed:
GPIO.output(8, False)
# run after 500ms (0.5s) instead of `sleep`
master.after(500, blink_on)
def blink_on():
if not button_pressed:
GPIO.output(8, True)
# run after 500ms (0.5s) instead of `sleep`
master.after(500, blink_off)
# --- main ---
master = tk.Tk()
button = tk.Button(master, text="Turn on light")
button.pack()
# here "button" means "tk.Button" and "Button" means "mouse left button"
button.bind('<ButtonPress>', on_press) # mouse left button pressed on tk.Button
button.bind('<ButtonRelease>', on_release) # mouse left button released on tk.Button
# start blinking - it will use `after()` to loop
blink_on()
master.mainloop()
Upvotes: 2
Reputation: 311238
I think that @furas has you set in the right direction, but since I was working on it for the fun of it I thought I would leave my solution here as well. I pulled out the GPIO code so that I could run it locally.
A key feature of both solutions is that we've removed the calls to time.sleep
, because when you're time.sleep
ing, Tk's event loop isn't able to process any events (which would manifest as the UI appearing to "freeze" during those sleep statements).
import time
import tkinter
buttonpressed = False
lastchange = 0
ledstate = False
def button_down(event):
global buttonpressed
print('BUTTON DOWN')
buttonpressed = True
def button_up(event):
global buttonpressed
print('BUTTON UP')
buttonpressed = False
def myloop(master):
global buttonpressed
global lastchange
global ledstate
now = time.time()
delta = now - lastchange
if not buttonpressed:
if now - lastchange > 0.5:
ledstate = not ledstate
print('LED', ledstate)
lastchange = now
else:
if not ledstate:
ledstate = True
print('LED', ledstate)
master.after_idle(myloop, master)
master = tkinter.Tk()
w = tkinter.Button(master, text="Turn on light")
w.bind('<ButtonPress>', button_down)
w.bind('<ButtonRelease>', button_up)
w.pack()
master.after(100, myloop, master)
master.mainloop()
Upvotes: 3