Search code examples
pythontkinterpyttsx3

How to pause, resume and stop pyttsx3 from speaking?


Is there any way to pause , resume and stop pyttsx3 from speaking?

I've tried many ways but I couldn't find a solution.

Here's the code:

from tkinter import *
import pyttsx3
import threading

root = Tk()

def read():
    engine.say(text.get(1.0 , END))
    engine.runAndWait()

def stop():
    # Code to stop pyttsx3 from speaking
    pass

def pause():
    # Code to pause pyttsx3
    pass

def unpause():
    # Code to unpause pyttsx3
    pass

engine = pyttsx3.init()

text = Text(width = 65 , height = 20 , font = "consolas 14")
text.pack()

text.insert(END , "This is a text widget\n"*10)

read_button = Button(root , text = "Read aloud" , command = lambda: threading.Thread(target=read, daemon=True).start())
read_button.pack(pady = 20)

pause_button = Button(root , text = "Pause" , command = lambda: threading.Thread(target=pause , daemon = True).start())
pause_button.pack()

unpause_button = Button(root , text = "Unpause" , command = unpause)
unpause_button.pack(pady = 20)

stop_button = Button(root , text = "Stop" , command = threading.Thread(target=stop, daemon = True).start())
stop_button.pack()

mainloop()

What I want is to pause, resume, and stop pyttsx3 whenever I want by clicking the buttons.

Is there any way to achieve this in tkinter?

It would be great if anyone could help me out.


Solution

  • There is no pause and resume function provided by pyttsx3, but you can simulate these feature by splitting the sentence into words, and then speak word by word. In between words, you can check whether pause, unpause or stop button is clicked and do corresponding action:

    from tkinter import *
    import pyttsx3
    import threading
    
    root = Tk()
    
    class Speaking(threading.Thread):
        def __init__(self, sentence, **kw):
            super().__init__(**kw)
            self.words = sentence.split()
            self.paused = False
    
        def run(self):
            self.running = True
            while self.words and self.running:
                if not self.paused:
                    word = self.words.pop(0)
                    print(word)
                    engine.say(word)
                    engine.runAndWait()
            print("finished")
            self.running = False
    
        def stop(self):
            self.running = False
    
        def pause(self):
            self.paused = True
    
        def resume(self):
            self.paused = False
    
    speak = None
    
    def read():
        global speak
        if speak is None or not speak.running:
            speak = Speaking(text.get(1.0, END), daemon=True)
            speak.start()
    
    def stop():
        global speak
        if speak:
            speak.stop()
            speak = None
    
    def pause():
        if speak:
            speak.pause()
    
    def unpause():
        if speak:
            speak.resume()
    
    engine = pyttsx3.init()
    
    text = Text(width=65, height=20, font="consolas 14")
    text.pack()
    
    text.insert(END, "This is a text widget\n"*10)
    
    read_button = Button(root, text="Read aloud", command=read)
    read_button.pack(pady=20)
    
    pause_button = Button(root, text="Pause", command=pause)
    pause_button.pack()
    
    unpause_button = Button(root, text="Unpause", command=unpause)
    unpause_button.pack(pady=20)
    
    stop_button = Button(root, text="Stop", command=stop)
    stop_button.pack()
    
    mainloop()
    

    Update: Using pygame as the player:

    from tkinter import *
    import pyttsx3
    import pygame
    
    pygame.mixer.init()
    engine = pyttsx3.init()
    
    root = Tk()
    
    def read():
        outfile = "temp.wav"
        engine.save_to_file(text.get('1.0', END), outfile)
        engine.runAndWait()
        pygame.mixer.music.load(outfile)
        pygame.mixer.music.play()
    
    def stop():
        pygame.mixer.music.stop()
    
    def pause():
        pygame.mixer.music.pause()
    
    def unpause():
        pygame.mixer.music.unpause()
    
    
    text = Text(width=65, height=20, font="consolas 14")
    text.pack()
    
    text.insert(END, "This is a text widget\n"*10)
    
    read_button = Button(root, text="Read aloud", command=read)
    read_button.pack(pady=20)
    
    pause_button = Button(root, text="Pause", command=pause)
    pause_button.pack()
    
    unpause_button = Button(root, text="Unpause", command=unpause)
    unpause_button.pack(pady=20)
    
    stop_button = Button(root, text="Stop", command=stop)
    stop_button.pack()
    
    mainloop()