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
Related
Here's my code:
import time
import keyboard #pip install keyboard - could use pynput listener instead.
from threading import Thread
hshtag = int(0)
done = False
fire = False
def StopStart(fire):
while not done:
global fire
if keyboard.is_pressed('#'):
hshtag = hshtag + 1
if hshtag % 2 ==0:
fire = False
else:
fire = True
return fire
def NormalFire():
while not done:
global fire
if fire == True:
#do x
else:
pass
t1 = Thread(target = StopStart)
t2 = Thread(target = NormalFire(fire))
t1.start()
t2.start()
The problem is function StopStart (should) effect what function Normalfire does but as the function only accepts a value for fire when it starts running (so it doesn't work). What I want is change what function normalfire does with function stopstart. and if you're wondering why I am using threading it because '#do x' actually takes a while to work so as one continuous script if I clicked hash at the wrong time it wouldn't stop. Maybe I could do this with classes instead but im not good with classes so if someone could either help with that or fix the above code that would be great thanks.
New attempt at explaining what's wrong with the top code - Ok, so both functions should be running simultaneously (which they are) - so no problems there. but as the function StopStart changes the boolean fire to true/false I want that to cause my NormalFire function to change what it is doing - nothing when I haven't clicked hash yet and something if I've clicked hash once but if I then click hash while its running it will finish whats its running then do nothing waiting for hash to be clicked again.
Sorry, my question wasn't clear take this code as a simplification of my core question.
##imports
import time
import keyboard #pip install keyboard - could use pynput listener instead.
from threading import Thread
##variable assigning
hshtag = int(0)
done = False
fire = False
def x():
while not done:
fire = True
return fire
def y(fire):
while not done:
if fire:
print('ok')
else:
pass
t1 = Thread(target = x)
t2 = Thread(target = y(fire))
t1.start()
t2.start()
Currently, the above code outputs nothing even though I've set 'fire = true' in function x and returned it how would I edit this code so that when boolean fire changes to true the function y starts printing ok?
Editing like Nair suggested also returns nothing and after 15 second the program stops running edited code:
##imports
import time
import keyboard #pip install keyboard - could use pynput listener instead.
from threading import Thread
##variable assigning
hshtag = int(0)
done = False
fire = False
def StopStart():
while not done:
fire = True
return fire
def NormalFire():
while not done:
if fire:
print('ok')
else:
pass
t1 = Thread(target = StopStart)
t2 = Thread(target = NormalFire)
t1.start()
t2.start()
I'm unable to comment so I apologize in advanced. I'm having trouble understanding your question above, but I reworked your code - fix/add whatever you need and get back to me!
import time
import keyboard #pip install keyboard - could use pynput listener instead.
from threading import Thread
hshtag = int(0)
done = False
fire = False
def StopStart():
while not done:
# global fire - You're setting StopStart up for a param that needs passed, that also is named another variable
# So it will just over write it (Also, no arg is passed for StopStart(fire))
if keyboard.is_pressed('#'):
hshtag = hshtag + 1
if hshtag % 2 == 0 : fire = False
else : fire = true
return fire
def NormalFire():
while not done:
#global fire - Don't need to global it, you would've had to global done if that was the case
if fire: # don't need == true, just need if fire (if true)
print("x")
t1 = Thread(target=StopStart)
t2 = Thread(target=NormalFire)
t1.start()
t2.start()
Not sure that this is exactly what you're asking for. I would maybe listen for keyboard events outside of either thread. Instead, just bind keyboard events to a callback which set a threading.Event object. Sorry for the weird and slightly morbid example:
from pynput import keyboard
from threading import Thread, Event
plate_dropped = Event()
def on_press(key):
if key is keyboard.Key.enter:
plate_dropped.set()
listener = keyboard.Listener(on_press=on_press)
def poll_plate_status():
from time import sleep
from random import choice
messages = [
"It sure is tempting, eh?",
"Are you gonna do it?"
]
print("It'd be a shame if someone would drop this plate and scare grandpa!")
while not plate_dropped.is_set():
print(choice(messages))
sleep(0.5)
print("The plate has been dropped!")
def poll_grandpa_status():
from time import sleep
from random import choice
messages = [
"*zzzZZZzzz*",
"*Snoooreee*"
]
print("Grandpa is sound asleep.")
while not plate_dropped.is_set():
print(choice(messages))
sleep(0.5)
print("HUH!?")
plate_thread = Thread(target=poll_plate_status, daemon=True)
grandpa_thread = Thread(target=poll_grandpa_status, daemon=True)
plate_thread.start()
grandpa_thread.start()
listener.start()
##imports
import time
import keyboard #pip install keyboard - could use pynput listener instead.
from threading import Thread
##variable assigning
hshtag = int(0)
done = False
fire = False
def StopStart(self, interval=1):
hshtag = 0
self.interval = interval
while not done:
if keyboard.is_pressed('#'):
hshtag = hshtag + 1
if hshtag % 2 ==0:
fire = False
else:
fire = True
def NormalFire():
while not done:
print('NormalFire Runs')
time.sleep(1)
if fire:
print('*fires*')
else:
print('*does nothing*')
#t1 = Thread(target = StopStart, daemon=True)
t2 = Thread(target = NormalFire, daemon=True)
#t1.start()
t2.start()
while not done:
#time.sleep()
if keyboard.is_pressed('#'):
hshtag = hshtag + 1
time.sleep(0.1)
if hshtag % 2 ==0:
fire = False
print(fire)
else:
fire = True
print(fire)
I realised my problem was my ides of threading (im new to it) this achieves what I wanted thanks for all the help.
I want to change label every sec when i press the button in tk:
# --coding:utf-8 -----
from Tkinter import *
import time
import random
def test(a):
begin =time.time()
end =time.time()
while True:
ran = random.random()
after = time.time()
if(after-begin >1):
a.set(str(ran))
print a.get()
begin =after
if(after-end>10):
a.set('over')
break
t = Tk()
a = StringVar()
a.set('0')
b = Label(t,textvariable = a)
b.pack()
Button(t,text ='test',command = lambda x=a:test(a)).pack()
t.mainloop()
My console output is right,but it doesnot effect on windows.WHY?
You can start the test() function in a separate thread, like this:
# --coding:utf-8 -----
from Tkinter import *
import time
import random
import threading
def startThread(a):
threading.Thread(target=test, args=(a,)).start()
def test(a):
begin =time.time()
end =time.time()
while True:
ran = random.random()
after = time.time()
if(after-begin >1):
a.set(str(ran))
print a.get()
begin =after
if(after-end>10):
a.set('over')
break
t = Tk()
a = StringVar()
a.set('0')
b = Label(t,textvariable = a)
b.pack()
Button(t,text ='test',command = lambda x=a:startThread(a)).pack()
t.mainloop()
However, the thread won't end until the end of the 10 seconds, even if you close the window. You'll need to find some code for the while loop to check if the application is still running.
Creating a behavior that must execute for a given time period is a frequent problem; you can tackle it with a dedicated Waiter class; this avoids the use of threads that are poorly supported by tkinter.
The following example opens a window with a label and a button. When the button is pressed, the label will update every seconds for 10 seconds, then stop.
Pressing the button again within 10 seconds has no effect; after that, it restarts the process for 10 more seconds.
import Tkinter as tk # tkinter if python >= 3
import time
import random
class Waiter(object):
def __init__(self, waiting_time):
"""
:param waiting_time: int, in seconds
"""
self.waiting_time = waiting_time
self.expiring_time = time.time() + self.waiting_time
self.waiting = True
# print('waiter started')
def stop(self):
# print('waiter stopping')
self.expiring_time = None
self.waiting = False
def is_waiting(self):
"""returns True while waiting, false otherwise"""
if time.time() > self.expiring_time:
self.stop()
return self.waiting
def atest():
global waiter
if waiter is None:
waiter = Waiter(10)
_atest()
def _atest():
""" equivalent to:
while the waiter is waiting,
change the label every second),
then destroy the waiter
"""
global waiter
if waiter.is_waiting():
a.set(random.random())
# print(time.time())
t.after(1000, _atest)
else:
waiter = None
if __name__ == '__main__':
waiter = None
t = tk.Tk()
a = tk.StringVar()
a.set('0')
tk.Label(t, textvariable=a).pack()
tk.Button(t, text='test', command=atest).pack()
t.mainloop()
Note:
You could make _atest an inner function of atest, but maybe it is easier to understand as it is?
using import Tkinter as tk instead of from Tkinter import * prevents cluttering the namespace, and arguably makes the code clearer.
You should probably consider using python 3.
Say I have a long running python function that looks something like this?
import random
import time
from rx import Observable
def intns(x):
y = random.randint(5,10)
print(y)
print('begin')
time.sleep(y)
print('end')
return x
I want to be able to set a timeout of 1000ms.
So I'm dong something like, creating an observable and mapping it through the above intense calculation.
a = Observable.repeat(1).map(lambda x: intns(x))
Now for each value emitted, if it takes more than 1000ms I want to end the observable, as soon as I reach 1000ms using on_error or on_completed
a.timeout(1000).subscribe(lambda x: print(x), lambda x: print(x))
above statement does get timeout, and calls on_error, but it goes on to finish calculating the intense calculation and only then returns to the next statements. Is there a better way of doing this?
The last statement prints the following
8 # no of seconds to sleep
begin # begins sleeping, trying to emit the first value
Timeout # operation times out, and calls on_error
end # thread waits till the function ends
The idea is that if a particular function timesout, i want to be able to continue with my program, and ignore the result.
I was wondering if the intns function was done on a separate thread, I guess the main thread continues execution after timeout, but I still want to stop computing intns function on a thread, or kill it somehow.
The following is a class that can be called using with timeout() :
If the block under the code runs for longer than the specified time, a TimeoutError is raised.
import signal
class timeout:
# Default value is 1 second (1000ms)
def __init__(self, seconds=1, error_message='Timeout'):
self.seconds = seconds
self.error_message = error_message
def handle_timeout(self, signum, frame):
raise TimeoutError(self.error_message)
def __enter__(self):
signal.signal(signal.SIGALRM, self.handle_timeout)
signal.alarm(self.seconds)
def __exit__(self, type, value, traceback):
signal.alarm(0)
# example usage
with timeout() :
# infinite while loop so timeout is reached
while True :
pass
If I'm understanding your function, here's what your implementation would look like:
def intns(x):
y = random.randint(5,10)
print(y)
print('begin')
with timeout() :
time.sleep(y)
print('end')
return x
You can do this partially using threading
Although there's no specific way to kill a thread in python, you can implement a method to flag the thread to end.
This won't work if the thread is waiting on other resources (in your case, you simulated a "long" running code by a random wait)
See also
Is there any way to kill a Thread in Python?
This way it works:
import random
import time
import threading
import os
def intns(x):
y = random.randint(5,10)
print(y)
print('begin')
time.sleep(y)
print('end')
return x
thr = threading.Thread(target=intns, args=([10]), kwargs={})
thr.start()
st = time.clock();
while(thr.is_alive() == True):
if(time.clock() - st > 9):
os._exit(0)
Here's an example for timeout
import random
import time
import threading
_timeout = 0
def intns(loops=1):
print('begin')
processing = 0
for i in range(loops):
y = random.randint(5,10)
time.sleep(y)
if _timeout == 1:
print('timedout end')
return
print('keep processing')
return
# this will timeout
timeout_seconds = 10
loops = 10
# this will complete
#timeout_seconds = 30.0
#loops = 1
thr = threading.Thread(target=intns, args=([loops]), kwargs={})
thr.start()
st = time.clock();
while(thr.is_alive() == True):
if(time.clock() - st > timeout_seconds):
_timeout = 1
thr.join()
if _timeout == 0:
print ("completed")
else:
print ("timed-out")
You can use time.sleep() and make a while loop for time.clock()
I'm having trouble understanding threads in Python. I have this program:
import _thread, time
def print_loop():
num = 0
while 1:
num = num + 1
print(num)
time.sleep(1)
_thread.start_new_thread(print_loop, ())
time.sleep(10)
And my question is if I need to close the thread print_loop, because it looks to me that both threads end when the main thread ends. Is this proper way to handle threads?
First, avoid using the low-level API unless you absolutely have to. The threading module is preferred over _thread. In general in Python, avoid anything starting with an underscore.
Now, the method you are looking for is called join. I.e.
import time
from threading import Thread
stop = False
def print_loop():
num = 0
while not stop:
num = num + 1
print(num)
time.sleep(1)
thread = Thread(target=print_loop)
thread.start()
time.sleep(10)
stop = True
thread.join()
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