nateshmbhat / pyttsx3

Offline Text To Speech synthesis for python
Mozilla Public License 2.0
2.07k stars 327 forks source link

pyttsx3 issue with tkinter, Pyhton 3.8.5 MacOs Mojave 10.14.6/MacOs BigSur 11.0.1 #158

Open 3861EW opened 3 years ago

3861EW commented 3 years ago

I've been googling for almost a week now and I know there are similar questions, but none of them have an answer. So here is one more, which hopefully will lead to an answer. I wrote a small program for my kid where a word shows up, is pronounced and after clearing the word she needs to reproduce it. I used pyttsx3 as a speech module because it's available offline. Program works fine, but just to make it more attractive for her, I decided to build a GUI with tkinter. What happens now is that the window is built, the word shows up is pronounced, but after that the program shuts down without any errors. To check how far the program is executed, I used a print command after every line. The print commands are executed, and are visible in the terminal. Every other line in between, related to tkinter is not executed. I tried to create a different function for the speak engine, same thing happens.

So again: pyttsx3 works fine, so does tkinter, but together in one program does not. if you remove the line engine.runAndWait() the program executes fine, just without speech.

Why is this happening and how can I solve it? I am very sorry, please explain it to me like I'm a 40 year old housefather who is trying to learn a new skill without any background in programming or CS, thanks.

Here is my code, sorry if it's a bit messy, just started a few weeks ago :-)

` """import nescessary.""" import tkinter as tk import sys, json, random, pyttsx3

setup window

window = tk.Tk() window.geometry("500x500") window.title("Aliyah's dictee spel") window.configure(background = "black")

my photo

photo1 = tk.PhotoImage(file="dictee.gif") tk.Label (window, image=photo1, bg="black") .grid(row=0, column=0)

def main(): """Setting up the game""" global WORDS_CORRECT, WORDS_WRONG, WORDS WORDS_CORRECT = 0 WORDS_WRONG = 0 with open('vocabulary.json') as json_file: data = json.load(json_file) WORDS = data["words"] for widget in window.winfo_children(): widget.destroy() tk.Label (window, image=photo1, bg="black") .grid(row=0, column=0) tk.Label (window, text = "Dit is het dictee spel voor Aliyah. Wil je beginnen?", bg="black", fg="white", font="none 12 bold") .grid(row=1) tk.Button(window, text="Ja", width=6, command=lambda: dictee(WORDS)) .grid(row=2, column=0) tk.Button(window, text="Nee", width=6, command=sys.exit) .grid(row=2, column=1) tk.Button(window, text="Bewerk lijst", width=10, command=manage_list) .grid(row=3, column=1)

def speak(WORD): this did not work, even the last print command in dictee() is executed in terminal

#engine = pyttsx3.init()
#nl_voice_id = "com.apple.speech.synthesis.voice.ellen"
#engine.setProperty('rate', 100)
#engine.setProperty('voice', nl_voice_id)
#engine.say(WORD)
#engine.runAndWait()

def dictee(WORDS): """Show random words and ask for input.""" if WORDS == []: for widget in window.winfo_children(): widget.destroy() tk.Label (window, image=photo1, bg="black") .grid(row=0, column=0) tk.Label (window, text = "Dat waren alle woordjes.", bg="black", fg="white", font="none 12 bold") .grid(row=1, column=0) tk.Label (window, text = "Woordjes goed: %d" % WORDS_CORRECT, bg="black", fg="white", font="none 12 bold") .grid(row=2, column=0) tk.Label (window, text = "Woordjes fout: %d" % WORDS_WRONG, bg="black", fg="white", font="none 12 bold") .grid(row=3, column=0) tk.Label (window, text = "Wil je het nog een keer proberen?", bg="black", fg="white", font="none 12 bold") .grid(row=4, column=0) tk.Button(window, text="Ja", width=6, command=main) .grid(row=5, column=0) tk.Button(window, text="Nee", width=6, command=exit_game) .grid(row=5, column=1) window.update() else: random.shuffle(WORDS) for widget in window.winfo_children(): widget.destroy() WORD = WORDS[0] tk.Label (window, image=photo1, bg="black") .grid(row=0, column=0) word = tk.Label (window, text = WORD, bg="black", fg="white", font="none 20 bold") word.grid(row=1, column=0) window.update()

speak(WORD)this is related to speak()

    engine = pyttsx3.init()
    nl_voice_id = "com.apple.speech.synthesis.voice.ellen"
    engine.setProperty('rate', 100)
    engine.setProperty('voice', nl_voice_id)
    engine.say(WORD) #WORD is being said!
    engine.runAndWait()#<< if you remove this line, the program is executed fine, but without speech
    #engine.stop() this did not work
    window.after(3000)
    print('after after')#all print commands after this are executed in terminal, all other lines are not executed
    word['text'] = ''
    print('after clear text')
    window.update()
    print('after update')
    ANSWER = tk.Entry(window, width=20, bg="white")
    print('after creating entry box')
    ANSWER.grid(row=2, column=0)
    print('after positioning entry window')
    tk.Button(window, text="Check", width=6, command=lambda: check(ANSWER,WORD)) .grid(row=3, column=0)
    print('after creating button')
    WORDS.remove(WORD)
    print('after words.remove')

def check(ANSWER, WORD): '''Cross check word shown with answer given''' global WORDS_CORRECT, WORDS_WRONG if ANSWER.get() == WORD: tk.Label (window, text = "Wat goed!", bg="black", fg="white", font="none 12 bold") .grid(row=4, column=0) WORDS_CORRECT += 1 else: tk.Label (window, text = "Jammer!", bg="black", fg="white", font="none 12 bold") .grid(row=4, column=0) WORDS_WRONG += 1

tk.Label (window, text = "Wil je het nog een keer proberen?", bg="black", fg="white", font="none 12 bold") .grid(row=5, column=0)
tk.Button(window, text="Ja", width=6, command=lambda: dictee(WORDS)) .grid(row=6, column=0)
tk.Button(window, text="Nee", width=6, command=exit_game) .grid(row=6, column=1)

def manage_list(): '''manage the list of words by user''' with open('vocabulary.json', "r") as f: data = json.load(f) WORDS = data["words"] for widget in window.winfo_children(): widget.destroy() window.update() tk.Label (window, text = "Dit zijn alle woorden in de vocabulaire", bg="black", fg="white", font="none 12 bold") .grid(row=0, column=0) tk.Label (window, text = WORDS, bg="black", fg="white", font="none 12 bold") .grid(row=1, column=0) tk.Label (window, text = "Wil je woorden toevoegen of verwijderen?", bg="black", fg="white", font="none 12 bold") .grid(row=2, column=0) tk.Button(window, text="alles verwijderen", command=lambda: clear_words('vocabulary.json')) .grid(row=5, column=0) words_to_add = tk.Entry(window, width=40, bg="white") words_to_add.grid(row=3, column=0) tk.Button(window, text="toevoegen", width=6, command=lambda: add_words('vocabulary.json', words_to_add.get())) .grid(row=8, column=2) tk.Button(window, text="hoofdmenu", width=6, command=main) .grid(row=10, column=0)

def add_words(filename, wordt_text): with open(filename, "r+") as f: data = json.load(f) data["words"].extend(wordt_text.split()) f.seek(0) json.dump(data, f, indent=4) manage_list()

def clear_words(filename): with open('vocabulary.json', "w+") as f: data = {"words":[]} json.dump(data, f, indent=4) manage_list()

def exit_game(): '''summarize results and exit after pushing enter''' for widget in window.winfo_children(): widget.destroy() tk.Label (window, image=photo1, bg="black") .grid(row=0, column=0) tk.Label (window, text = "Tot de volgende keer.", bg="black", fg="white", font="none 12 bold") .grid(row=1, column=0) tk.Label (window, text = "Woordjes goed: %d" % WORDS_CORRECT, bg="black", fg="white", font="none 12 bold") .grid(row=2, column=0) tk.Label (window, text = "Woordjes fout: %d" % WORDS_WRONG, bg="black", fg="white", font="none 12 bold") .grid(row=3, column=0) tk.Label (window, text = "Klik op OK om af te sluiten", bg="black", fg="white", font="none 12 bold") .grid(row=4, column=0) tk.Button(window, text="OK", width=6, command=sys.exit) .grid(row=5, column=0)

if name == "main": main()

window.mainloop() `

Here is the vocabulary.json file:

{ "words": [ "huis", "muis", "bal", "vuur", "muur", "ik", "zon", "hok", "poep", "aap", "noot", "hij", "zij", "test", "woord" ] }

here is a link to the image I used: https://www.obsbeekbergen.nl/wp-content/uploads/sites/34/2016/04/dictee.gif

The output in the terminal when the program is shut down (again, no errors):

after after after clear text after update after creating entry box after positioning entry window after creating button after words.remove

I'm using python 3.8.5 with MacOs Mojave 10.14.6.

utmcontent commented 3 years ago
```python
   insert code here
\```

to reformat your code.It's hard to read.

3861EW commented 3 years ago
```python
   insert code here
\```

to reformat your code.It's hard to read.

Thanks!


import tkinter as tk
import sys, json, random, pyttsx3

#setup window
window = tk.Tk()
engine = pyttsx3.init()
window.geometry("500x500")
window.title("Aliyah's dictee spel")
window.configure(background = "black")

#my photo
photo1 = tk.PhotoImage(file="dictee.gif")
tk.Label (window, image=photo1, bg="black") .grid(row=0, column=0)

def main():
    """Setting up the game"""
    global WORDS_CORRECT, WORDS_WRONG, WORDS
    WORDS_CORRECT = 0
    WORDS_WRONG = 0
    with open('vocabulary.json') as json_file:
        data = json.load(json_file)
        WORDS = data["words"]
    for widget in window.winfo_children():
        widget.destroy()
    tk.Label (window, image=photo1, bg="black") .grid(row=0, column=0)
    tk.Label (window, text = "Dit is het dictee spel voor Aliyah. Wil je beginnen?", bg="black", fg="white", font="none 12 bold") .grid(row=1)
    tk.Button(window, text="Ja", width=6, command=lambda: dictee(WORDS)) .grid(row=2, column=0)
    tk.Button(window, text="Nee", width=6, command=sys.exit) .grid(row=2, column=1)
    tk.Button(window, text="Bewerk lijst", width=10, command=manage_list) .grid(row=3, column=1)

#def speak(WORD): this did not work, even the last print command in dictee() is executed in terminal
    #engine = pyttsx3.init()
    #nl_voice_id = "com.apple.speech.synthesis.voice.ellen"
    #engine.setProperty('rate', 100)
    #engine.setProperty('voice', nl_voice_id)
    #engine.say(WORD)
    #engine.runAndWait()

def dictee(WORDS):
    """Show random words and ask for input."""
    if WORDS == []:
        for widget in window.winfo_children():
            widget.destroy()
        tk.Label (window, image=photo1, bg="black") .grid(row=0, column=0)
        tk.Label (window, text = "Dat waren alle woordjes.", bg="black", fg="white", font="none 12 bold") .grid(row=1, column=0)
        tk.Label (window, text = "Woordjes goed: %d" % WORDS_CORRECT, bg="black", fg="white", font="none 12 bold") .grid(row=2, column=0)
        tk.Label (window, text = "Woordjes fout: %d" % WORDS_WRONG, bg="black", fg="white", font="none 12 bold") .grid(row=3, column=0)
        tk.Label (window, text = "Wil je het nog een keer proberen?", bg="black", fg="white", font="none 12 bold") .grid(row=4, column=0)
        tk.Button(window, text="Ja", width=6, command=main) .grid(row=5, column=0)
        tk.Button(window, text="Nee", width=6, command=exit_game) .grid(row=5, column=1)
        window.update()
    else:
        random.shuffle(WORDS)
        for widget in window.winfo_children():
            widget.destroy()
        WORD = WORDS[0]
        tk.Label (window, image=photo1, bg="black") .grid(row=0, column=0)
        word = tk.Label (window, text = WORD, bg="black", fg="white", font="none 20 bold")
        word.grid(row=1, column=0)
        window.update()
        #speak(WORD)this is related to speak()
        #engine = pyttsx3.init()
        nl_voice_id = "com.apple.speech.synthesis.voice.ellen"
        engine.setProperty('rate', 100)
        engine.setProperty('voice', nl_voice_id)
        engine.say(WORD) #WORD is being said!
        engine.runAndWait()
        #engine.stop() this did not work
        window.after(3000)
        print('after after')#all print commands after this are executed in terminal, all other lines are not executed
        word['text'] = ''
        print('after clear text')
        window.update()
        print('after update')
        ANSWER = tk.Entry(window, width=20, bg="white")
        print('after creating entry box')
        ANSWER.grid(row=2, column=0)
        print('after positioning entry window')
        tk.Button(window, text="Check", width=6, command=lambda: check(ANSWER,WORD)) .grid(row=3, column=0)
        print('after creating button')
        WORDS.remove(WORD)
        print('after words.remove')

def check(ANSWER, WORD):
    '''Cross check word shown with answer given'''
    global WORDS_CORRECT, WORDS_WRONG
    if ANSWER.get() == WORD:
        tk.Label (window, text = "Wat goed!", bg="black", fg="white", font="none 12 bold") .grid(row=4, column=0)
        WORDS_CORRECT += 1
    else:
        tk.Label (window, text = "Jammer!", bg="black", fg="white", font="none 12 bold") .grid(row=4, column=0)
        WORDS_WRONG += 1

    tk.Label (window, text = "Wil je het nog een keer proberen?", bg="black", fg="white", font="none 12 bold") .grid(row=5, column=0)
    tk.Button(window, text="Ja", width=6, command=lambda: dictee(WORDS)) .grid(row=6, column=0)
    tk.Button(window, text="Nee", width=6, command=exit_game) .grid(row=6, column=1)

def manage_list():
    '''manage the list of words by user'''
    with open('vocabulary.json', "r") as f:
        data = json.load(f)
        WORDS = data["words"]
    for widget in window.winfo_children():
        widget.destroy()
    window.update()
    tk.Label (window, text = "Dit zijn alle woorden in de vocabulaire", bg="black", fg="white", font="none 12 bold") .grid(row=0, column=0)
    tk.Label (window, text = WORDS, bg="black", fg="white", font="none 12 bold") .grid(row=1, column=0)
    tk.Label (window, text = "Wil je woorden toevoegen of verwijderen?", bg="black", fg="white", font="none 12 bold") .grid(row=2, column=0)
    tk.Button(window, text="alles verwijderen", command=lambda: clear_words('vocabulary.json')) .grid(row=5, column=0)
    words_to_add = tk.Entry(window, width=40, bg="white")
    words_to_add.grid(row=3, column=0)
    tk.Button(window, text="toevoegen", width=6, command=lambda: add_words('vocabulary.json', words_to_add.get())) .grid(row=8, column=2)
    tk.Button(window, text="hoofdmenu", width=6, command=main) .grid(row=10, column=0)

def add_words(filename, wordt_text):
    with open(filename, "r+") as f:
        data = json.load(f)
        data["words"].extend(wordt_text.split())
        f.seek(0)
        json.dump(data, f, indent=4)
    manage_list()

def clear_words(filename):
    with open('vocabulary.json', "w+") as f:
        data = {"words":[]}
        json.dump(data, f, indent=4)
    manage_list()

def exit_game():
    '''summarize results and exit after pushing enter'''
    for widget in window.winfo_children():
        widget.destroy()
    tk.Label (window, image=photo1, bg="black") .grid(row=0, column=0)
    tk.Label (window, text = "Tot de volgende keer.", bg="black", fg="white", font="none 12 bold") .grid(row=1, column=0)
    tk.Label (window, text = "Woordjes goed: %d" % WORDS_CORRECT, bg="black", fg="white", font="none 12 bold") .grid(row=2, column=0)
    tk.Label (window, text = "Woordjes fout: %d" % WORDS_WRONG, bg="black", fg="white", font="none 12 bold") .grid(row=3, column=0)
    tk.Label (window, text = "Klik op OK om af te sluiten", bg="black", fg="white", font="none 12 bold") .grid(row=4, column=0)
    tk.Button(window, text="OK", width=6, command=sys.exit) .grid(row=5, column=0)

if __name__ == "__main__":
    main()

window.mainloop()
\```
TotallyAProgrammer commented 3 years ago

@3861EW Hey, if this is still an issue, I might be able to help. pyttsx3 is blocking in how it executes, and so is tkinter. This can create issues when things are executing on the main thread. Not to mention a few bugs in pyttsx3 cause it to not work properly after runAndWait is called sometimes. To cope with this I use this function:

def speak(text=None):
    """
    Speak
    """

    import pyttsx3

    # Initialize the engine
    engine = pyttsx3.init(driverName=None, debug=True)

    # Get and set female voice, because in this case Ava is a female.
    voices = engine.getProperty('voices')
    engine.setProperty('voice', voices[1].id)

    # Speak
    engine.say(text)

    # Blocking method! Process all queued TTS commands
    engine.runAndWait()

When called with an input this function will speak what text is passed into it. If you have any questions. feel free to ask.

3861EW commented 3 years ago

When called with an input this function will speak what text is passed into it. If you have any questions. feel free to ask. @TotallyAProgrammer Hi, thanks for replying.. Apparently i did not get a notice that someone commented.. You have to understand that I'm a total noob when it comes to Python or programming in general and I tried to follow your comment.. I did not quite get how to implement your answer, and also what the difference is with my solution on using the engine as a method (which did not work):


#def speak(WORD): this did not work, even the last print command in dictee() is executed in terminal
    #engine = pyttsx3.init()
    #nl_voice_id = "com.apple.speech.synthesis.voice.ellen"
    #engine.setProperty('rate', 100)
    #engine.setProperty('voice', nl_voice_id)
    #engine.say(WORD)
    #engine.runAndWait()
\```

is it the debug=True that I'm missing?
TotallyAProgrammer commented 3 years ago

Howdy, The debug=True is just for extra debugging info to make issues in code execution easier to find. I recommend using it when developing. I do not program on Mac, nor do I work with the NSSS engine. I also forgot to read your code when I first commented.

Try using the builtin pyttsx3 speak() function, and see if that fixes your issue. pyttsx3.speak("Hello, World!") And to avoid rewriting a bunch of code you can change your speak function to: def speak(WORD): pyttsx3.speak(WORD) runAndWait has weird issues, and I think that is the source of your issue, though I don't fully understand what the issue is.

3861EW commented 3 years ago

@TotallyAProgrammer thanks for bearing with me :-)

I put your pyttsx3.speak("Hello, World!")recomendation in the dictee() function. But, same result. Program crashes after word is being said. Just to check how far it is running in the background, I put in print commands after pyttsx3.speak(WORD) => which is the moment where it crashes. All print commands are executed and are visible in the terminal, but all the lines in between are ignored. for example it prints ('after entry')but the line before it:

ANSWER = tk.Entry(window, width=20, bg="white")
        ANSWER.grid(row=2, column=0)

is ignored and the program comes to a stop after the last line in this function. Just to be complete, engine.runAndWait() has been commented out. I know it's a bug on the MacOs I'm using, since on windows it works fine, but there the speak engine is really bad...

here is the the function I edited:


def dictee(WORDS):
    #Show random words and ask for input.
    if WORDS == []:
        for widget in window.winfo_children():
            widget.destroy()
        tk.Label (window, image=photo1, bg="black") .grid(row=0, column=0)
        tk.Label (window, text = "Dat waren alle woordjes.", bg="black", fg="white", font="none 12 bold") .grid(row=1, column=0)
        tk.Label (window, text = "Woordjes goed: %d" % WORDS_CORRECT, bg="black", fg="white", font="none 12 bold") .grid(row=2, column=0)
        tk.Label (window, text = "Woordjes fout: %d" % WORDS_WRONG, bg="black", fg="white", font="none 12 bold") .grid(row=3, column=0)
        tk.Label (window, text = "Wil je het nog een keer proberen?", bg="black", fg="white", font="none 12 bold") .grid(row=4, column=0)
        tk.Button(window, text="Ja", width=6, command=main) .grid(row=5, column=0)
        tk.Button(window, text="Nee", width=6, command=exit_game) .grid(row=5, column=1)
        window.update()
    else:
        random.shuffle(WORDS)
        for widget in window.winfo_children():
            widget.destroy()
        WORD = WORDS[0]
        tk.Label (window, image=photo1, bg="black") .grid(row=0, column=0)
        word = tk.Label (window, text = WORD, bg="black", fg="white", font="none 20 bold")
        word.grid(row=1, column=0)
        window.update()
        pyttsx3.speak(WORD)
        #speak(WORD)this is related to speak()
        #engine = pyttsx3.init()
        #nl_voice_id = "com.apple.speech.synthesis.voice.ellen"
        #engine.setProperty('rate', 100)
        #engine.setProperty('voice', nl_voice_id)
        #engine.say(WORD) #WORD is being said!
        #engine.runAndWait()
        #engine.stop() this did not work
        window.update()
        print('after .update')
        window.after(3000)
        print('after .after')#all print commands after this are executed in terminal, all other lines are not executed
        word['text'] = ''
        print('after text')
        window.update()
        print('after last .update')
        ANSWER = tk.Entry(window, width=20, bg="white")
        ANSWER.grid(row=2, column=0)
        print('after creating entry')
        tk.Button(window, text="Check", width=6, command=lambda: check(ANSWER,WORD)) .grid(row=3, column=0)
        print('after button')
        WORDS.remove(WORD)
        print('after remove word')```