Python – How do I continuously repeat a sequence without a While loop and still be able to stop the sequence at any time

Question:

I have a Raspberry Pi with the Piface adaptor board. I have made a GUI which controls the LED’s on the Piface board.
I wrote a small piece of code to make the LED’s run up and down continuously, like Knight Riders car, using a While loop.
I then wrote another piece of code that created a GUI. In the GUI is a button that starts the LED’s running up and down continuously with the While loop piece of code.
What I want to do is to have that GUI button start the LED running sequence, and then the same button stop the sequence at any time.

I do understand that the code is sitting/stuck in the While loop. And hence any buttons in the GUI are not going to have an effect.

So is there a better way of doing it? Any pointers would be appreciated.

Thanks in advance.

Asked By: blast_uk

||

Answers:

If you have a while loop and a GUI you can use generators to still use the loop and let the GUI run properly.
I sketch the Idea here and create an example for the Tkinter GUI.

You want to write your code as a loop and still use it in a GUI:

from Tkinter import *
from guiLoop import guiLoop # https://gist.github.com/niccokunzmann/8673951

@guiLoop
def led_blink(argument):
    while 1:
        print("LED on " + argument)
        yield 0.5 # time to wait
        print("LED off " + argument)
        yield 0.5
t = Tk()
led_blink(t, 'shiny!') # run led_blink in this GUI
t.mainloop()

Output while the GUI is responsive:

LED on shiny!
LED off shiny!
LED on shiny!
LED off shiny!
...

Sadly Tkinter is the only GUI I know and it is a bad example because you can always update the GUI in your loop with the update() method of GUI elements:

root = Tk()
while 1:
    print("LED on")
    t = time.time() + 0.5
    while t > time.time(): root.update()
    print("LED off")
    t = time.time() + 0.5
    while t > time.time(): root.update()

But with such a guiLoop you can have multiple loops:

t = Tk()
led_blink(t, 'red')
led_blink(t, 'blue')
led_blink(t, 'green')
t.mainloop()

Here are some examples for starting and stopping the loop with a button.

Answered By: User

Another option is to run the LED while loop in a separate thread. Like in
the next code. The while loop is stopped by toggling the shared led_switch
variable.

"""
blinking LED
"""

import tkinter as tk
import threading
import time

led_switch=False

def start_stop():
    global led_switch
    led_switch=not led_switch
    if led_switch:
        t=threading.Thread(target=LED)
        t.start()

def LED():
    while led_switch:
        print('LED on')
        time.sleep(1)
        print('LED off')
        time.sleep(1)

root=tk.Tk()

button=tk.Button(root,command=lambda: start_stop(),text='start/stop')
button.pack()
tk.mainloop()
Answered By: Arnold van der Wal

If you’re using Tkinter, there’s a very easy pattern for running a loop. Given that the UI (in just about every UI toolkit) is already running an infinite loop to process events, you can leverage this to run code periodically.

Let’s assume you have a python object “led” which has a method for toggling it on and off. You can have it switch from on to off every 100ms with something as simple as these three lines of code:

def blink(led):
    led.toggle()
    root.after(100, blink, led)

The above code will run forever, causing the led to blink every 100ms. If you want to be able to start and stop the blinking with a button, introduce a flag:

def blink(led):
    if should_blink:
        led.toggle()
    root.after(100, blink, led)

When you set the toggle to True, the led will start blinking. When it’s False, it will stop blinking.

The main thing to take away from this is that you already have an infinite loop running, so there’s no need to create one of your own, and no need to use something as complex as threading. Simply create a function that does one frame of animation, or calls some function or does some unit of work, then have the function request that it be run again in the future. How far in the future defines how fast your animation or blink will run.

Answered By: Bryan Oakley

Bryan Oakley you helped me out 8 years later. You are the man!

Answered By: Thankful Dude