python add time to a countdown already running - python

I want to have an app where if I click a button I add X amount of time to my running countdown timer.
I'm guessing I have to use threads for this but am not sure how to implement it..
Here is the code I have so far:
def countdown_controller(add_time):
end_it = False
def timer(time_this):
start = time.time()
lastprinted = 0
finish = start + time_this
while time.time() < finish:
now = int(time.time())
if now != lastprinted:
time_left = int(finish - now)
print time_left
lastprinted = now
if end_it == True:
now = finish
time.sleep(0.1)
# Check if the counter is running otherwise just add time.
try:
time_left
except NameError:
timer(add_time)
else:
if time_left == 0:
timer(add_time)
else:
add_this = time_left
end_it = True
while now != finish:
time.sleep(0.1)
timer(add_time + add_this)
Obviously this will not work, because every time I call countdown_controller(15) fx, it will start counting down for 15 seconds and if I click my button nothing happens until the timer is ended.
Help would be greatly appreciated.

I would say that there is a flaw in the design of the code, because your screen output blocks down the entire program doing nothing (time.sleep(0.1)).
Typically what you want to to do in these cases is having a main loop in your program that cycles through the various operations that make your program run. This guarantees a sensible distribution of system resources between the various tasks.
In your specific case, what you would like to have in your main loop is:
Check user input (has extra time been added?)
Update output of the countdown
Example implementation:
import time
import curses
# The timer class
class Timer():
def __init__(self):
self.target = time.time() + 5
def add_five(self):
self.target += 5
def get_left(self):
return int(self.target-time.time())
# The main program
t = Timer()
stdscr = curses.initscr()
stdscr.nodelay(True)
curses.noecho()
# This is the main loop done in curses, but you can implement it with
# a GUI toolkit or any other method you wish.
while True:
left = t.get_left()
if left <= 0:
break
stdscr.addstr(0, 0, 'Seconds left: %s ' % str(left).zfill(3))
c = stdscr.getch()
if c == ord('x') :
t.add_five()
# Final operations start here
stdscr.keypad(0)
curses.echo()
curses.endwin()
print '\nTime is up!\n'
The above program will increase the counter of 5 seconds if you press the x key (lowercase). Most of the code is boilerplate to use the curses module, but of course if you use PyGTK, PySide or any other graphical toolkit, it will be different.
EDIT: As a rule of thumb, in python you want to avoid threading as much as you can, both because it often (but not always) slows down programs (see "Global Interpreter Lock") and because it makes software harder to debug/maintain.
HTH!

I would probably have a Timer object with a finish attribute that I could simply add an int to. Have that timer running in another thread that you can then query for the current time remaining from your GUI.
class Timer(object):
def __init__(self, length):
self.finish = time.time() + length
def get_time(self):
return time.time() >= self.finish

Related

How to stop playing a sound from playsound module [duplicate]

This question already has answers here:
How to stop audio with playsound module?
(7 answers)
Closed 12 months ago.
I'm making a timer program and I would like it to play a song at the end of the timer but I would also like to stop the sound when I press a button.
I've seen other posts that use the multiprocessing module (How to stop audio with playsound module?) to stop the audio but I am already using the threading module, so I was wondering if its possible to use that to stop the audio instead.
Edit: Matiis gave a solution different to what i was looking for but it still works perfectly. glory9211 also gave the solution i was looking for later on
from tkinter import *
from time import sleep
from threading import Thread
from threading import Event
import playsound
class App(Tk):
def __init__(self):
super().__init__()
self.title("Clock")
self.t_evt = Event()
self.frame2 = Frame(self)
self.timerStart = Button(self.frame2, text="Start", command=self.tt_Start)
self.timerStart.config(height=2, width=5)
self.timerStart.grid(row=1)
self.timerEnd = Button(self.frame2, text="End", command=self.timer_End)
self.timerEnd.config(height=2, width=5)
self.timerEnd.grid(row=2)
def tt_Start(self):
t = Thread(target=self.timer_Start)
t.setDaemon(True)
t.start()
def timer_Start(self):
self.t_evt.clear()
timer_seconds = int(self.timerS_Entry.get())
timer_minutes = int(self.timerM_Entry.get())
if timer_seconds > 59:
timer_seconds -= 60
timer_minutes += 1
while not self.t_evt.is_set():
print(f"Minutes: {timer_minutes}, Seconds: {timer_seconds}")
self.timerSeconds.config(text=timer_seconds)
self.timerMinutes.config(text=timer_minutes)
self.update()
time = (timer_minutes * 60) + timer_seconds
timer_seconds -= 1
sleep(1)
if time == 0:
break
if timer_seconds < 0:
timer_seconds = 59
timer_minutes -= 1
playsound.playsound('C:\\Users\\nonon\\mp3\\song.wav')
print("TIMER UP")
return
def timer_End(self):
self.t_evt.set()
here is some code for you to work off of, let me know if you need more.
Again, I would like to be able to stop playsound.playsound('C:\\Users\\nonon\\mp3\\song.wav') when I press the end button
Short Answer
You can use threading Events instead of threads. Or switch to multiprocessing and use p.terminate()
Long Answer
You cannot stop a python threading.Thread using any provided function. People achieving this using flags. good reads: Is there any way to kill a Thread?
i.e.
def thread_func():
while flag:
print("I'm running")
def run_button():
flag = True
t = threading.Thread(target=thread_func)
t.start()
def stop_button():
flag = False
# This will make the function exit
But in your case the playsound function isn't a looping function where you can sneak a flag to stop the function. You can imagine it to be indefinite sleep function i.e.
def play_sound():
time.sleep(1000000) # Replacing with playsound.playsound
So using the threading.Events() we can achieve this with this sample code
import random
import signal
import threading
import time
exit_event = threading.Event()
def bg_thread():
for i in range(1, 30):
print(f'{i} of 30 iterations...')
if exit_event.wait(timeout=random.random()):
break
print(f'{i} iterations completed before exiting.')
def signal_handler(signum, frame):
exit_event.set() # same as setting the flag False
signal.signal(signal.SIGINT, signal_handler)
th = threading.Thread(target=bg_thread)
th.start()
th.join()
This solution effectively gives you an "interruptible" sleep, because if the event is set while the thread is stuck in the middle of the call to wait() then the wait will return immediately.
Read the detailed example here: https://blog.miguelgrinberg.com/post/how-to-kill-a-python-thread

How to make a loop that calculates running time?

I'm making a simple client/server program in Python 3 and in the client I would like a clock or printout of the running time. I'm trying to make it in a loop that starts at the beginning of the program, but in a thread so the rest of the code keeps going.
class time_thread():
def run(self):
loop = 0
while (zetime > -1):
print(zetime);
zetime = zetime + 1;
time_thread.start()
zetime = 0
This is what I have so far, but it doesn't work. It says:
time_thread has no attribute start()
I'm new to this and haven't used threads before, so I'm not sure how to go about this. Is there a better way?
I think this is what you're looking for:
import time, sys
zetime = 0
while (zetime > -1):
sys.stdout.write("\r" + str(zetime))
sys.stdout.flush()
time.sleep(1)
zetime = zetime + 1;
First of all , to use Thread module, you have to inherit the class Thread on your class, so you can use their methods like start.
To calculate time, you can use datetime.
from datetime import datetime
from time import sleep
start_time = datetime.now()
sleep(5) # code that you want to calculate.
end_time = datetime.now()
print(end_time - start_time)
Just place this
So let's say you define a function elapsed_time such as:
import time, sys
def elapsed_time(time_start):
''' time_start: a time.time()
Goal: print the elapsed time since time_start '''
# Allow to stop the counting once bool_time = False in the main program
global bool_elapsed_time
# loop while the condition
while bool_elapsed_time == True:
# Note: erase and write below does not work in spyder but in a shell yes
print ("Elapsed time: {} seconds".format(int(time.time() - time_start))),
sys.stdout.flush()
print ("\r"),
time.sleep(1)
# erase the line with elapsed time to clean the shell at the end
sys.stdout.flush()
print ("\r"),
Then in your program:
import threading
bool_elapsed_time = True
t = threading.Thread(name='child procs', target=elapsed_time, args=(time.time(),))
t.start()
## Here add the job you want to do as an example:
time.sleep(10)
bool_elapsed_time = False #to stop the elapsed time printing
Should do the job you want to do.
Note: I used python 2.7 so it might be slightly different with 3.x

Python multipul Event Timer

I have a water pump with pressure sensors. One on the input (low) and one on the output (high). My problem is my low pressure sensor. Sometimes the low pressure is just at the cut-off point causing the motor to start and stop quickly - this is not desirable. The system is running on a home-made PLS.
I'm a beginner at programming, 3 months, but the system is working for the most part. I need help on creating a timer between low pressure alarm events. I am thinking that the system can have 3 events within 30 seconds, but if any one event occurs in less than 5 seconds the system should shut down.
So if less than 5 seconds between the first event and second event the motor shuts down for good. The same goes for for second to third and third to fourth event. On the fourth event if less than 30 seconds occurs between first event and the fourth, the system also shuts down for good. Keep in mind that this is a part of a much larger loop. Here is the code I was able to create:
def Systemofftimer():
EventCounter = (0)
OneTimeLoopVarable = (0)
While True
if (is_low_pressure_alarm_on() and (OneTimeLoopVarable ==0)):
Timer = time.time()
EventCounter = EventCounter + (1)
OneTimeLoopVarable = 1
if EventCounter == (2) and (time.time() - Timer >= (10))
EventCounter = EventCounter + (1)
stop_motor()
if EventCounter == (3) and (time.time() - Timer >= (20))
EventCounter = EventCounter + (1)
stop_motor()
if EventCounter == (4) and (time.time() - Timer >= (30))
EventCounter = EventCounter + (1)
stop_motor()
else:
start_motor()
I would actually use a different approach for this: simply make your threshold for turning on larger than your threshold for turning of. For example:
That way you don't need to deal with the timing of it and can still eliminate the jittery nature around your state transition. You can also tune this to account for how noisy your sensors are.
Edit:
Below I've mocked up the piece of your system you're asking about. It's probably way more than you were initially looking for, but I wanted to test make sure it all worked properly before I posted so you're welcome to use it in whole or in part. As for the timer you asked about, it's based on Hans Then's post from this thread. To trigger the alarm, you just call TriggerAlarm() on the PumpSystem class. It will log that an alarm was triggered and then check the two conditions you mentioned in your question (5 sec and 30 sec errors). Each element of self.alarms contains the number of alarms that happened in a particular second, and each second the timer triggers to remove the oldest second from the list and create a fresh one. If you run the program, you can trigger alarms by pressing spacebar and see how the list is updated. The MockUp class is just meant to test and demonstrate how this works. I imagine you'll remove it if you decide to plug some portion of this into what you're working on. Anyway, here's the code.
from threading import Timer, Thread, Event
class PumpSystem():
def __init__(self):
self.alarms = [0 for x in range(30)]
self.Start()
return None
def SetUpdateFlag(self, flag):
self.update_flag = flag
return True
def Start(self):
self.stop_flag = Event()
self.thread = ClockTimer(self.OnTimerExpired, self.stop_flag)
self.thread.start()
return True
def Stop(self):
self.stop_flag.set()
return True
def TriggerAlarmEvent(self):
self.alarms[-1] += 1
self.CheckConditions()
self.update_flag.set()
return True
def OnTimerExpired(self):
self.UpdateRunningAverage()
def CheckConditions(self):
# Check if another error has triggered in the past 5 seconds
if sum(self.alarms[-5:]) > 1:
print('5 second error')
# Check if more than 3 errors have triggered in the past 30 seconds
if sum(self.alarms) > 3:
print('30 second error')
return True
def UpdateRunningAverage(self):
self.alarms.append(0)
self.alarms.pop(0)
self.update_flag.set()
return True
class ClockTimer(Thread):
def __init__(self, callback, event):
Thread.__init__(self)
self.callback = callback
self.stopped = event
return None
def SetInterval(self, time_in_seconds):
self.delay_period = time_in_seconds
return True
def run(self):
while not self.stopped.wait(1.0):
self.callback()
return True
## START MOCKUP CODE ##
import tkinter as tk
class MockUp():
def __init__(self):
self.pump_system = PumpSystem()
self.update_flag = Event()
self.pump_system.SetUpdateFlag(self.update_flag)
self.StartSensor()
return None
def StartSensor(self):
self.root = tk.Tk()
self.root.protocol("WM_DELETE_WINDOW", self.Exit)
self.alarms = tk.StringVar()
w = tk.Label(self.root, textvariable=self.alarms, width=100, height=15)
self.alarms.set(self.pump_system.alarms)
w.pack()
self.root.after('idle', self.ManageUpdate)
self.root.bind_all('<Key>', self.ManageKeypress)
self.root.mainloop()
return True
def ManageUpdate(self):
if self.update_flag.isSet():
self.alarms.set(self.pump_system.alarms)
self.update_flag.clear()
self.root.after(1, self.ManageUpdate)
return True
def ManageKeypress(self, event):
if event.keysym == 'Escape':
self.Exit()
if event.keysym == 'space':
self.pump_system.TriggerAlarmEvent()
return True
def Exit(self):
self.pump_system.Stop()
self.root.destroy()
mockup = MockUp()
This may look like a lot, but half is the mockup class that you can probably just ignore. Let me know if there's anything that you're confused about and I'd be happy to explain what's happening.

Is it possible to execute function every x seconds in python, when it is performing pool.map?

I am running pool.map on big data array and i want to print report in console every minute.
Is it possible? As i understand, python is synchronous language, it can't do this like nodejs.
Perhaps it can be done by threading.. or how?
finished = 0
def make_job():
sleep(1)
global finished
finished += 1
# I want to call this function every minute
def display_status():
print 'finished: ' + finished
def main():
data = [...]
pool = ThreadPool(45)
results = pool.map(make_job, data)
pool.close()
pool.join()
You can use a permanent threaded timer, like those from this question: Python threading.timer - repeat function every 'n' seconds
from threading import Timer,Event
class perpetualTimer(object):
# give it a cycle time (t) and a callback (hFunction)
def __init__(self,t,hFunction):
self.t=t
self.stop = Event()
self.hFunction = hFunction
self.thread = Timer(self.t,self.handle_function)
def handle_function(self):
self.hFunction()
self.thread = Timer(self.t,self.handle_function)
if not self.stop.is_set():
self.thread.start()
def start(self):
self.stop.clear()
self.thread.start()
def cancel(self):
self.stop.set()
self.thread.cancel()
Basically this is just a wrapper for a Timer object that creates a new Timer object every time your desired function is called. Don't expect millisecond accuracy (or even close) from this, but for your purposes it should be ideal.
Using this your example would become:
finished = 0
def make_job():
sleep(1)
global finished
finished += 1
def display_status():
print 'finished: ' + finished
def main():
data = [...]
pool = ThreadPool(45)
# set up the monitor to make run the function every minute
monitor = PerpetualTimer(60,display_status)
monitor.start()
results = pool.map(make_job, data)
pool.close()
pool.join()
monitor.cancel()
EDIT:
A cleaner solution may be (thanks to comments below):
from threading import Event,Thread
class RepeatTimer(Thread):
def __init__(self, t, callback, event):
Thread.__init__(self)
self.stop = event
self.wait_time = t
self.callback = callback
self.daemon = True
def run(self):
while not self.stop.wait(self.wait_time):
self.callback()
Then in your code:
def main():
data = [...]
pool = ThreadPool(45)
stop_flag = Event()
RepeatTimer(60,display_status,stop_flag).start()
results = pool.map(make_job, data)
pool.close()
pool.join()
stop_flag.set()
One way to do this, is to use main thread as the monitoring one. Something like below should work:
def main():
data = [...]
results = []
step = 0
pool = ThreadPool(16)
pool.map_async(make_job, data, callback=results.extend)
pool.close()
while True:
if results:
break
step += 1
sleep(1)
if step % 60 == 0:
print "status update" + ...
I've used .map() instead of .map_async() as the former is synchronous one. Also you probably will need to replace results.extend with something more efficient. And finally, due to GIL, speed improvement may be much smaller than expected.
BTW, it is little bit funny that you wrote that Python is synchronous in a question that asks about ThreadPool ;).
Consider using the time module. The time.time() function returns the current UNIX time.
For example, calling time.time() right now returns 1410384038.967499. One second later, it will return 1410384039.967499.
The way I would do this would be to use a while loop in the place of results = pool(...), and on every iteration to run a check like this:
last_time = time.time()
while (...):
new_time = time.time()
if new_time > last_time+60:
print "status update" + ...
last_time = new_time
(your computation here)
So that will check if (at least) a minute has elapsed since your last status update. It should print a status update approximately every sixty seconds.
Sorry that this is an incomplete answer, but I hope this helps or gives you some useful ideas.

Parallel while Loops in Python

I'm pretty new to Python, and programming in general and I'm creating a virtual pet style game for my little sister.
Is it possible to run 2 while loops parallel to each other in python?
eg:
while 1:
input_event_1 = gui.buttonbox(
msg = 'Hello, what would you like to do with your Potato Head?',
title = 'Main Screen',
choices = ('Check Stats', 'Feed', 'Exercise', 'Teach', 'Play', 'Go to Doctor', 'Sleep', 'Change Favourite Thing', 'Get New Toy', 'Quit'))
if input_event_1 == 'Check Stats':
myPotatoHead.check_p_h_stats()
elif input_event_1 == 'Feed':
myPotatoHead.feed_potato_head()
elif input_event_1 == 'Exercise':
myPotatoHead.exercise_potato_head()
elif input_event_1 == 'Teach':
myPotatoHead.teach_potato_head(myPotatoHead)
elif input_event_1 == 'Play':
myPotatoHead.play_with_toy()
elif input_event_1 == 'Sleep':
myPotatoHead.put_p_h_asleep()
elif input_event_1 == 'Go to Doctor':
myPotatoHead.doctor_check_up()
elif input_event_1 == 'Change Favourite Thing':
myPotatoHead.change_favourite_thing()
elif input_event_1 == 'Quit':
input_quit = gui.ynbox(
msg = 'Are you sure you want to quit?',
title = 'Confirm quit',
choices = ('Quit', 'Cancel'))
if input_quit == 1:
sys.exit(0)
while 1:
time.sleep(20)
myPotatoHead.hunger = str(float(myPotatoHead.hunger) + 1.0)
myPotatoHead.happiness = str(float(myPotatoHead.happiness) - 1.0)
myPotatoHead.tiredness = str(float(myPotatoHead.tiredness) + 1.0)
If not, is there some way that I can turn this into one loop?
I want the stuff in the second loop to happen every 20 seconds, but the stuff in the first loop to by constantly happening.
Thanks for any help
Have a look at Threading.Timer.
There is a code recipe here to schedule a function to run every 5 seconds.
import thread
import threading
class Operation(threading._Timer):
def __init__(self, *args, **kwargs):
threading._Timer.__init__(self, *args, **kwargs)
self.setDaemon(True)
def run(self):
while True:
self.finished.clear()
self.finished.wait(self.interval)
if not self.finished.isSet():
self.function(*self.args, **self.kwargs)
else:
return
self.finished.set()
class Manager(object):
ops = []
def add_operation(self, operation, interval, args=[], kwargs={}):
op = Operation(interval, operation, args, kwargs)
self.ops.append(op)
thread.start_new_thread(op.run, ())
def stop(self):
for op in self.ops:
op.cancel()
self._event.set()
if __name__ == '__main__':
# Print "Hello World!" every 5 seconds
import time
def hello():
print "Hello World!"
timer = Manager()
timer.add_operation(hello, 5)
while True:
time.sleep(.1)
The only way to "have two while loops in parallel" would be to place them on different threads, but then you need to tackle the synchronization and coordination problems between them since they're reaching into the same object.
I suggest you instead put a time check in the first (and single) loop and perform the increases that you now have in the second loop proportionately to that time-check; not quite satisfactory since the buttonbox call might take an indefinite amount of time to return, but way simpler to arrange, esp. for a beginner, than proper threading coordination.
Once you do have the basic logic in place and working, then you can consider threads again (with a periodic timer for what you'd like in the 2nd loop in one thread, the blocking buttonbox call in the main thread [[I think in easygui it has to be]], both feeding events into a Queue.Queue [[intrinsically thread-safe]] with another thread getting them and operating accordingly, i.e. most of what you now have in the 1st loop). But that's quite an advanced architectural problem, which is why I recommend you don't try to deal w/it right now!-)
put one of them into a function, the threading.Thread class supports a target attribute:
import threading
threading.Thread(target=yourFunc).start()
Will start yourFunc() running in the background.
You should use State Machines for this (see the Apress pygame book - downloads here: http://apress.com/book/downloadfile/3765 ), see chapter 7.
A simplified state machine:
def do_play(pet, time_passed):
pet.happiness += time_pass*4.0
def do_feed(pet, time_passed):
pet.hunger -= time_passed*4.0
def do_sleep(pet, time_passed):
pet.tiredness += time_passed*4.0
if pet.tiredness <= 0:
return 'Waiting'
def do_waiting(pet, time_passed):
pass
def do_howl(pet, time_passed):
print 'Hoooowl'
def do_beg(pet, time_passed):
print "I'm bored!"
def do_dead(pet, time_passed):
print '...'
STATE_TO_FUNC = dict(Waiting=do_waiting,
Sleeping=do_sleep,
Feeding=do_feed,
Playing=do_play,
Howling=do_howl,
Begging=do_beg,
Dead=do_dead
)
class Pet:
def __init__(self):
self.state = 'Waiting'
self.hunger = 1.0
self.tiredness = 1.0
self.happiness = 1.0
def process(self, time_passed):
self.hunger +=1*time_passed
self.tiredness +=1*time_passed
self.happiness -= 1*time_passed
func = STATE_TO_FUNC[self.state]
new_state = func(self, time_passed)
if new_state is not None:
self.set_state(new_state)
if self.hunger >10:
self.set_state('Dead')
elif self.hunger > 5 and not (self.state == 'Feeding'):
self.set_state('Howling')
elif self.tiredness > 5:
self.set_state('Sleeping')
elif self.happiness < 0 and not (self.state == 'Playing'):
self.set_state('Begging')
def set_state(self,state):
if not self.state == 'Dead':
self.state = state
from msvcrt import getch
import time
pet = Pet()
while True:
time0 = time.time()
cmd = getch() # what command?
pet.process(time.time()-time0)
if cmd == 'a':
pet.set_state('Feeding')
if cmd == 's':
pet.set_state('Sleeping')
if cmd == 'd':
pet.set_state('Playing')
Essentially to have processing to happen in parallel you have several solutions
1- Separate processes (ie: programs) running independently that speak to one another through a specific protocol (eg: Sockets)
2- Or you can have the one process spawn off multiple threads
3- Build an event queue internally and process them one by one
That is the general picture.
As for the specific answer to your question, you said "the stuff in the first loop to b[e] constantly happening". The reality is you never want this to happen all the time, because all that will do is use up 100% of the CPU and nothing else will ever get done
The simplest solution is probably number 3.
The way I would implement it is in my main loop have a thread that goes through an event queue and sets a timer for each event. Once all the timers have been sent the main loop then goes to sleep.
When a timer times out, an other function will then run the corresponding function for the event that triggered that timer.
In your case, you have two events. One for displaying the selection menu (first loop) and the second for changing myPotatoHead. The timer associated with the first one, I would set to 0.5sec, making it larger reduces CPU usage but slows down responsivness, increasing it usses up more CPU, for the second event I would set a 20 second timer.
Ofcourse when the timer expires, you would not do while 1 but you will just go through your while loop body once (ie get rid of while).
There is also a package called SimPy that you could also look at. The threading and multiprocessing libraries may also help.
i think they cannot be coupled in to one while loop.
maybe you need to check the threading or multiprocessing library.

Categories

Resources