tkinter und pyttsx3

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Septias
User
Beiträge: 80
Registriert: Freitag 24. Juni 2016, 19:15

Ich bin dabei ein Text-adventure-spiel zu programmieren. Dafür erstelle ich bei Klicken auf einen Button immer neue Buttons und lösche die Alten, außerdem verändere den Text eines Texfeldes. Jetzt möchte ich, dass der Text des Texfeldes bei Erscheinen des aktualiesierten Windows mit tts ausgegeben wird. Das Problem ist jetzt, dass der Text erst von pyttsx3 vollkommen ausgegben wird, bevor tkinter einen neuen Schleifendurchlauf starten kann, und somit das Fenster neu zeichnet. Das resultiert darin, dass man immer noch den alten Text sieht, wenn der Neue ausgegeben wird.

Wie kann ich es hinbekommen, dass erst das Fenster aktualisiert wird, und dann der Text ausgegeben ?

Ich habe es schon mit Multiprocessing versucht, aber wenn ich es so mache:

Code: Alles auswählen

        p = multiprocessing.Process(target=engine.runAndWait)
        p.start()
bekomme ich diesen Fehler:

Code: Alles auswählen

        An attempt has been made to start a new process before the
        current process has finished its bootstrapping phase.

        This probably means that you are not using fork to start your
        child processes and you have forgotten to use the proper idiom
        in the main module:

            if __name__ == '__main__':
                freeze_support()
                ...

        The "freeze_support()" line can be omitted if the program
        is not going to be frozen to produce an executable.
Ich schätze mal, das hängt damit zusammen, dass Tkinter auch noch multiprogressing verwendet. Weil in meinem Code wird erst nach dem Drücken eines Knopfes ein weitere Text und somit ein weitere Prozess gestartet.
Für alle meine Codebeispiele gilt: Äußert bitte jegliche Art von Verbesserungsvorschlägen. Ich versuche immer meinen Stil zu verbessern und wenn man mir einfach sagt, was ich falsch machen, ist es um einiges einfacher, als wenn ich es mühselig selber herausfinden muss :-)
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@Septias: zeig doch den Code, der was mit dem Problem zu tun hat. Wo wird welcher Text geändert und was bleibt dann noch stehen?
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Septias: Ich würde für die Sprachausgabe einen eigenen Thread starten (`threading`-Modul) und über eine `Queue` die Texte die gesprochen werden sollen an diesen Thread kommunizieren.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Septias
User
Beiträge: 80
Registriert: Freitag 24. Juni 2016, 19:15

Hier der Code für das Setzen des Textes:

Code: Alles auswählen

    def set_text(self, text):
        self.text.config(state=tk.NORMAL)
        self.text.delete(1.0, tk.END)
        self.text.insert(1.0, text)
        self.text.config(state=tk.DISABLED)
        engine.say(text)
        p = multiprocessing.Process(target=engine.runAndWait)
        p.start()
@__blackjack__: Was soll mir das Code-Beispiel sagen ?
Für alle meine Codebeispiele gilt: Äußert bitte jegliche Art von Verbesserungsvorschlägen. Ich versuche immer meinen Stil zu verbessern und wenn man mir einfach sagt, was ich falsch machen, ist es um einiges einfacher, als wenn ich es mühselig selber herausfinden muss :-)
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Septias: Welches Code-Beispiel? Meine Signatur? Das ist die Einschaltmeldung vom besten Computersystem ever. :-D
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

Threads gehen so:

Code: Alles auswählen

from threading import Thread
from queue import Queue
def say_loop(queue):
    engine = pyttsx3.init()
    while True:
        text = queue.get()
        engine.say(text)
        engine.runAndWait()

# somewhere in __init__:
#   def __init__(self):
        self.text_queue = Queue()
        say_thread = Thread(target=say_loop, args=(self.text_queue,))
        say_thread.deamon = True
        say_thread.start()

    def set_text(self, text):
        self.text.config(state=tk.NORMAL)
        self.text.delete(1.0, tk.END)
        self.text.insert(1.0, text)
        self.text.config(state=tk.DISABLED)
        self.text_queue.put(text)
Septias
User
Beiträge: 80
Registriert: Freitag 24. Juni 2016, 19:15

Okay, vielen Dank ! Hat alles super funktioniert :)
Für alle meine Codebeispiele gilt: Äußert bitte jegliche Art von Verbesserungsvorschlägen. Ich versuche immer meinen Stil zu verbessern und wenn man mir einfach sagt, was ich falsch machen, ist es um einiges einfacher, als wenn ich es mühselig selber herausfinden muss :-)
Septias
User
Beiträge: 80
Registriert: Freitag 24. Juni 2016, 19:15

Okay, doch noch nicht vorbei... beim weiteren Testen ist mir aufgefallen, dass der Text weiter vorgelesen wird, wenn ich auf einen Button drücke. Eigentlich sollte das Vorlesen dann abgebrochen werde, und der neue Text gestartet.
Theoretisch könnte ich ja einfach die Queue raus lassen, und für jedes Sprechen einen eigenen Thread starten, und den dann vorzeitig zerstören, wenn auf einen Knopf gedrückt wird. Diese Methode scheint mir aber nicht richtig, weil sonst hätte man ja auch bei dem Beispiel oben einfach die Queue raus lassen können. Kann ich das trotzdem so machen, oder gibt es einen besseren Weg das zu realisieren ?
Für alle meine Codebeispiele gilt: Äußert bitte jegliche Art von Verbesserungsvorschlägen. Ich versuche immer meinen Stil zu verbessern und wenn man mir einfach sagt, was ich falsch machen, ist es um einiges einfacher, als wenn ich es mühselig selber herausfinden muss :-)
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Septias: Bietet pyttsx3 denn überhaupt einen Weg die Ausgabe abzubrechen? Threads vorzeitig zerstören, so wie Du das beschreibst, geht jedenfalls nicht.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@Septias: man kann wohl was mit startLoop und endLoop machen. Aber da das hier kein Programmierservice ist, mußt Du Dich selbst in die Bibliothek einlesen und ausprobieren, wie das gemacht werden kann.
Septias
User
Beiträge: 80
Registriert: Freitag 24. Juni 2016, 19:15

@__blackjack__: Da hab ich tatsächlichlich auch dran gedacht... nur nach dem Stellen der Frage... Also ja, so eine Funktion gibt es, und die habe ich auch dann verwendet.

Vielen Danke für die ganze Hilfe !
Für alle meine Codebeispiele gilt: Äußert bitte jegliche Art von Verbesserungsvorschlägen. Ich versuche immer meinen Stil zu verbessern und wenn man mir einfach sagt, was ich falsch machen, ist es um einiges einfacher, als wenn ich es mühselig selber herausfinden muss :-)
Antworten