Как использовать кнопку закрытия (X) или любую другую кнопку, когда в Tkinter работает бесконечный l oop - PullRequest
1 голос
/ 29 мая 2020

Я пишу программу для GUI для поддержки моего сценария Voice Assistant Python с использованием tkinter. Но проблема в том, что как только программа Voice Assistant активируется, то есть бесконечное l oop функции непрерывного прослушивания того, что говорит пользователь, выполняется, все окно зависает, и я не могу нажать любую другую кнопку, и я не возможность доступа к кнопке закрытия (X). Я знаю, что в этом случае мне нужно использовать потоки, но я не понимаю, где именно использовать их для достижения моего желания. Я был бы очень признателен, если бы кто-нибудь помог мне с этим. Я даю весь код, который написал, чтобы помочь вам с этой проблемой. Функция бесконечного бега в программе: Geega()

from tkinter import *
import tkinter.font as font
import tkinter.messagebox as tmsg
import pyttsx3 
import speech_recognition as sr 
import datetime
import wikipedia 
import webbrowser
import os
import time
import smtplib
import random
import wolframalpha
import apiai
import json
import _thread

#API configuration
engine = pyttsx3.init('sapi5')
client = wolframalpha.Client('AQKXVX-UVQ8TUWUVU')
voices = engine.getProperty('voices')
engine.setProperty('voice', voices[1].id)

#Conversation mode
def convo_mode():
    Text_frame1['text'] = "Geega has entered conversation mode..."
    Button_frame1 = Button(Frame1, image=Button_image, bg='#444444', borderwidth=0, height=200, width=200, state=DISABLED, activebackground='#444444').place(x=5,y=15)
    # Button_frame3 = Button(Frame3, text='Button', bg='white', borderwidth=0, cursor='hand2', state=DISABLED, activebackground='#444444').place(x=5,y=15)
    Button_frame3 = Button(Frame3, image=Button_image2, bg='#333333', borderwidth=0, height=80, width=250, cursor='hand2', command=convo_mode, state=DISABLED, activebackground='#333333').place(x=320,y=360)
    main_window.update()
    speak("Geega has entered Conversation mode...")
    query = takeCommand().lower()
    while "back" not in query:
        response = apiai_connc(query)
        speak(response)
        query = pause().lower()
    else:
        speak("Re-entering Normal Mode...")
        Button_frame1 = Button(Frame1, image=Button_image, bg='#444444', borderwidth=0, height=200, width=200, state=NORMAL, cursor= 'hand2', activebackground='#444444', command=Geega).place(x=5,y=15)
        # Button_frame3 = Button(Frame3, text="Button", bg='white', borderwidth=0, cursor='hand2', state=NORMAL, activebackground='#444444', command = convo_mode).place(x=5,y=15)
        Button_frame3 = Button(Frame3, image=Button_image2, bg='#333333', borderwidth=0, height=80, width=250, cursor='hand2', command=convo_mode, state=NORMAL, activebackground='#333333').place(x=320,y=360)
        Text_frame1['text'] = "Geega has entered Normal mode..."
        main_window.update()


#Function for instructions
def Instructions():
    Instruction = tmsg.showinfo("Instructions", "- Click the mic to activate Geega\n\n- Say \'Hello Geega\' before each command to make Geega do it\n\n- Click on \'Turn on Conversation Mode\' to enter Conversation Mode to make Geega answer your questions!\n\nNote: Geega would not execute command when running in Conversation mode. To bring back Geega to normal mode, just speak \'Back\'.")

#Function for About Geega
def About_Geega():
    copyright_symbol = u"\u00A9"
    About_Geega = tmsg.showinfo("About Geega", u"Geega v1.0.0\n\nReleased on May 16, 2020\n\n%s All rights reserved" % (copyright_symbol))


#Function for authors
def Authors():
    Author = tmsg.showinfo("Authors","Designed by Soham Chatterjee")




#Function to make Geega speak
def speak(audio):
    engine.say(audio)
    engine.runAndWait()


#Function to make Geega wish the user
def wishMe():
    hour = int(datetime.datetime.now().hour)
    if hour>=0 and hour<12:
        speak("Good Morning sir!")

    elif hour>=12 and hour<18:
        speak("Good Afternoon sir!")   

    else:
        speak("Good Evening sir!")  

    speak("I am Geega - the personal Voice Assistant. Just speak \"Hello Geega\" to activate me. I am always there for your service!")

#Function to take command from user
def takeCommand():
    r = sr.Recognizer()
    with sr.Microphone(device_index=1) as source:
        r.pause_threshold = 1
        audio = r.listen(source)
        query = ''
    try:
        query = r.recognize_google(audio, language='en-in')
    except sr.UnknownValueError:
        speak("Sorry! Couldn\'t get you!! Please say again")
        return takeCommand().lower()
    except sr.RequestError:
        print("Aw, Snap! Geega is down!! Please restart the program!")
        return takeCommand().lower()
    return query.lower()


#Function to pause when user is doing any work
def pause():
    r = sr.Recognizer()
    with sr.Microphone() as source:
        r.pause_threshold = 1
        audio = r.listen(source)
        query = ''
    try:   
        query = r.recognize_google(audio, language='en-in')

    except sr.UnknownValueError:  
        return pause().lower()
    except sr.RequestError:
        return pause().lower()
    return query


#Function for API connection
def apiai_connc(voice_data):
    CLIENT_ACCESS_TOKEN = "6b496c3a7afe409da201e7e82b3b5215"
    ai = apiai.ApiAI(CLIENT_ACCESS_TOKEN)
    request = ai.text_request()
    request.lang = "de"
    request.session_id = "<SESSION ID, UNIQUE FOR EACH USER>"
    request.query = voice_data
    source = request.getresponse()
    source_data = source.read()
    obj = json.loads(source_data)
    return obj["result"]["fulfillment"]["speech"]


#Main Geega function
def Geega():
    Button_frame3 = Button(Frame3, image=Button_image2, bg='#333333', borderwidth=0, height=80, width=250, cursor='hand2', command=convo_mode, state=DISABLED, activebackground='#333333').place(x=320,y=360)
    Text_frame1['text'] = "Geega is running..."
    main_window.update()
    wishMe()
    while True:
        query = pause().lower()
        if query.count("hello")>0:        
            speak("Hello sir! Geega is ready to be instructed...")
            query = takeCommand().lower()
            if query == '':
                speak("Sorry! I couldn't hear you! Can you please say that again?")
            elif 'youtube' in query:
                speak("Opening YouTube...")
                webbrowser.open("www.youtube.com")
            elif 'google' in query:
                speak("Opening Google...")
                webbrowser.open("www.google.com")
            elif 'meet' in query:
                speak("Opening Google Meet...")
                webbrowser.open("https://meet.google.com")
            elif 'physics class' in query:
                speak("Opening your Physics class on Google Meet...")
                webbrowser.open("https://meet.google.com/inw-ncmr-emq")
            elif 'mathematics' in query:
                speak("Opening your Mathematics class on Google Meet...")
                webbrowser.open("https://meet.google.com/inw-ncmr-emq")
            elif 'chemistry class' in query:
                speak("Opening your Chemistry class on Google Meet...")
                webbrowser.open("https://meet.google.com/inw-ncmr-emq")
            elif 'english class' in query:
                speak("Opening your English class on Google Meet...")
                webbrowser.open("https://meet.google.com/eym-ypwa-kkx")
            elif 'play music' in query:
                music_dir = 'E:\\Bony Laptop\\Music'
                songs = os.listdir(music_dir)
                speak("Playing music...")
                os.startfile(os.path.join(music_dir,songs[0]))
            elif 'day' in query:
                now = datetime.datetime.now()
                day = now.strftime("%A")
                speak(f"Today is {day}")
            elif 'date' in query:
                date = datetime.datetime.now().strftime("%B")
                speak(f"Today's date is {date}")
            elif 'time' in query:
                strTime = datetime.datetime.now().strftime("%I:%M:%S")
                speak(f"The time is {strTime}")
            elif 'browser' in query:
                speak('Sure! Opening Internet Explorer...')
                browser_path = webbrowser.open(url="google.com")
            elif 'open python' in query:
                python_path = "C:\\Users\\admin\\AppData\\Local\\Programs\\Python\\Python38-32\\Lib\\idlelib\\idle.pyw"
                speak("Opening Python...")
                os.startfile(python_path)  
            elif 'calculate' in query:
                query = query
                speak("Calculating...")
                try:
                    res = client.query(query)
                    results = next(res.results).text
                    speak(f"The results are {results}")
                except:
                    speak("Oops! Something went wrong. I couldn't calculate the problem.")
            elif 'exit' in query or "quit" in query:
                speak("Good Bye Sir! Have a nice day!")
                quit()
            elif 'search' in query:
                speak("Searching on the Web...")
                url = 'https://www.google.co.in/search?q='+query
                webbrowser.get().open(url)
            elif 'weather' in query:
                speak("Getting weather forecast results for your location on Google...")
                url = 'https://www.google.co.in/search?q=weather'
                webbrowser.get().open(url)
            elif 'i want to talk to you' in query:
                convo_mode()
            else:
                speak("Could not get you!")



main_window = Tk()

def close(event):
    global main_window
    key = event.char
    if key == "<ESCAPE>":
        main_window.destroy()

_thread.start_new_thread(main_window.bind("<Key>", lambda a: close(a)))

#Window configuration
windowWidth = 500
windowHeight = 300
screen_width = main_window.winfo_screenwidth()
screen_height = main_window.winfo_screenheight()
x_coordinate = (screen_width/2)-(windowWidth/2)
y_coordinate = (screen_height/2)-(windowHeight/2)

#Main Window Configuration
main_window.configure(bg='white')
main_window.iconbitmap('Geega_logo.ico')
main_window.title("Geega")
main_window.geometry('%dx%d+%d+%d' % (windowWidth, windowHeight, x_coordinate, y_coordinate))
main_window.minsize(500,300)
main_window.update()


#Frame 1 for Speak button
Frame1 = Frame(main_window, bg='#444444', borderwidth=5)
Frame1.place(x=0, y=0, width=500, height=300)

#Frame 2 for features
Frame2 =  Frame(main_window, bg='#555555', borderwidth=5)
Frame2.place(x=0,y=300, width=500, height=440)

#Frame 3 for conversation mode
Frame3 =  Frame(main_window, bg='#333333', borderwidth=5)
Frame3.place(x=500,y=0, width=865, height=740)

#Defining the font
Font = font.Font(family='Segoe UI', size=15, weight='bold')
Font2 = font.Font(family='Segoe UI', size=15)
Font3 = font.Font(family='Segoe UI', size=30)

#Defining the menu bar
MenuBar= Menu(main_window)
Drop_down1 = Menu(MenuBar, tearoff=0)
Drop_down1.add_command(label="Instructions", command=Instructions)
Drop_down1.add_separator()
Drop_down1.add_command(label='About Geega', command=About_Geega)
Drop_down1.add_command(label='Authors', command=Authors)

#Adding the menu bar
main_window.config(menu=MenuBar)
MenuBar.add_cascade(label="Help", menu=Drop_down1)

#Frame 1 button configuration
Button_image = PhotoImage(file="rsz_1speak_button_png.png")
Button_frame1 = Button(Frame1, image=Button_image, bg='#444444', borderwidth=0, height=200, width=200, cursor='hand2', command=Geega, activebackground='#444444').place(x=5,y=15)

#Frame 1 text configuration:
Text_frame1 = Label(Frame1, text="Click the mic to activate Geega...", bg='#444444', fg='white')
Text_frame1.place(x=43,y=225)
Text_frame1['font'] = Font

#Frame 2 text configuration
Text1_frame2 = Label(Frame2, text='Exciting things to do with Geega!', bg='#555555',fg='white')
Text1_frame2.place(x=43, y=15)
Text1_frame2['font'] = Font2

Text2_frame2 = Label(Frame2, text='- Tell her to open Google for you', bg='#555555',fg='white')
Text2_frame2.place(x=50, y=50)
Text2_frame2['font'] = Font2

Text3_frame2 = Label(Frame2, text='- Tell her to open Chrome for you', bg='#555555',fg='white')
Text3_frame2.place(x=50, y=85)
Text3_frame2['font'] = Font2

Text4_frame2 = Label(Frame2, text='- Bored? Ask Geega to play some music', bg='#555555',fg='white')
Text4_frame2.place(x=50, y=120)
Text4_frame2['font'] = Font2

Text5_frame2 = Label(Frame2, text='for you', bg='#555555',fg='white')
Text5_frame2.place(x=63, y=155)
Text5_frame2['font'] = Font2

Text4_frame2 = Label(Frame2, text='- Try out conversing with Geega in the', bg='#555555',fg='white')
Text4_frame2.place(x=50, y=185)
Text4_frame2['font'] = Font2

Text5_frame2 = Label(Frame2, text='Conversation mode', bg='#555555',fg='white')
Text5_frame2.place(x=63, y=220)
Text5_frame2['font'] = Font2

Text6_frame2 = Label(Frame2, text='Check out the other features too. Interact', bg='#555555',fg='white')
Text6_frame2.place(x=43, y=255)
Text6_frame2['font'] = Font2

Text6_frame2 = Label(Frame2, text='with Geega to find more...', bg='#555555',fg='white')
Text6_frame2.place(x=43, y=290)
Text6_frame2['font'] = Font2

Text7_frame2 = Label(Frame2, text='More new features coming soon...', bg='#555555',fg='white')
Text7_frame2.place(x=43, y=325)
Text7_frame2['font'] = Font


#Frame 3 button configuration
Button_image2 = PhotoImage(file="Convo button.png")
Button_frame3 = Button(Frame3, image=Button_image2, bg='#333333', borderwidth=0, height=80, width=250, cursor='hand2', command=convo_mode, state=NORMAL, activebackground='#333333').place(x=320,y=360)

#Frame 3 text configuration
Text_frame3 = Label(Frame3, text="Check out the all new conversation mode!", bg='#333333', fg='white')
Text_frame3.place(x=255,y=300)
Text_frame3['font'] = Font2

main_window.mainloop()

1 Ответ

1 голос
/ 29 мая 2020

Вы правы, думая о многопоточности.

Для этого достаточно стандартной библиотеки потоков. Вы захотите создать два класса: один для вашего GUI и один для других процессов. Вы можете сделать что-то вроде следующего для потоковой передачи вашей функции:

import threading

...

class BackgroundProcess(threading.Thread):

def __init__(self, parent=None):
    super().__init__()
    self.parent = parent

def run(self):
    Geega()

и вместо простого вызова Geega () из пользовательского интерфейса создайте новый объект BackgroundProcess и вызовите его функцию start () следующим образом:

process = BackgroundProcess()
process.start()

Надеюсь, это поможет!

Изменить: забыл упомянуть, что, поскольку этот процесс бесконечен, вы должны очистить поток после закрытия ваш GUI для предотвращения утечек памяти. Создайте прослушиватель событий для закрытия вашего окна, который убивает все процессы, например

os._exit(1)
...