main thread is not in main loop

Fragen zu Tkinter.
Antworten
Benutzeravatar
Bio Salami
User
Beiträge: 63
Registriert: Mittwoch 28. Juli 2021, 14:10

Hi,
ich versuche eine Gut für einen Sprachassistenten zu programmieren. Leider habe ich keine Lösung für den "main thread is not in main loop" error gefunden und bin ein bisschen verzweifelt. (Ich weis das ich das Programm an einigen stellen umständlich geschrieben habe)

Code: Alles auswählen

from tkinter import *
import cv2
import pyttsx3
import threading
import PIL.Image, PIL.ImageTk
from chat import ChatBot
from faceRecognitionGui import faceRecognition
import speech_recognition as sr
import os
import sys
from playsound import playsound
import os, json, time, webbrowser, requests, wikipediaapi, pywhatkit, re, phonenumbers, datetime
from time import sleep
from playsound import playsound
from phonenumbers import geocoder as PhoneGeocoder
from phonenumbers import carrier
from chat import ChatBot

#from bibliothek import respond
#from speak import record_audio

BG_WHITE = "#ffffff"
BG_COLOR = "#e6e6e6"
TEXT_COLOR = "#000000"

FONT = "Helvetica 14"
FONT_BOLD = "Helvetica 13 bold"

listen = True
answere_spoke = True

stop = False
standby = False

class EveGui():

    def __init__(self, window):
        self.window = window
        self._setup_main_window()

        self.var1 = BooleanVar()
        self.var2 = BooleanVar()

        self.var1.set(True)
        self.var2.set(True)
    
    def run(self):
        self.window.title("EVE")
        self.window.resizable(width=False, height=False)
        self.window.configure(width=600, height=440, bg=BG_COLOR)
        self.window.mainloop()
        global stop
        stop = True

    def _setup_main_window(self):

        # head label
        #head_label = Label(self.window, bg=BG_COLOR, fg=TEXT_COLOR, text="Welcome", font=FONT_BOLD, pady=5)
        #head_label.place(relwidth=1)
        self.controll_button = Button(self.window, bg=BG_COLOR, fg=TEXT_COLOR, text="Einstellungen", font=FONT_BOLD, pady=5, command=lambda: self._setup_settings_window())
        self.controll_button.place(relwidth=0.2)

        # tiny divider
        self.line = Label(self.window, width=450, bg=BG_WHITE)
        self.line.place(relwidth=1, rely=0.07, relheight=0.012)

        # text widget
        self.text_widget = Text(self.window, width=20, height=2, bg=BG_COLOR, fg=TEXT_COLOR,font=FONT, padx=5, pady=5)
        self.text_widget.place(relheight=0.745, relwidth=1, rely=0.08)
        self.text_widget.configure(cursor="arrow", state=DISABLED)

        # scroll bar
        #scrollbar = Scrollbar(self.text_widget)
        #scrollbar.place(relheight=1, relx=0.974)
        #scrollbar.configure(command=self.text_widget.yview)

        # bottom label
        self.bottom_label = Label(self.window, bg=BG_WHITE, height=80)
        self.bottom_label.place(relwidth=1, rely=0.825)

        try:
            speech_recognition = self.var2.get()
        except:
            speech_recognition = True

        if speech_recognition:
            global standby
            standby = False
            self.standby_button = Button(self.bottom_label, text="Standby", font=FONT_BOLD, width=20, bg=BG_WHITE, command=lambda: self._standby_listener(None))
            self.standby_button.place(relx=0.77, rely=0.008, relheight=0.03, relwidth=0.22)

            self.state = Label(self.bottom_label, bg=BG_COLOR, fg=TEXT_COLOR, text="Höre", font=FONT_BOLD, pady=5)
            self.state.place(relwidth=0.74, relheight=0.03, rely=0.008, relx=0.011)
        else:
            # message entry box
            self.msg_entry = Entry(self.bottom_label, bg=BG_WHITE, fg=TEXT_COLOR, font=FONT)
            self.msg_entry.place(relwidth=0.74, relheight=0.03, rely=0.008, relx=0.011)
            self.msg_entry.focus()
            self.msg_entry.bind("<Return>", self._on_enter_pressed)
        
            # send button
            self.send_button = Button(self.bottom_label, text="Senden", font=FONT_BOLD, width=20, bg=BG_WHITE, command=lambda: self._on_enter_pressed(None))
            self.send_button.place(relx=0.77, rely=0.008, relheight=0.03, relwidth=0.22)


    def _on_enter_pressed(self, event):
        msg = self.msg_entry.get()
        self._insert_message(msg)

    def _setup_settings_window(self):
        self.line.place_forget()
        self.text_widget.place_forget()
        self.bottom_label.place_forget()
        try:
            self.msg_entry.place_forget()
            self.send_button.place_forget()
        except:
            pass
        try:
            self.standby_button.place_forget()
        except:
            pass

        self.controll_button = Button(self.window, bg=BG_COLOR, fg=TEXT_COLOR, text="Schließen", font=FONT_BOLD, pady=5, command=lambda: self._clear_settings_window())
        self.controll_button.place(relwidth=0.2)

        self.spech_output = Checkbutton(self.window, text="Sprachausgabe", variable=self.var1)
        self.spech_output.place(relwidth=1, rely=0.07, relheight=0.5)
        self.spech_input = Checkbutton(self.window, text="Spracherkennung", variable=self.var2)
        self.spech_input.place(relwidth=1, rely=0.5, relheight=0.5)
    
    def _disable_input(self):
        self.msg_entry.config(state = DISABLED)
        self.send_button.config(state = DISABLED)

    def _enable_input(self):
        self.msg_entry.config(state = NORMAL)
        self.send_button.config(state = NORMAL)
    
    def _change_state(self, msg):
        self.state.config(text = msg)

    def _clear_settings_window(self):
        global listen
        global answere_spoke

        self.spech_input.place_forget()
        self.spech_output.place_forget()

        answere_spoke = self.var1.get()
        listen = self.var2.get()

        print(answere_spoke)
        print(listen)

        self._setup_main_window()

    def _insert_message_user(self, msg):
        #if not msg:
        #    return

        try:
            self.msg_entry.delete(0, END)
        except:
            pass
        msg1 = f"{msg}\n\n"
        
        self.text_widget.configure(state=NORMAL)
        self.text_widget.insert(END, msg1)
        self.text_widget.configure(state=DISABLED)
        

    def _insert_message_eve(self, eve):
        msg2 = f"Eve: {eve}\n\n"                                       #Da rein
        self.text_widget.configure(state=NORMAL)
        self.text_widget.insert(END, msg2)
        self.text_widget.configure(state=DISABLED)

        self.text_widget.see(END)
    
    def _standby_listener(self, _):
        global standby
        standby = True

        self.standby_button.configure(self.bottom_label, text="Start", font=FONT_BOLD, width=20, bg=BG_WHITE, command=lambda: self._start_listener(None))
    
    def _start_listener(self, _):
        global standby
        standby = False
        self.standby_button.configure(self.bottom_label, text="Standby", font=FONT_BOLD, width=20, bg=BG_WHITE, command=lambda: self._standby_listener(None))

window = Tk()
app = EveGui(window)

class Eve(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
    
    def run(self):
        global stop
        while not stop:
            voice_data = self.listener()
            app._insert_message_user(voice_data)
            self.biblio(voice_data)


    def speaker(self, text):
        voice = pyttsx3.init()
        voice.setProperty('rate', 200)
        if text == "" or text == " ":
            app._insert_message_eve("Ich verstehe nicht")
            print("Ich verstehe nicht")
            voice.say("Ich verstehe nicht")
            voice.runAndWait()
        else:
            try:
                print(text)
                app._insert_message_eve(text)
                voice.say(text)
                voice.runAndWait()
            except TypeError:
                pass

    def listener(self, ask=False):
        global listen
        global stop
        global standby
        while True:
            while listen and not standby:
                with sr.Microphone() as source:
                    if ask:
                        self.speaker(ask)
                    
                    r= sr.Recognizer()
                    print("wait for order")
                    audio = r.listen(source, timeout=20, phrase_time_limit=4)
                    voice_data = ''

                    try:
                        print("send order to Server")
                        voice_data = r.recognize_google(audio, language="de-DE",)
                        print("order detected! " + voice_data)
                    except sr.UnknownValueError:
                        print("order not understood")
                    except sr.RequestError:
                        print("server not accessible")

                    if not listen or stop:
                        break
                    else:
                        #app._insert_message(voice_data)
                        return voice_data
            if stop:
                break
    
    def biblio(self, voice_data):
        if 'suche Ort' in voice_data or 'suche den Ort' in voice_data or 'wo liegt' in voice_data:
            location = ''
            trash = ''

            try:
                if 'suche den Ort' in voice_data:
                    trash,location = voice_data.split('suche den Ort')
                elif 'wo liegt' in voice_data:
                    trash,location = voice_data.split('wo liegt')
            except ValueError:
                location = self.listener("Welchen Ort suchst du?")

            while location == '':
                location = self.listener('Welchen Ort suchst du?')
            url = 'https://google.nl/maps/place/' + location + '/&amp;'
            webbrowser.get().open(url)
            self.speaker('Hier ist der Ort ' + location)
        elif 'Suche nach' in voice_data or 'suche' in voice_data or 'Google Suche' in voice_data:
            trash = ''
            search = ''

            try:
                trash,search = voice_data.split('Suche nach')
            except ValueError:
                search = self.listener("Nach was suchst du?")
            while search == '':
                search = self.listener('Nach was suchst du?')
            url = 'https://google.com/search?q=' + search
            webbrowser.get().open(url)
            self.speaker('Hier ist mein suchergebnis für ' + search)  
        elif 'was weißt du über' in voice_data or 'weißt du was über' in voice_data or 'was ist' in voice_data or 'Wer war' in voice_data or 'Wer ist' in voice_data or 'Wikipedia Suche' in voice_data:
            self.speaker("Ich schaue mal nach")

            wp = wikipediaapi.Wikipedia("de")
            info = ''
            question = ''
            trash = ''

            try:
                if 'was weißt du über' in voice_data:
                    trash,question = voice_data.split('was weißt du über')
                elif 'weißt du was über' in voice_data:
                    trash,question = voice_data.split('weißt du was über')
                elif 'was ist' in voice_data:
                    trash,question = voice_data.split('was ist')
                elif 'Wer war' in voice_data:
                    trash,question = voice_data.split('Wer war')
                elif 'Wer ist' in voice_data:
                    trash,question = voice_data.split('Wer ist')
            except ValueError:
                pass
            
            while question == '':
                question = self.listener("Was willst du wissen?")

            p = wp.page(question)
            ans_all = wp.extracts(p)
            ans = wp.extracts(p, exsentences=4)

            self.speaker(ans)

            while info == '':
                info = self.listener("Willst du mehr hören?")
            if 'ja' in info:
                trash,ans = ans_all.split(ans)
                self.speaker(ans)     
        elif 'sende eine nachricht' in voice_data or 'sende eine Nachricht an' in voice_data:
            trash = ''
            name = ''
            nachricht = ''
            bestätigung = ''
            try:
                if 'sende eine Nachricht an' in voice_data:
                    trash,name = voice_data.split('sende eine Nachricht an')
            except ValueError:
                name = self.listener("An wen geht die Nachricht?")
            while name == '':
                name = self.listener("An wen geht die Nachricht?")
            while nachricht == '':
                nachricht = self.listener(f"Was möchtest du {name} senden?")
            while bestätigung == '':
                bestätigung = self.listener(f"Soll ich die Nachricht: {nachricht} an {name} absenden?")
            if 'ja' in bestätigung or 'absenden' in bestätigung:
                try:
                    trash,name = name.split(' ')
                except:
                    """
                    """
                with open('numbers.json') as nmb:
                    numbers = json.load(nmb)
                try:
                    number = numbers[name]
                    print(number)

                    self.speaker("Nachricht wird gleich versendet")
                    pywhatkit.sendwhatmsg_instantly(number[0], nachricht,tab_close=True)
                    self.speaker("Die Nachricht wurde versendet")
                except KeyError:
                    self.speaker(f"Ich habe {name} nicht gefunden")
            else:
                self.speaker("Ich habe die Nachricht nicht gesendet")
        elif 'übersetze' in voice_data or 'Übersetze' in voice_data:
            text = ''
            übersetzen_sprache = ''
            sprache = ''
            trash = ''
            try:
                if 'Übersetzer' in voice_data:
                    """
                    Nothing
                    """
                elif 'übersetze' in voice_data:
                    trash,text = voice_data.split('übersetze')
                elif 'Übersetze' in voice_data:
                    trash,text = voice_data.split('Übersetze')
            except ValueError:
                """
                """
            while text == '':
                text = self.listener("Was soll ich übersetzen")
            while sprache == '':
                sprache = self.listener("In welche sprache soll ich übersetzen?")
            try:
                datei = open(f'./languadge/{sprache}.txt','r')
                übersetzen_sprache = datei.readline()
                url = f'https://translate.google.de/?sl=auto&tl={übersetzen_sprache}&text={text}&op=translate'
                webbrowser.get().open(url)
                self.speaker("Hier ist die übersetzung")
            except FileNotFoundError:
                self.speaker("In diese Sprache kann ich noch nicht übersetzen")
        elif 'Öffne' in voice_data or 'öffne' in voice_data:
            try:
                if 'Öffne' in voice_data:
                    trash,voice_data = voice_data.split('Öffne ')
                elif 'öffne' in voice_data:
                    trash,voice_data = voice_data.split('öffne ')
                else:
                    self.speaker("Anscheinend ist etwas schiefgegangen")
            except ValueError:
                self.speaker("Anscheinend ist etwas schiefgegangen")

            try:
                if 'den' in voice_data:
                    trash,voice_data = voice_data.split('den ')
                elif 'die' in voice_data:
                    trash,voice_data = voice_data.split('die ')
            except ValueError:
                pass
                
            program = voice_data.lower()

            with open('programms.json') as path:
                programms = json.load(path)
            try:
                programm_path = programms[program]
                print("Öffne Programm")
                os.system(programm_path[0])
            except KeyError:
                self.speaker("Ich habe " + voice_data + " nicht gefunden")
        else:
            sentence = ChatBot(voice_data)
            try:
                if '## ' in sentence:
                    sentence = sentence.split('## ')
                else:
                    self.speaker(sentence)
            except TypeError:
                sentence = 'nothing here'
            if '' == sentence:
                        sentence = 'nothing here'
            elif 'time' in sentence:
                self.speaker("Es ist " + time.strftime('%H:%M', time.localtime()) + "Uhr")
            elif 'sss' in sentence:
                spielzug_sss = ''

                self.speaker("Schnik Schnak Schnuk")

                while spielzug_sss == '':
                    spielzug_sss = self.listener('Was hast du?')

                if 'Schere' in spielzug_sss:
                    self.speaker("Ich habe Stein.")
                elif 'Stein' in spielzug_sss:
                    self.speaker("Ich habe Papier")
                elif 'Papier' in spielzug_sss:
                    self.speaker("Ich habe Schere")

                self.speaker("Ich habe gewonnen")
            elif 'ausschalten' in sentence:
                playsound('audio/alert2.wav')
                exit()
            elif 'wetter' in sentence:
                self.speaker(self.weather()) 

    
    def weather(self):
        weather_key = "Der Key"
        city = "Die Stadt"



        url = f'https://api.openweathermap.org/data/2.5/weather?q={city}&appid={weather_key}&units=metric&lang=de'


        data = requests.get(url)
        weather_data = data.json()


        tmp = format(str(weather_data["main"]["temp"]))
        tmp,_ = tmp.split('.')
        max_tmp = format(str(weather_data["main"]["temp_max"]))
        max_tmp,_ = max_tmp.split('.')
        min_tmp = format(str(weather_data["main"]["temp_min"]))
        min_tmp,_ = min_tmp.split('.')
        clouds = weather_data["clouds"]["all"]


        if clouds <= 10:
            clouds = "Es sollten keine Wolken am Himmel sein."
        elif clouds <= 40:
            clouds = "Es ist teilweise Bewölkt."
        elif clouds <= 100:
            clouds = "Es ist gerade sehr Bewölkt."

        return f"Die Temperatur beträgt {tmp}°. Die maximale Temperatur liegt bei {max_tmp}° und die minnimale Temperatur liegt bei {min_tmp}°. {clouds}"

if __name__ == "__main__":
    #faceRecognition(window)
    eve = Eve()
    eve.start()
    app.run()

Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Bio Salami: Die GUI muss halt im Hauptthread laufen.

Sonstiges: Du verwendest Klassen, aber trotzdem sind da in Methoden ``global``-Deklarationen. Da gibt es keine Ausrede für, die müssen weg.

Zwischen zwei grossen Klassendefinitionen versteckt stehen zwei kleine Zeilen auf Modulebene die zwei globale Variablen definieren → Das Hauptprogramm gehört an eine Stelle, und auch nicht Modulebene, sondern in eine Funktion.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Importe müssen aufgeräumt werden. Keine *-Importe, keine doppelten Importe. Nur ein Modul pro Import-Zeile.
Deine Fehlerbehandlung ist falsch. Man darf nicht einfach irgendeinen Code in try-Blöcke packen, weil sie irgendwie hin und wieder fehlschlagen, sondern man sollte den konkreten Fall, was schief läuft, abfangen und passend darauf reagieren.
Zudem sollte ein place_forget in einem Normalen GUI-Programm nicht vorkommen. Fenster werden einmal aufgebaut und dann nur verändert.
Global darf es gar nicht geben, erst recht nicht in Zusammenhang von Threads oder GUIs.
Innerhalb eines Threads darf die GUI auf keinen Fall verändert werden.

Die biblio-Methode ist viel zu lang. Man belegt keine Variablen auf Vorrat mit leeren Strings.
Die leeren Strings in except-Blöcken sind quatsch. os.system benutzt man nicht, statt dessen das subprocess-Modul. exit hat in einem ordentlichen Programm nichts zu suchen, erst recht nicht irgendwo tief verschachtelt in einer Funktion.
In `weather` formatierst Du Parameter in URLs hinein, statt sie richtig per params-Argument an requests.get zu übergeben. Die Temperaturen in Strings umzuwandeln ist unschön, das als Argument an format zu übergeben, ein schwer zu entdeckender Programmierfehler. Um die Nachkommastellen loszuwerden gibt es Formatangaben.

Das beste ist, Du fängst mit einer kleinen GUI und einem kleinen Thread an, und schreibst das erst wirklich sauber und ordentlich.
Jetzt bei knapp 500 Zeilen die Probleme alle lösen zu wollen, ist nicht zielführend.
Benutzeravatar
Bio Salami
User
Beiträge: 63
Registriert: Mittwoch 28. Juli 2021, 14:10

@__blackjack__,
wie kann ich die Gut bearbeiten ohne mit einer Funktion auf die Klasse zu zugreifen?
__deets__
User
Beiträge: 14536
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du musst Nachrichten an die GUI schicken zb via Queue, und die in der GUI regelmäßig abholen, zb über die after Methode. Ist hier endlos oft diskutiert worden, bitte mal im tkinter Forum etwas stöbern.
Benutzeravatar
Dennis89
User
Beiträge: 1154
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

nur noch ein kleiner Hinweis, da ich nicht verstehe was eine/r "Gut" sein soll.
https://de.wikipedia.org/wiki/Grafische ... l%C3%A4che

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Antworten