My question is a bit confusing so I will explain it by saying exactly what I am trying to do.
I just got a Raspberry Pi and am writing a Python project with it. I have a function that makes a light blink on and off infinitely. I want to use the blinking light to show a status of a job (one that could take awhile).
Here is the pseudo-code for what I am trying to do:
def blink():
while 1:
##light on##
time.sleep(.5)
##light off##
time.sleep(.5)
longRunningJob() #stop blinking when job returns
Any ideas?
You may use a class to pass a stop variable and finish thread like this:
import time
from threading import Thread
class Blink(Thread):
def __init__(self,starting_variable):
Thread.__init__(self)
print("starting variable: %s"%(starting_variable))
self.stop=False
def Stop(self):
self.stop = True
def blink(self):
print("light on")
##light on##
time.sleep(.5)
print("light off")
##light off##
time.sleep(.5)
def run(self):
while not self.stop:
self.blink()
print("exiting loop ...")
def longRunningJob():
for sleep_delay in range(5):
print("in longRunningJob with sleep: %s"%(sleep_delay))
time.sleep(sleep_delay)
blink=Blink("something")
blink.start()
longRunningJob()
blink.Stop()
print("END")
Here is the solution
import threading
import time
RUNNING = False
def blink():
while RUNNING:
##light on##
time.sleep(.5)
##light off##
time.sleep(.5)
t = threading.Thread(target=blink)
RUNNING = True
t.start()
longRunningJob() #stop blinking when job returns
RUNNING = False
Related
Please, can anyone help me to understand why in the following sample code everything I input by the console (I'm using Spyder 4) isn't taken by "input" statement? The main thread exits while the thread containing the input statement goes running forever. The only way to kill it is to reset the Python kernel. Everything goes right if I don't use the input statement.
Thanks for any hint!
import threading
import time
class myThread (threading.Thread):
def __init__(self, name, counter):
threading.Thread.__init__(self)
self.name = name
self.counter = counter
def run(self):
print (f"Starting {self.name}")
wait_input(self.name, 5, self.counter)
print (f"Exiting {self.name}")
def wait_input(threadName, counter, delay):
a = input("tell me smthg: ")
while counter:
time.sleep(delay)
print (f"{threadName}, {time.ctime(time.time())}, {counter}")
counter -= 1
# Create new threads
thread1 = myThread("Thread-1", 1)
# Start new Threads
thread1.start()
print ("Exiting Main Thread")
So I've got a problem similar to this: Running infinite loops using threads in python
I want to create a number of threads (up to 50) which are running the same code at the same time, having an infinite while loop. There's no interaction between these threads. The practical idea behind this is that i have a string of WS2811 LEDs which I want to control independently with different color modes like blinking.
The problem I have with the similar question is, that I don't want to create 50 classes for each thread if they are all doing the same. I'd like to create these threads, based on one common class, with a for loop. The problem I encountered with this is that only one thread is in this infinite loop, while the other one not even starts. How do I fix this?
import threading
import time
class LEDManager(threading.Thread):
def __init__(self, id_manager):
threading.Thread.__init__(self)
self.id_manager = int(id_manager)
def initiate(id_manager):
while True:
print("Thread " + str(id_manager) + " blink on")
time.sleep(2)
print("Thread " + str(id_manager) + " blink off")
time.sleep(2)
def main():
thread_id = ("0", "1")
led_index = 0
thread_list = list()
for objs in thread_id:
thread = threading.Thread(target=LEDManager.initiate(led_index), args=(led_index,))
thread_list.append(thread)
time.sleep(1)
led_index += 1
for thread in thread_list:
thread.start()
if __name__ == "__main__":
main()
The output from the code above is:
Thread 0 blink on
Thread 0 blink off
Thread 0 blink on
Thread 0 blink off
.
.
.
here is one way you can refactor the code to make it work
import threading
import time
class LEDManager(object):
def __init__(self):
pass
def initiate(self, idx):
while True:
print("Thread " + str(idx) + " blink on")
time.sleep(2)
print("Thread " + str(idx) + " blink off")
time.sleep(2)
def main():
thread_list = list()
l = LEDManager()
for i in range(50):
thread = threading.Thread(target=l.initiate, args=(i,))
thread_list.append(thread)
for thread in thread_list:
thread.start()
of course it could be written in much more best practice way, my suggestion is to look at greenlet
keep in mind the GIL won't give you real threading behaviour (truly parallel run) you can take a look at multi-processing for this
Since you're deriving LEDManager from threading.Thread, it is a thread. Don't create new threadingThread objects to run its member function! Just create instances of LEDManager and start() those:
import threading
import time
class LEDManager(threading.Thread):
def __init__(self, id_manager):
threading.Thread.__init__(self)
self.id_manager = int(id_manager)
def run(self):
while True:
print("Thread " + str(self.id_manager) + " blink on")
time.sleep(2)
print("Thread " + str(self.id_manager) + " blink off")
time.sleep(2)
def main():
thread_id = ("0", "1")
led_index = 0
thread_list = list()
for objs in thread_id:
thread = LEDManager(led_index)
thread_list.append(thread)
led_index += 1
for thread in thread_list:
thread.start()
time.sleep(1)
if __name__ == "__main__":
main()
(Credits to #stovfl)
The threading.Thread.run() method is called automatically when the thread is start()ed.
I also moved the one-second sleep to the start loop to get even interleaving of the threads' output, which I suspect is what you intended.
I'm trying to light a 5mm LED while a function is running. When this function (more details about this below) is finished and has returned a value I would like to break the while loop.
Current code for while loop:
pins = [3,5,8,15,16]
def piBoard():
finished = 0
while finished!=10:
for pin in pins
GPIO.output(
pin, GPIO.HIGH
)
time.sleep(0.1)
GPIO.output(
pin, GPIO.LOW
)
finished+=1
Now in the above example I just run the while loop until the count is equal to 10, not best practice. I would like the while loop to break if my next function has returned a value.
Function I want to break my while loop when returned its value
def myFunction():
Thread(target = piBoard().start()
// Trying to recognize the song
return the song which is recognized
Thanks, - K.
It sounds to me like you want to write a class that extends Thread and implements __enter__ and __exit__ methods to make it work in the with statement. Simple to implement, simple syntax, works pretty well. The class will look like this:
import threading
class Blinky(threading.Thread):
def __init__(self):
super().__init__()
self.daemon = True
self._finished = False
def __enter__(self):
self.start()
def __exit__(self, exc_type, exc_val, exc_tb):
self.stop()
def run(self):
# turn light on
while not self._finished:
time.sleep(.5)
# turn light off
def stop(self):
self._finished = True
Then, to run your function, you simply put:
with Blinky():
my_function()
The light should turn on once the with statement is reached and turn off up to a half second after the context of the with is exited.
In while condition put true and in while loop put if statement which will check if your function return any value if return write break
You need some kind of inter-thread communication. threading.Event is about as simple as you can get.
import threading
song_recognized_event = threading.event()
in your song recognizer, call set() once the song is recognized.
In your LED loop, check isSet() occasionally while toggling LEDs.
while not song_recognized_event.isSet():
# toggle LEDs
Run clear() to reset it.
if you are open to using threads.
you can achieve this by using threads.
here's the example code
from concurrent.futures._base import as_completed
from concurrent.futures.thread import ThreadPoolExecutor
WORK_FINISHED = False
def piBoard():
while not WORK_FINISHED:
# Do some stuff
# Drink some coffee
def myFunction():
time.sleep(5)
global WORK_FINISHED
WORK_FINISHED = True #update gobal status flag
return something
if __name__ == '__main__':
futures = []
MAX_WORKERS = 5 #max number of threads you want to create
with ThreadPoolExecutor(MAX_WORKERS) as executor:
executor.submit(piBoard)
# submit your function to worker thread
futures.append(executor.submit(myFunction))
# if you need to get return value from `myFunction`
for fut in as_completed(futures):
res = fut.result()
Hope this helps.
Using decorator and asyncio, inspired by #Eric Ed Lohmar:
import asyncio
def Blink():
from functools import wraps
async def _blink():
while True:
print("OFF")
await asyncio.sleep(.5)
print("ON")
await asyncio.sleep(.5)
def Blink_decorator(func):
#wraps(func)
async def wrapper(*args,**kwargs):
asyncio.ensure_future(_blink())
await func(*args,**kwargs)
return wrapper
return Blink_decorator
#Blink()
async def longTask():
print("Mission Start")
await asyncio.sleep(3)
print("Mission End")
def main():
loop = asyncio.get_event_loop()
loop.run_until_complete(longTask())
I have a project that involves a raspberry pi, the dothat 16x2 lcd display and some python code. I am essentially trying to make a dynamic while loop that displays information on the lcd. I am also trying to add a function that cancels the while loop by pressing one of the touch buttons (reference: https://github.com/pimoroni/dot3k/blob/master/python/REFERENCE.md)
This is what I got so far:
import dothat.lcd as l
import dothat.backlight as b
import dothat.touch as t
from time import sleep
import signal
import os
def main():
i=0
k=0
while True:
l.clear() # Clear LCD screen
b.hue(1.5) # Set background color
l.set_cursor_position(0, 1) # Set cursor position on LCD
l.write("%s" % k) # Write variable "k" to LCD
#t.on(t.CANCEL) # When CANCEL button is pressed then go to function
def cancel(ch, evt):
i=1 # Set variable "i" as 1
return
if i == 1:
break
k=k+1
sleep(1)
l.clear() # Clear LCD screen
b.off() # Turn the LCD Backlight off
cmd='pkill python' #
os(cmd) # Kill all python processes
signal.pause()
main()
The while loop is running but it won't break when the button is pressed. Ideas?
I fixed it, though I'm getting errors about the 'module' object not being callable regarding os(cmd).
The code:
def main():
global i
i=0
k=0
while True:
l.clear()
b.hue(1.5)
l.set_cursor_position(0, 1)
l.write("%s" % k)
#t.on(t.CANCEL)
def cancel(ch, evt):
global i
i=1
return
if i == 1:
break
k=k+1
sleep(1)
l.clear()
b.off()
cmd='pkill python'
os(cmd)
signal.pause()
main()
I don't have a dothat LCD display, so I can't test your code. But I think #Pigface333 was right, the i inside cancel is a local variable, thus the i in your if statement is not set to 1 after pressing Cancel. The following code demonstrates that:
from time import sleep
def main():
i = 0
k = 0
while True:
def cancel():
print "inside cancel"
i = 1
return
cancel()
if i == 1:
break
k = k+1
sleep(1)
exit(0)
main()
This will print inside cancel every 1 second, but won't exit, showing that the i inside cancel is a local variable. To fix it, you can create a class that stores the cancellation status:
from time import sleep
class Cancel(object):
def __init__(self):
self.is_cancelled = False
def cancel(self):
self.is_cancelled = True
def main():
canceller = Cancel()
while True:
canceller.cancel()
if canceller.is_cancelled:
break
sleep(1)
exit(0)
main()
The same method can be applied to your code:
import dothat.lcd as l
import dothat.touch as t
import dothat.backlight as b
from time import sleep
import signal
class Cancel(object):
def __init__(self,):
self.is_cancelled = False
#t.on(t.CANCEL)
def cancel(self, ch, evt):
self.is_cancelled = True
return
def main():
k = 0
cancel = Cancel()
while True:
l.clear() # Clear LCD screen
b.hue(1.5) # Set background color
l.set_cursor_position(0, 1) # Set cursor position on LCD
l.write("%s" % k) # Write variable "k" to LCD
if cancel.is_cancelled:
break
k = k+1
sleep(1)
l.clear() # Clear LCD screen
b.off() # Turn the LCD Backlight off
signal.pause()
exit(0)
main()
To help understanding why the original code didn't work and why using a class is a good idea, I suggest reading about Python's variable's scope and Object-Oriented Prograaming in Python.
I have simple script for watchdog on network device. Script monitors response from PING command. If there is no answer then second thread executes and first thread is stopped. If second thread is finished then first thread is resumed (checking ping). If there is no answer then following message appears:
RuntimeError: threads can only be started once
Here is my code:
#!/usr/bin/python
import os
import time
import sqlite3
from ablib import Pin
import threading
led=Pin('W9','OUTPUT')
class threadout1(threading.Thread):
def run(self):
while True:
conn = sqlite3.connect('database/database.db')
cur = conn.cursor()
cur.execute("SELECT * FROM watchdog")
rows_output = cur.fetchall()
time.sleep(1)
if rows_output[0][1] == "ping":
response = os.system("ping -c 1 " + rows_output[0][2])
if response != 0:
print "bad"
rest.start()
rest.join()
class restart(threading.Thread):
def run(self):
led.on()
time.sleep(15)
led.off()
thr = threadout1()
rest = restart()
thr.start()
You can either create the restart thread every time you need it
if response != 0:
print "bad"
restart_thread = restart()
restart_thread.start()
restart_thread.join()
or use Events
class restart_thread(threading.Thread):
def __init__(self, evt):
self.evt = evt
def run(self):
self.evt.wait()
# do stuff
self.evt.clear()
class threadout(threading.Thread):
def __init__(self, evt):
self.evt = evt
def run(self):
if #other thread needs to run once
self.evt.set()
evt = threading.Event()
restart_thread = restart(evt)
restart_thread.start()
pinging_thread = threadout(evt)
pinging_thread.start()
To make the pinging_thread wait for the restart_thread to finish, you could use another Event.