asynchronous progress spinner - python

This is the code for the progress spinner:
import sys
import time
def spinning_cursor():
while True:
for cursor in '|/-\\':
yield cursor
spinner = spinning_cursor()
for _ in range(50):
sys.stdout.write(spinner.next())
sys.stdout.flush()
time.sleep(10)
sys.stdout.write('\b')
Output
python2.7 test.py
|
It is spinning very slowly since the loop sleeps for 10 seconds...
How do I keep rotating the spinner while the process is sleeping?

You'll have to create a separate thread. The example below roughly shows how this can be done. However, this is just a simple example.
import sys
import time
import threading
class SpinnerThread(threading.Thread):
def __init__(self):
super().__init__(target=self._spin)
self._stopevent = threading.Event()
def stop(self):
self._stopevent.set()
def _spin(self):
while not self._stopevent.isSet():
for t in '|/-\\':
sys.stdout.write(t)
sys.stdout.flush()
time.sleep(0.1)
sys.stdout.write('\b')
def long_task():
for i in range(10):
time.sleep(1)
print('Tick {:d}'.format(i))
def main():
task = threading.Thread(target=long_task)
task.start()
spinner_thread = SpinnerThread()
spinner_thread.start()
task.join()
spinner_thread.stop()
if __name__ == '__main__':
main()

You could sleep in smaller steps until you reach 10 seconds:
import sys, time
def spinning_cursor():
while True:
for cursor in '|/-\\':
yield cursor
spinner = spinning_cursor()
end_time = time.time() + 10
while time.time() < end_time:
sys.stdout.write(spinner.next())
sys.stdout.flush()
time.sleep(0.2) # adjust this to change the speed
sys.stdout.write('\b')
But this will block your main thread, so it will only be useful if you want to wait for 10 seconds without doing anything else in your Python program (e.g., waiting for some external process to complete).
If you want to run other Python code while the spinner is spinning, you will need two threads -- one for the spinner, one for the main work. You could set that up like this:
import sys, time, threading
def spin_cursor():
while True:
for cursor in '|/-\\':
sys.stdout.write(cursor)
sys.stdout.flush()
time.sleep(0.1) # adjust this to change the speed
sys.stdout.write('\b')
if done:
return
# start the spinner in a separate thread
done = False
spin_thread = threading.Thread(target=spin_cursor)
spin_thread.start()
# do some more work in the main thread, or just sleep:
time.sleep(10)
# tell the spinner to stop, and wait for it to do so;
# this will clear the last cursor before the program moves on
done = True
spin_thread.join()
# continue with other tasks
sys.stdout.write("all done\n")

Spawn two threads, A and B. Thread A runs cmd to completion. Thread B displays the spinning cursor and waits for thread A to exit, which will happen when cmd completes. At that point, thread B clears the spinning cursor and then exit.
Or use an existing library instead of re-inventing the wheel. Consider the progressbar library. You'll want the RotatingMarker progress indicator.

Related

How to program a task with a timer in my Python code?

I want to execute a task after certain time, so I have tried a countdown timer with a condition of being finished (when countdown variable = 0, the task is performed). The thing is that I don't want to stop the execution of the main program while performing the countdown. I have tried this:
import time
def countdown(num_of_secs):
while(num_of_secs):
time.sleep(1)
num_of_secs -= 1
return num_of_secs
So, I run my code setting a number of seconds to the countdown, and when this countdown reaches the 0 value, a task must be executed. Using this code (it uses a while), when I call my function "countdown" it stops the execution of the main program, so it is the same as a big time.sleep. I want to carry out this countdown in the background, without stopping other actions until the countdown finishes and the task starts.
Thank you
Another alternative is by using threading.
I've got a simple example here with 2 Threads where the working thread is waiting for the countdown thread to finish and starting. The Main is still working fine.
import threading
import time
def do_something():
countdown_thread.join()
print("Starting Task")
time.sleep(3)
print("Finished Task")
def countdown(num_of_secs):
while(num_of_secs):
time.sleep(1)
num_of_secs -= 1
print(num_of_secs)
if __name__ == '__main__':
countdown_thread = threading.Thread(target=countdown, args=(3,))
work_thread = threading.Thread(target=do_something)
countdown_thread.start()
work_thread.start()
while True:
print("Main doing something")
time.sleep(1)
Example picture for multithreading: Sequential vs Threading
Usually python only has a single program flow, so every instruction needs to complete before the next one can get executed.
For your case you need asynchronicity, with e.g. asyncio.sleep(5) as a separate task in the same event loop.
import asyncio
async def sleeper():
print('Holding...')
await asyncio.sleep(5)
print('Doing Work!')
async def work():
print('Doing work')
print('while')
print('the other guy is sleeping')
async def main():
await asyncio.gather(sleeper(), work())
asyncio.run(main())
The most common and easiest way to implement this would be with a Timer object from the threading library. It would go as follows:
import threading
import time
i = 0
done = False
def show_results():
print("results from GPIO readings")
print("=)")
global done
done = True # signal end of while loop
def read_GPIO():
print("reading GPIO...")
t = threading.Timer(60, show_results) # task will trigger after 60 seconds
t.start()
# your while loop would go here
read_GPIO() # do work
while not done:
print("waiting", i) # doing work while waiting for timer
time.sleep(1)
i += 1
pass
Notice that the time library is used only for illustrative purposes. You could also start the timer recursively to check periodically GPIOs and print results or trigger an event. For more information on the threading library or the Timer object check the docs

Event.set() in Threads Containing While Loops

I have two threads and am trying to use event.set() and event.wait() to pause the doing_different_stuff thread whilst the monitoring thread is executing.
My code is below. Even though the Rain_Check image is not on screen the doing_different_stuff thread only prints once when it is first run.
Can anyone see what I am doing wrong?
popup_found = threading.Event()
def monitoring():
while True:
if pyautogui.locateOnScreen('Rain_Check.png'):
popup_found.set()
print("Found pop-up")
sleep(float(random.uniform(22.21, 44.36)))
print("Pausing before closing pop-up")
def doing_different_stuff():
while True:
popup_found.wait()
sleep(1)
print("DDSing...")
rc_thread = threading.Thread(target=monitoring)
rc_thread.start()
dds_thread = threading.Thread(target=doing_different_stuff)
dds_thread.start()

Progress bar based on elapsed time in Python

I am trying to add a progress bar based on elapsed time in Python2.7. Basically, I am executing a bash script from python that takes about 90 minutes to complete, but the underlying application being called doesn't provide information that often. There are about 45 minutes where the user wouldn't see anything happening and I don't want them to kill the script thinking the process has frozen. Because I know approximately how long the executable takes to finish, I want to add a progress bar based on the elapsed time. Here is what I have so far:
def sys_call(self, cmd, log_file="prog.log"):
# setup toolbar and start timer
start = datetime.datetime.now()
stdo = sys.stdout
msg = "elapsed {}".format(datetime.timedelta())
stdo.write(msg)
stdo.write("\b"*len(msg)) # go to beginning of line
stdo.flush() # flush buffer
# initiate command and monitor
log = open(log_file, 'w')
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
for line in iter(process.stdout.readline, ''):
log.write(line.decode('utf-8'))
# display toolbar
msg = "elapsed {}".format(datetime.datetime.now() - start)
stdo.write(msg)
stdo.write("\b"*len(msg)) # go to beginning of line
stdo.flush() # flush buffer
log.close()
I am looking for a way to update the progress in the terminal based on the elapsed time. The line loop above stops looping for long periods of time after the script is executed because the application stops sending anything to stdout, so my elapsed time stops updating which is misleading. I can take care of calculating and printing a percentage for the user to see the process is not frozen, but I'm not sure how to run the elapsed time code in a separate thread.
I looked into progressbar and tqdm, but unfortunately, I cannot use third-party packages due to requirements outside my control.
I followed the blow snippet from this link
# Python program killing
# threads using stop
# flag
import threading
import time
def run(stop):
while True:
print('thread running')
if stop():
break
def main():
stop_threads = False
t1 = threading.Thread(target = run, args =(lambda : stop_threads, ))
t1.start()
time.sleep(1)
stop_threads = True
t1.join()
print('thread killed')
main()
Ultimately, here is what I came up with:
import datetime
import time
def thread_progressbar(self, stop):
"""Progress Bar Thread
This function is to be used with `threading`. Reference example.
Note, ensure the thread is a daemon otherwise CTRL+C will not
work and if this occurs, you will have to kill the thread from
the command line even though `main()` has already terminated.
Example
```
thread_stop = False
t = threading.Thread(target=thread_progressbar, args=(lambda:thread_stop,))
t.daemon = True # make daemon to kill thread if main terminates
t.start() # start executing thread
<do something else>
thread_stop = True # break out of inf loop
t.join() # kill thread
```
"""
start = datetime.datetime.now()
while True:
# get elapsed time and stay at 99 if we exceed expected time
elapsed = datetime.datetime.now() - start
percent = int(100.0*(elapsed.total_seconds()/self.expect_time.total_seconds()))
percent = 99 if percent >= 100 else percent
# display toolbar
sys.stdout.write("[{}%](elapsed {})".format(percent, elapsed))
sys.stdout.write("\b"*len(msg)) # go to beginning of line
sys.stdout.flush() # flush buffer
# use stop to determine when we are done
if stop():
elapsed = datetime.datetime.now() - start
sys.stdout.write("[100%](elapsed {})\n".format(elapsed))
break
time.sleep(1)
Use '\r' to move cursor back to beginning of the line after every time you print the bar, and just loop the bar until the task is done making sure to have '\r' at the end of every iteration.
This is an example of how that would be used
import time
for x in range(101):
print(f'{x}% done',end='\r')
time.sleep(1)

time.sleep blocks while loop in thread

When I run a While True loop in thread and use time.sleep() function the loop stops looping.
I am using this code:
import threading
from time import sleep
class drive_worker(threading.Thread):
def __init__(self):
super(drive_worker, self).__init__()
self.daemon = True
self.start()
def run(self):
while True:
print('loop')
#some code
time.sleep(0.5)
To start the thread I am using this code:
thread = drive_worker()
The loop stops because you flagged the thread as daemon.
The program terminates when there are only daemon threads left running.
self.daemon = True # remove this statement and the code should work as expected
Or make the main thread wait for the the daemon thread to finish
dthread = drive_worker()
# no call to start method since your constructor does that
dthread.join() #now the main thread waits for the new thread to finish
You imported sleep as
from time import sleep
so you have to call sleep in run() as sleep(0.5) or you have to change import as
import time
which I do not recommend.

Python running two threads simultaneously [duplicate]

This question already has answers here:
Terminate multiple threads when any thread completes a task
(5 answers)
Closed 8 years ago.
Is it possible to run two threads simultaneously? For example...
I have two classes like this...
import threading
class Thread1(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
print("Thread1")
class justAClass(object):
def do_soemthing(self):
print("Thread2")
if __name__ == "__main__":
total = 0
thread_limit = 200
while True:
if threading.activeCount() < thread_limit:
Thread1().start()
# I will never run because I want to wait until while True has finished to run!
t = threading.Timer(1.0, justAClass().do_soemthing())
t.start()
If you run this code you will see that Tread2 never gets printed out because the Thread2 has to wait for Thread1 to finished (which it never will because of the While statement.
What I'm after is for both Thread1 and Thread2 to run at the same time independent of each other.
while True:
if threading.activeCount() < thread_limit:
Thread1().start()
# I will never run because I want to wait until while True has finished to run!
t = threading.Timer(1.0, justAClass().do_soemthing())
t.start()
obviously that's the case! And as you're never getting out of the loop, the code below the comment is unreachable.
Though, your first code was:
tor.connect()
tor.new_identity()
t = threading.Timer(10.0, tor.new_identity())
t.start()
total = 0
thread_limit = 200
while True:
if threading.activeCount() < thread_limit:
stress_test(host_ip, host_port).start()
And there you were initiating the Timer before the infinite loop, so your Timer thread was definitely working, as we said in our comments. To make your SSCCE work correctly, here comes the fix:
import threading
import time
class Thread1(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
time.sleep(1)
print("Thread1")
class justAClass(object):
def do_something(self, pause):
while True:
time.sleep(pause)
print("Thread2XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
if __name__ == "__main__":
total = 0
thread_limit = 200
t = threading.Timer(1.0, justAClass().do_something, args=(1.0,))
t.start()
while True:
if threading.activeCount() < thread_limit:
Thread1().start()
Though, be aware that your timer thread, with the do_something function as it is, will only run once, unless you rearm it from within the thread, or you build a while loop within.
BTW, I fixed another mistake in your code I did not see at first, you're calling the
timer over the do_something function, be if you pass the do_something function with
parens at the end do_something(), it will be evaluated in your main thread as you're
creating the timer, and then you'll be passing the result of the function to the Timer
function... Whereas if you do not use the parens, you're giving the function object itself
to Timer() which will then be able to call it after the delay.
is there a way to get the timer to run every x seconds while still allowing the other function to run?
of course:
class justAClass(object):
def do_something(self, pause):
while True:
time.sleep(pause)
print("Thread2XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")

Categories