Loop

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

Hi,
ich Programmiere einen Sprachassistent und habe Probleme eine UI mit Tkinter einzubinden. Der Sprachassistent läuft in einem loop der die Ganze zeit die Audioaufnahme startet. Bisher habe ich keine Möglichkeit gefunden einen loop in Tkinter zu schreiben. Ich kann zwar einen loop durch einen Button starten. Leider wird der Cursor zum ladebalken. Hilfe wäre nett.
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@Bio Salami,

bei einer GUI wie Tkinter läuft die grafische Darstellung der Oberfläche in einer Schleife. D.h. wenn du eine Schleife durch einen Button startest, friert die Obefläche wärend deiner Schleifenausführung ein.
Daher muss man Aufgaben, die parallel zur Oberflächendarstellung funktionieren sollen, in einem zusätzlichen separaten Thread laufen lassen. Dann können Oberflächendarstellung und Hintergrundaufgabe parallel laufen.

Wie das konkret bei die aussehen müsste, kann man aber nur am Codebeispiel sagen.
Benutzeravatar
Bio Salami
User
Beiträge: 63
Registriert: Mittwoch 28. Juli 2021, 14:10

@rogerb,
kannst du mir schreiben wie sowas aussieht. Ich brauche halt eine die die befehle:

Code: Alles auswählen

voice_data = record_audio()
if voice_data != '':
      respond(voice_data)
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

Ein leerer String gilt auch als False. Und wenn der String mindestens ein Zeichen enthält gilt er als True:

Code: Alles auswählen

voice_data = record_audio()
if voice_data:
      respond(voice_data)
Ich verstehe aber nicht was das mit der Frage von vorher zu tun hat. Wo ist die Schleife? und Wie wird dieser Code in Tkinter aufgerufen?
Benutzeravatar
Bio Salami
User
Beiträge: 63
Registriert: Mittwoch 28. Juli 2021, 14:10

@rogerb,
Sorry. Hier ist der code der Tkinter Datei.

Code: Alles auswählen

from tkinter import *

from bibliothek import respond
from speak import eve_speak, record_audio


BG_GRAY = "#ABB2B9"
BG_COLOR = "#17202A"
TEXT_COLOR = "#EAECEE"

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

class ChatApplication:
    
    def __init__(self):
        self.window = Tk()
        self._setup_main_window()
        
    def run(self):
        self.window.mainloop()
        
    def _setup_main_window(self):
        self.window.title("EVE")
        self.window.resizable(width=False, height=False)
        self.window.configure(width=470, height=550, bg=BG_COLOR)
        
        # head label
        #head_label = Label(self.window, bg=BG_COLOR, fg=TEXT_COLOR,
        #                   text="Welcome", font=FONT_BOLD, pady=10)
        #head_label.place(relwidth=1)
        
        # tiny divider
        #line = Label(self.window, width=450, bg=BG_GRAY)
        #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
        bottom_label = Label(self.window, bg=BG_GRAY, height=80)
        bottom_label.place(relwidth=1, rely=0.825)
        
        # message entry box
        self.msg_entry = Entry(bottom_label, bg="#2C3E50", fg=TEXT_COLOR, font=FONT)
        self.msg_entry.place(relwidth=0.74, relheight=0.06, rely=0.008, relx=0.011)
        self.msg_entry.focus()
        self.msg_entry.bind("<Return>", self._on_enter_pressed)
        
        # send button
        send_button = Button(bottom_label, text="Send", font=FONT_BOLD, width=20, bg=BG_GRAY,
                             command=lambda: self._on_enter_pressed(None))
        send_button.place(relx=0.77, rely=0.008, relheight=0.06, relwidth=0.22)
     
    def _on_enter_pressed(self, event):

        pass
        
    def _insert_message(self, msg): #, ans
        if not msg:
            return
        
        self.msg_entry.delete(0, END)
        msg1 = f"Du: {msg}\n\n"
        self.text_widget.configure(state=NORMAL)
        self.text_widget.insert(END, msg1)
        self.text_widget.configure(state=DISABLED)
        
        msg2 = f"EVE: Antwort\n\n"
        self.text_widget.configure(state=NORMAL)
        self.text_widget.insert(END, msg2)
        self.text_widget.configure(state=DISABLED)
        
        self.text_widget.see(END)
             
        
if __name__ == "__main__":
    app = ChatApplication()
    app.run()

Der Code ist noch nicht final, sollte dann aber zum start aufgerufen werden. Das hier triggert den Sprachassistenten:

Code: Alles auswählen

voice_data = record_audio()
if voice_data != '':
    respond(voice_data)
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@Bio Salami,

Zunächst, mal einige Hinweise:
Der *- Import von Tkinter scheint zunächst vielleicht praktisch und man sieht das auch in vielen Dokumentationen, ist aber nicht ratsam, da sehr viele Namen in dein Skript importiert werden, die du meist gar nicht brauchst. Da man nicht genau weiß, was eigentlich importiert wurde, macht man leicht den Fehler diese Namen dann wieder selber zu überschreiben und wundert sich, dass es nicht funktioniert.
_unterstrichnamen für Funktionen sind eigentlich per Konvention für spezielle Zwecke reserviert. Die sollte man hier nicht verwenden.
Da ich nicht genau weiß, wie deine Sprachein- und Ausgabe funktioniert, habe ich da einfach eine Zählschleife eingesetzt. Es sollte möglich sein diese einfach durch deinen Code zu ersetzen.

Ich habe einen Thread für die Hintergrundarbeit erstellt und kommuniziere damit über eine Queue.
Außerdem wird der Button während der Schleife gesperrt, und nachher wieder entsperrt. Das ist dazu da, dass man die Schleife nicht neu starten kann während die vorherige noch läuft.
Das Texteingabefeld ist aber weiterhin aktiv und blockiert nicht.

Ich hoffe es ist selbsterklärend, sonst frag einfach nochmal.
Ich bin auch kein GUI-Experte, vielleicht hat ja jemand anders noch Verbesserungsvorschläge...

Code: Alles auswählen

import tkinter as tk
from threading import Thread
from queue import Queue
import time

from bibliothek import respond
from speak import eve_speak, record_audio


BG_GRAY = "#ABB2B9"
BG_COLOR = "#17202A"
TEXT_COLOR = "#EAECEE"

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


class ChatApplication:
    def __init__(self):
        self.window = tk.Tk()
        self.queue = Queue()
        self.background_task = BackgroundTask(self)
        self.background_task.start()
        self.setup_main_window()

    def run(self):
        self.window.mainloop()

    def setup_main_window(self):
        self.window.title("EVE")
        self.window.resizable(width=False, height=False)
        self.window.configure(width=470, height=550, bg=BG_COLOR)

        # head label
        # head_label = Label(self.window, bg=BG_COLOR, fg=TEXT_COLOR,
        #                   text="Welcome", font=FONT_BOLD, pady=10)
        # head_label.place(relwidth=1)

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

        # text widget
        self.text_widget = tk.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=tk.DISABLED)

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

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

        # message entry box
        self.msg_entry = tk.Entry(bottom_label, bg="#2C3E50", fg=TEXT_COLOR, font=FONT)
        self.msg_entry.place(relwidth=0.74, relheight=0.06, rely=0.008, relx=0.011)
        self.msg_entry.focus()
        self.msg_entry.bind("<Return>", self.on_enter_pressed)

        # send button
        self.send_button = tk.Button(
            bottom_label,
            text="Send",
            font=FONT_BOLD,
            width=20,
            bg=BG_GRAY,
            command=lambda: self.on_enter_pressed(None),
        )
        self.send_button.place(relx=0.77, rely=0.008, relheight=0.06, relwidth=0.22)

    def on_enter_pressed(self, event):
        self.queue.put(self.send_button)

    def insert_message(self, msg):  # , ans
        if not msg:
            return

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

        msg2 = f"EVE: Antwort\n\n"
        self.text_widget.configure(state=tk.NORMAL)
        self.text_widget.insert(tk.END, msg2)
        self.text_widget.configure(state=tk.DISABLED)

        self.text_widget.see(tk.END)


class BackgroundTask(Thread):
    def __init__(self, app):
        super().__init__(daemon=True)
        self.app = app

    def run(self):
        while True:
            self.app.queue.get()
            self.app.send_button["state"] = tk.DISABLED
            
            # dein code hier ----
            for i in range(10):
                print(i)
                time.sleep(1)
            # -------------------------
            self.app.send_button["state"] = tk.NORMAL


if __name__ == "__main__":
    app = ChatApplication()
    app.run()
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

@rogerb

self.app.send_button["state"] = tk.DISABLED

darf nicht sein. Damit manipulierst du GUI Objekte aus einem anderen thread. Das geht ggf mächtig ins Auge.

Für sowas braucht man auch in rückrichtung einen message Mechanismus. Zb mit einer Queue und after. Oder per filedeskriptor, aber leider nur unter Unix.
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

__deets__ hat geschrieben: Dienstag 31. August 2021, 19:45 self.app.send_button["state"] = tk.DISABLED

darf nicht sein. Damit manipulierst du GUI Objekte aus einem anderen thread. Das geht ggf mächtig ins Auge.
Ja, es funktionierte in diesem kleinen Beispiel noch ganz gut. Der Button State wird ja auch nur vom Background Task geändert. Aber sauber ist das wohl nicht.
Ich habe es jetzt mit zwei Queues und after() hinbekommen.
Es funktioniert. Was meinst du?

Code: Alles auswählen

import queue
import tkinter as tk
from threading import Thread
from queue import Queue
import time

# from bibliothek import respond
# from speak import eve_speak, record_audio


BG_GRAY = "#ABB2B9"
BG_COLOR = "#17202A"
TEXT_COLOR = "#EAECEE"

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


class ChatApplication:
    def __init__(self):
        self.window = tk.Tk()
        self.transmit_queue = Queue()
        self.receive_queue = Queue()
        self.background_task = BackgroundTask(self.receive_queue, self.transmit_queue)
        self.background_task.start()
        self.setup_main_window()

    def run(self):
        self.window.mainloop()

    def setup_main_window(self):
        self.window.title("EVE")
        self.window.resizable(width=False, height=False)
        self.window.configure(width=470, height=550, bg=BG_COLOR)

        # head label
        # head_label = Label(self.window, bg=BG_COLOR, fg=TEXT_COLOR,
        #                   text="Welcome", font=FONT_BOLD, pady=10)
        # head_label.place(relwidth=1)

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

        # text widget
        self.text_widget = tk.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=tk.DISABLED)

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

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

        # message entry box
        self.msg_entry = tk.Entry(bottom_label, bg="#2C3E50", fg=TEXT_COLOR, font=FONT)
        self.msg_entry.place(relwidth=0.74, relheight=0.06, rely=0.008, relx=0.011)
        self.msg_entry.focus()
        self.msg_entry.bind("<Return>", self.on_enter_pressed)

        # send button
        self.send_button = tk.Button(
            bottom_label,
            text="Send",
            font=FONT_BOLD,
            width=20,
            bg=BG_GRAY,
            command=lambda: self.on_enter_pressed(None),
        )
        self.send_button.place(relx=0.77, rely=0.008, relheight=0.06, relwidth=0.22)

    def wait_for_background_task(self):
        try:
            self.receive_queue.get_nowait()
            self.send_button["state"] = tk.NORMAL
            return
        except queue.Empty:
            pass

        self.window.after(100, self.wait_for_background_task)

    def on_enter_pressed(self, event):
        self.send_button["state"] = tk.DISABLED
        self.transmit_queue.put(self.send_button)
        self.wait_for_background_task()

    def insert_message(self, msg):  # , ans
        if not msg:
            return

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

        msg2 = f"EVE: Antwort\n\n"
        self.text_widget.configure(state=tk.NORMAL)
        self.text_widget.insert(tk.END, msg2)
        self.text_widget.configure(state=tk.DISABLED)

        self.text_widget.see(tk.END)


class BackgroundTask(Thread):
    def __init__(self, transmit_queue, receive_queue):
        self.transmit_queue = transmit_queue
        self.receive_queue = receive_queue
        super().__init__(daemon=True)

    def run(self):
        while True:
            self.receive_queue.get()
            for i in range(5):
                print(i)
                time.sleep(1)
            self.transmit_queue.put(1)


if __name__ == "__main__":
    app = ChatApplication()
    app.run()
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ja, so passt es. Man kann natürlich viel an den Details schrauben, wie wenn was disabled wird, aber das ist natürlich eine Frage der Anwendungsgestaltung.
Benutzeravatar
Bio Salami
User
Beiträge: 63
Registriert: Mittwoch 28. Juli 2021, 14:10

@__deets__ und @rogerb,
Danke für die Hilfe.

Ich habe nur noch ein Problem. Ich habe meine drei Zeilen reingeschrieben, aber das Programm beendet sich nach auslösen der Background Task immer mit: Befehl: Tcl_WaitForEvent: Notifier not initialized

Mein Code wäre:

Code: Alles auswählen

import queue
import tkinter as tk
from threading import Thread
from queue import Queue
import time

from bibliothek import respond
from speak import eve_speak, record_audio


BG_GRAY = "#ABB2B9"
BG_COLOR = "#17202A"
TEXT_COLOR = "#EAECEE"

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


class ChatApplication:
    def __init__(self):
        self.window = tk.Tk()
        self.transmit_queue = Queue()
        self.receive_queue = Queue()
        self.background_task = BackgroundTask(self.receive_queue, self.transmit_queue)
        self.background_task.start()
        self.setup_main_window()

    def run(self):
        self.window.mainloop()

    def setup_main_window(self):
        self.window.title("EVE")
        self.window.resizable(width=False, height=False)
        self.window.configure(width=470, height=550, bg=BG_COLOR)

        # head label
        # head_label = Label(self.window, bg=BG_COLOR, fg=TEXT_COLOR,
        #                   text="Welcome", font=FONT_BOLD, pady=10)
        # head_label.place(relwidth=1)

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

        # text widget
        self.text_widget = tk.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=tk.DISABLED)

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

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

        # message entry box
        self.msg_entry = tk.Entry(bottom_label, bg="#2C3E50", fg=TEXT_COLOR, font=FONT)
        self.msg_entry.place(relwidth=0.74, relheight=0.06, rely=0.008, relx=0.011)
        self.msg_entry.focus()
        self.msg_entry.bind("<Return>", self.on_enter_pressed)

        # send button
        self.send_button = tk.Button(
            bottom_label,
            text="Send",
            font=FONT_BOLD,
            width=20,
            bg=BG_GRAY,
            command=lambda: self.on_enter_pressed(None),
        )
        self.send_button.place(relx=0.77, rely=0.008, relheight=0.06, relwidth=0.22)

    def wait_for_background_task(self):
        try:
            self.receive_queue.get_nowait()
            self.send_button["state"] = tk.NORMAL
            return
        except queue.Empty:
            pass

        self.window.after(100, self.wait_for_background_task)

    def on_enter_pressed(self, event):
        self.send_button["state"] = tk.DISABLED
        self.transmit_queue.put(self.send_button)
        self.wait_for_background_task()

    def insert_message(self, msg):  # , ans
        if not msg:
            return

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

        msg2 = f"EVE: Antwort\n\n"
        self.text_widget.configure(state=tk.NORMAL)
        self.text_widget.insert(tk.END, msg2)
        self.text_widget.configure(state=tk.DISABLED)

        self.text_widget.see(tk.END)


class BackgroundTask(Thread):
    def __init__(self, transmit_queue, receive_queue):
        self.transmit_queue = transmit_queue
        self.receive_queue = receive_queue
        super().__init__(daemon=True)

    def run(self):
        while True:
            self.receive_queue.get()
            voice_data = record_audio()
            if voice_data != '':
                respond(voice_data)
            self.transmit_queue.put(1)


if __name__ == "__main__":
    app = ChatApplication()
    app.run()
Benutzeravatar
Bio Salami
User
Beiträge: 63
Registriert: Mittwoch 28. Juli 2021, 14:10

Ich habe jetzt diese Error Meldung:

Code: Alles auswählen

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/threading.py", line 973, in _bootstrap_inner
    self.run()
  File "/Users/laurin/Documents/Python/Sprachasistent/Skripte/app.py", line 134, in run
    respond(voice_data)
  File "/Users/laurin/Documents/Python/Sprachasistent/Skripte/bibliothek.py", line 83, in respond
    info = record_audio("Willst du mehr hören?")
  File "/Users/laurin/Documents/Python/Sprachasistent/Skripte/speak.py", line 14, in record_audio
    eve_speak(ask)
  File "/Users/laurin/Documents/Python/Sprachasistent/Skripte/speak.py", line 53, in eve_speak
    voice.runAndWait()
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/pyttsx3/engine.py", line 177, in runAndWait
    raise RuntimeError('run loop already started')
RuntimeError: run loop already started
Das einzige was ich geändert hatte was die Importbefehle für die Sprach und Bibliothek Datei mit in den Loop zu schreiben
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Rumraten hilft selten weiter. Wie sehen denn die bibliothek.py und speak.py aus?
Benutzeravatar
Bio Salami
User
Beiträge: 63
Registriert: Mittwoch 28. Juli 2021, 14:10

@Sirius,
Hier ist die speak.py

Code: Alles auswählen

import pyttsx3
import speech_recognition as sr

#Einstellungen
spoke = True                #Höhren.      Falls False erfolgt Input über eingabe, nicht übers Sprechen.
answere_spoke = True        #Sprechen.    Falls False Spricht EVE nicht.


#Höhren
def record_audio(ask = False):
    if spoke:
        with sr.Microphone() as source:
            if ask:
                eve_speak(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")
                eve_speak("Meine Server sind gerade nicht erreichbar")
            return voice_data
    else:
        if ask:
            voice_data = input(f"{ask} Befehl: ")
        else:
            voice_data = input("Befehl: ")
        return voice_data


#Sprechen
def eve_speak(text):
    if answere_spoke:
        voice = pyttsx3.init()
        voice.setProperty('rate', 200)
        if text == "" or text == " ":
            print("say: Ich verstehe nicht")
            voice.say("Ich verstehe nicht")
            voice.runAndWait()
        else:
            try:
                print("say: " + text)
                voice.say(text)
                voice.runAndWait()
            except TypeError:
                pass

            
    else:
        print(text)
Hier ist die Bibliothek.py. Da ist noch eine Ai eingebaut. Wüste nicht ob die noch gebraucht wird. Das wären dann nochmal zwei oder drei Dateien

Code: Alles auswählen

from os import error

from speak import eve_speak, record_audio
from stbw import standby

import new_info
import json
import time
import webbrowser
import requests
import wikipediaapi
import pywhatkit
from time import sleep


def respond(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 = record_audio("Welchen Ort suchst du?")

        while location == '':
            location = record_audio('Welchen Ort suchst du?')
        url = 'https://google.nl/maps/place/' + location + '/&amp;'
        webbrowser.get().open(url)
        eve_speak('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 = record_audio("Nach was suchst du?")
        while search == '':
            search = record_audio('Nach was suchst du?')
        url = 'https://google.com/search?q=' + search
        webbrowser.get().open(url)
        eve_speak('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:
        
        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:
            """
            Nothing
            """
        
        while question == '':
            question = record_audio("Was willst du wissen?")

        p = wp.page(question)
        ans_all = wp.extracts(p)


        ans = wp.extracts(p, exsentences=4)
        eve_speak(ans)

        while info == '':
            info = record_audio("Willst du mehr hören?")
        if 'ja' in info:
            trash,ans = ans_all.split(ans)
            eve_speak(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 = record_audio("An wen geht die Nachricht?")
        while name == '':
            name = record_audio("An wen geht die Nachricht?")
        while nachricht == '':
            nachricht = record_audio(f"Was möchtest du {name} senden?")
        while bestätigung == '':
            bestätigung = record_audio(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)

                eve_speak("Nachricht wird gleich versendet")
                pywhatkit.sendwhatmsg_instantly(number[0], nachricht,tab_close=True)
                eve_speak("Die Nachricht wurde versendet")
            except KeyError:
                eve_speak(f"Ich habe {name} nicht gefunden")
        else:
            eve_speak("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 = record_audio("Was soll ich übersetzen")
        while sprache == '':
            sprache = record_audio("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)
            eve_speak("Hier ist die übersetzung")
        except FileNotFoundError:
            eve_speak("In diese Sprache kann ich noch nicht übersetzen")
    
    else:

        from chat import ChatBot
        sentence = voice_data
        sentence = ChatBot(sentence)

        try:
            if '## ' in sentence:
                sentence = sentence.split('## ')
            else:
                eve_speak(sentence)
        except TypeError:
            sentence = 'nothing here'

        if '' == sentence:
                    sentence = 'nothing here'
        elif 'time' in sentence:
            eve_speak("Es ist " + time.strftime('%H:%M', time.localtime()) + "Uhr")
        elif 'sss' in sentence:
            spielzug_sss = ''
        
            eve_speak("Schnik Schnak Schnuk")
        
            while spielzug_sss == '':
                spielzug_sss = record_audio('Was hast du?')
            
            if 'Schere' in spielzug_sss:
                eve_speak("Ich habe Stein.")
            elif 'Stein' in spielzug_sss:
                eve_speak("Ich habe Papier")
            elif 'Papier' in spielzug_sss:
                eve_speak("Ich habe Schere")
            
            eve_speak("Ich habe gewonnen")
        elif 'ausschalten' in sentence:
            exit()

        elif 'wetter' in sentence:
            weather_key = "8e158e2c667709dc57afddf9e623d376"
            city = "willich"
            trash = ""
            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,trash = tmp.split('.')
            max_tmp = format(str(weather_data["main"]["temp_max"]))
            max_tmp,trash = max_tmp.split('.')
            min_tmp = format(str(weather_data["main"]["temp_min"]))
            min_tmp,trash = 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."
            eve_speak(f"Die Temperatur beträgt {tmp}°. Die maximale Temperatur liegt bei {max_tmp}° und die minnimale Temperatur liegt bei {min_tmp}°. {clouds}") 
        elif 'radio' in sentence:
            eve_speak('Radio Auswahl wird geöffnet')
            url = 'https://www.news894.de/service/radioplayer.html?radiochannel=live'
            webbrowser.get().open(url)
            standby()
            exit()
        elif 'stb' in sentence:
            eve_speak("Standby modus Aktiviert")
            standby()
            exit()
        elif 'reload' in sentence:
            exit()
        elif 'kalender' in sentence:
            url = 'https://www.icloud.com/calendar/'
            webbrowser.get().open(url)
            eve_speak("Hier ist dein Kalender")

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

@Bio Salami: Anmerkungen zum `speak`-Modul: Hören schreibt man ohne `h` (also ausser das am Anfang 🙂)

Konstanten werden KOMPLETT_GROSS geschrieben. `spoke` heisst auf Dutsch „gesprochen“, da dann den Kommentar ``# Hören`` hinter zu schreiben ist verwirrend. `answere_spoke` ist auch komisch bis falsch (und auf jeden Fall auch Orthografisch falsch geschrieben).

`record_audio()` ist als Name falsch wenn die Funktion gar kein Audio aufnimmt.

Das beim Sprechen eine leere Zeichenkette und eine mit nur einem Leerzeichen so besonders behandelt wird, hat da nichts zu suchen. Das gehört in den Aufrufer. Und auch sollte es nicht einfach ignoriert werden falls `text` einen falschen Typ hat. Da hat der Aufrufer was falsch gemacht und keinen Text übergeben sondern etwas anderes. Darüber möchte man als Programmierer in der Regel über eine Ausnahme informiert werden. Der Aufrufer kann dann ja immer noch entscheiden, dass ignorieren Sinn macht.

Beim Hören ist `ask` ein komischer Name für etwas das offenbar ein Flag oder eine Zeichenkette (?) sein kann, was an sich auch wieder schräg ist. Hier würde ich einfach nur eine Zeichenkette verwenden. Die kann ja auch leer sein.

Bleibt (ungetestet):

Code: Alles auswählen

import pyttsx3
import speech_recognition as sr

USE_AUDIO_INPUT = True
USE_AUDIO_OUTPUT = True


def say(text):
    if USE_AUDIO_OUTPUT:
        voice = pyttsx3.init()
        voice.setProperty("rate", 200)
        print("say:", text)
        voice.say(text)
        voice.runAndWait()
    else:
        print(text)


def listen(prompt=""):
    if USE_AUDIO_INPUT:
        if prompt:
            say(prompt)

        with sr.Microphone() as source:

            recognizer = sr.Recognizer()
            print("wait for order")
            audio = recognizer.listen(source, timeout=20, phrase_time_limit=4)
            voice_data = ""
            try:
                print("send order to Server")
                voice_data = recognizer.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")
                say("Meine Server sind gerade nicht erreichbar")

            return voice_data

    else:
        return input("{}Befehl: ".format(prompt + " " if prompt else ""))
Einige von den `print()`-Aufrufen wären Kandidaten für Logging.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
Bio Salami
User
Beiträge: 63
Registriert: Mittwoch 28. Juli 2021, 14:10

@__blackjack__,
ich weiß mein Code ist eine Zumutung. Danke fürs reinschreiben. Ich kann es gerade nicht testen aber trotzdem danke. 😃
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Bio Salami: Anmerkungen zum `Bibliothek`-Modul: Der Name ist nicht gut.

`error` aus `os`, `sleep` aus `time`, und `new_info` werden importiert, aber nirgends verwendet.

`ChatBot` wird weit unten, tief in einer Funktion versteckt importiert.

Die ``if``-Bedingungen gegen `voice_data` haben alle das gleiche Muster bei dem `voice_data` immer mehrfach in der Bedingung steht – das kann man in eine eigene Funktion herausziehen.

`trash` ist kein so guter Name und den sollte man auch nicht vorbelegen mit einem Wert der nirgends verwendet wird. Üblich ist es `_` als Namen für Werte zu verwenden, die nicht verwendet werden.

Es wiederholt sich sehr oft ein Muster mit `split()`-Aufrufen in einem ``try``/``except``-Block was auch noch mal Zeichenkettenwiederholungen enthält, und dem immer die gleiche ``while``-Schleife folgt.

Zeichenkettenliterale sind kein Ersatz für ``pass``.

Wenn man JSON-Dateien im Binärmodus öffnet, kümmert sich das JSON-Modul um die Zeichenkodierung.

``try``-Blöcke sollten möglichst klein sein, damit nicht die Gefahr besteht, dass die behandelten Ausnahmen von Code kommen von dem man sie nicht erwartet hat.

Der Teil mit dem `ChatBot` sieht komisch aus. Entweder ist das keine Klasse oder der Code ist falsch wenn er das Ergebnis einfach als Zeichenkette behandelt‽

Was soll die Umbennenung von `voice_data` in `sentence`? Das wäre aber vielleicht generell ein besserer Name für `voice_data`.

In dem ``try``-Block sollte kein `TypeError` auftreten können‽

Je nachdem ob `sentence` "## " enthält ist es danach eine Zeichenkette oder eine Liste mit Zeichenkette, was nicht sein sollte, weil sehr verwirrend.

`exit()` gibt es nicht wirklich wenn man das nicht aus `sys` importiert.

Die URL-Parameter kann man bei `requests` als Wörterbuch übergeben. Das ist übersichtlicher und sicherer als die Werte selbst in eine URL hinein zu formatieren.

`data` ist kein guter Name für ein `Response`-Objekt.

Man sollte prüfen ob die HTTP-Antwort Ok-Status hat.

Die `format()`-Funktion mit nur einem Argument aufrufen macht bei Zeichenketten keinen Sinn.

`tmp` ist eine übliche Abkürzung für `temporary`. Wenn man `temparature` meint, sollte man nicht `tmp` schreiben.

Gleitkommazahlen in Zeichenketten umwandeln um dann die Nachkommastellen zu entfernen ist umständlich. Man formatiert einfach die Zahl mit 0 Nachkommastellen. Oder, falls man immer abrunden möchte, wandelt man die Gleitkommazahl einfach in eine ganze Zahl um.

Die Funktion ist insgesamt zu lang.

Ungetestet:

Code: Alles auswählen

import json
import sys
import webbrowser
from datetime import datetime as DateTime

import pywhatkit
import requests
import wikipediaapi
from chat import ChatBot
from speak import listen, say
from stbw import standby


def contains_any(haystack, needles):
    return any(needle in haystack for needle in needles)


def listen_til_answer(prompt):
    while True:
        answer = listen(prompt)
        if answer:
            return answer


def get_subject(text, prefixes, prompt):
    for prefix in prefixes:
        _, prefix, subject = text.partition(prefix)
        if subject:
            return subject

    return listen_til_answer(prompt)


def respond(sentence):
    if contains_any(sentence, ["suche Ort", "suche den Ort", "wo liegt"]):
        location = get_subject(
            sentence, ["suche den Ort", "wo liegt"], "Welchen Ort suchst Du?"
        )
        webbrowser.get().open(f"https://google.nl/maps/place/{location}/&amp;")
        say("Hier ist der Ort " + location)

    elif contains_any(sentence, ["Suche nach", "suche", "Google Suche"]):
        search = get_subject(sentence, ["Suche nach"], "Nach was suchst Du?")
        webbrowser.get().open(f"https://google.com/search?q={search}")
        say("Hier ist mein suchergebnis für " + search)

    elif contains_any(
        sentence,
        [
            "was weißt du über",
            "weißt du was über",
            "was ist",
            "Wer war",
            "Wer ist",
            "Wikipedia Suche",
        ],
    ):
        question = get_subject(
            sentence,
            [
                "was weißt du über",
                "weißt du was über",
                "was ist",
                "Wer war",
                "Wer war",
            ],
            "Was willst Du wissen?",
        )

        wikipedia = wikipediaapi.Wikipedia("de")
        page = wikipedia.page(question)
        article_start = wikipedia.extracts(page, exsentences=4)
        say(article_start)

        if "ja" in listen_til_answer("Willst Du mehr hören?"):
            say(wikipedia.extracts(page)[len(article_start) :])

    elif contains_any(
        sentence, ["sende eine nachricht", "sende eine Nachricht an"]
    ):
        name = get_subject(
            sentence,
            ["sende eine Nachricht an"],
            "An wen geht die Nachricht?",
        )
        nachricht = listen_til_answer(f"Was möchtest Du {name} senden?")
        if contains_any(
            listen_til_answer(
                f"Soll ich die Nachricht: {nachricht} an {name} absenden?"
            ),
            ["ja", "absenden"],
        ):
            vorname, _, name = name.partition(" ")
            if not name:
                name = vorname

            with open("numbers.json", "rb") as json_file:
                numbers = json.load(json_file)

            try:
                number = numbers[name]
            except KeyError:
                say(f"Ich habe {name} nicht gefunden.")
            else:
                print(number)
                say("Nachricht wird gleich versendet.")
                pywhatkit.sendwhatmsg_instantly(
                    number[0], nachricht, tab_close=True
                )
                say("Die Nachricht wurde versendet.")
        else:
            say("Ich habe die Nachricht nicht gesendet.")

    elif contains_any(sentence, ["übersetze", "Übersetze"]):
        text = get_subject(
            sentence, ["übersetze", "Übersetze"], "Was soll ich übersetzen?"
        )
        sprache = listen_til_answer("In welche sprache soll ich übersetzen?")
        try:
            with open(f"language/{sprache}.txt", encoding="utf-8") as file:
                sprach_code = next(file)
        except FileNotFoundError:
            say("In diese Sprache kann ich noch nicht übersetzen")
        else:
            webbrowser.get().open(
                f"https://translate.google.de/"
                f"?sl=auto&tl={sprach_code}&text={text}&op=translate"
            )
            say("Hier ist die übersetzung")

    else:
        sentence = ChatBot(sentence) or "nothing here"

        if "## " in sentence:
            parts = sentence.split("## ")
        else:
            say(sentence)
            parts = [sentence]

        if "time" in parts:
            say(f"Es ist {DateTime.now():%H:%M} Uhr")

        elif "sss" in parts:
            say("Schnik Schnak Schnuk")
            spielzug = listen_til_answer("Was hast du?")
            for zug, gegenzug in [
                ("Schere", "Stein"),
                ("Stein", "Papier"),
                ("Papier", "Schere"),
            ]:
                if zug in spielzug:
                    say(f"Ich habe {gegenzug}.")
                    break

            say("Ich habe gewonnen")

        elif contains_any(parts, ["ausschalten", "reload"]):
            sys.exit()

        elif "wetter" in parts:
            response = requests.get(
                "https://api.openweathermap.org/data/2.5/weather",
                params={
                    "appid": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
                    "lang": "de",
                    "q": "willich",
                    "units": "metric",
                },
            )
            response.raise_for_status()
            
            weather_data = response.json()
            
            temperatures = weather_data["main"]
            temperature = int(temperatures["temp"])
            max_temperature = int(temperatures["temp_max"])
            min_temperature = int(temperatures["temp_min"])

            clouds_percentage = weather_data["clouds"]["all"]
            if clouds_percentage <= 10:
                clouds_text = "Es sollten keine Wolken am Himmel sein."
            elif clouds_percentage <= 40:
                clouds_text = "Es ist teilweise Bewölkt."
            elif clouds_percentage <= 100:
                clouds_text = "Es ist gerade sehr Bewölkt."
            else:
                assert False, f"clouds_percentage > 100: {clouds_percentage}"

            say(
                f"Die Temperatur beträgt {temperature}°."
                f" Die maximale Temperatur liegt bei {max_temperature}°"
                f" und die minimale Temperatur liegt bei {min_temperature}°."
                f" {clouds_text}"
            )

        elif "radio" in parts:
            say("Radio Auswahl wird geöffnet")
            webbrowser.get().open(
                "https://www.news894.de/service/radioplayer.html"
                "?radiochannel=live"
            )
            standby()
            sys.exit()

        elif "stb" in parts:
            say("Standby modus Aktiviert")
            standby()
            sys.exit()

        elif "kalender" in parts:
            webbrowser.get().open("https://www.icloud.com/calendar/")
            say("Hier ist dein Kalender")
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten