-2

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

yorbby
  • 21
  • 7
  • 1
    `playsound` has no way of stopping the sound, even the module [description (and docs)](https://pypi.org/project/playsound/) says that the module has only one function and I am pretty sure that that one function is `playsound`, there is no `stopsound` or anything, seemingly if you want to stop it, either put it in a process (if it can be pickled) and when needed terminate that process or use sth like [`pygame.mixer.music`](https://www.pygame.org/docs/ref/music.html) – Matiiss Dec 21 '21 at 17:29
  • @Matiiss Ah ok I see. Thank you – yorbby Dec 21 '21 at 17:36
  • 1
    Does this answer your question? [How to stop audio with playsound module?](https://stackoverflow.com/questions/57158779/how-to-stop-audio-with-playsound-module) – Tomerikoo Dec 22 '21 at 09:30
  • @Tomerikoo no but Matiiss answered my question. – yorbby Dec 27 '21 at 18:48
  • I edited my question to explain why that solution wasnt optimal for me – yorbby Dec 27 '21 at 18:55

1 Answers1

0

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

glory9211
  • 741
  • 7
  • 18