pythonpython-playsound

How to stop playing a sound from playsound module


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


Solution

  • 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