我有一个尝试在按下按钮时生成文本的应用程序。在大多数情况下,文本生成速度很快,但是其中一个函数执行大约需要20秒(取决于文本量)。在此过程中,GUI曾经冻结,因此我将该功能转移到了单独的线程上,并且在该字段上一切正常。
现在我的按钮有问题。当需要花费一些时间才能执行的功能正在运行时,用户仍然可以单击按钮,并且在仍在处理第一个调用的同时,该功能将执行多次。 我想通过在该函数运行时禁用所有按钮来防止这种情况,但是我无法使线程正常工作。
这是我拥有的代码:
def generate_text():
choice = dropdown_choice.get()
if context_obj.context_text.get() == '':
if choice == 'OpenAI':
context = 'Some random context text'
else:
context = ' '
else:
context = context_obj.context_text.get()
if choice == 'OpenAI':
progress.start(50)
progress_bar_text = Label(text='Please wait while the text is being generated',
background='#E5F2FF',
font=("Helvetica", 12))
progress_bar_text.place(relx=.2,
rely=.66,
anchor="c")
# multithreading for the OpenAI text generation
q = queue.Queue()
thread1 = Thread(target=openAI_generator.sample, args=[text_amount.get(), temperature.get(), context, q])
thread1.start()
def display_AI_text(q):
openAI_text = q.get()
generated_text.configure(state='normal')
generated_text.delete(1.0,END)
generated_text.insert(tk.END, openAI_text)
generated_text.configure(state='disabled')
progress.stop()
progress_bar_text.place_forget()
thread2 = Thread(target=display_AI_text, args=[q])
thread2.start()
在此代码中,thread1
正在执行该功能,而thread2
正在从该功能获取输入并显示它。
我想做的是,在执行thread2
时,所有按钮都将被禁用,并且当线程完成时,这些按钮将再次被启用。
我尝试添加:
thread2 = Thread(target=display_AI_text, args=[q])
generate_button.config(state="disabled")
thread2.start()
然后:
thread2.join()
generate_button.config(state="normal")
但是此代码将冻结应用程序。我假设主线程正在等待thread2
完成,这就是为什么它没有响应。
有人知道解决这个问题的方法吗?
答案 0 :(得分:1)
在许多GUI中,您不能在线程中更改GUI-您必须在主过程中进行更改。
您可以使用队列将信息发送到将更新GUI的主进程。
在Tkinter中,您可以使用
root.after(time_in_milliseconds, function_name)
定期运行功能,可以检查来自此队列的消息。
或者它可以定期检查
thread2.is_alive()
而不是使用thread2.join()
,因为is_alive()
不会阻止代码。
import tkinter as tk
from threading import Thread
import time
def long_running_function():
print('start sleep')
time.sleep(3)
print('end sleep')
def start_thread():
global t
global counter
b['state'] = 'disable'
counter = 0
t = Thread(target=long_running_function)
t.start()
check_thread()
# or check after 100ms
# root.after(100, check_thread)
def check_thread():
global counter
if not t.is_alive():
b['state'] = 'normal'
l['text'] = ''
else:
l['text'] = str(counter)
counter += 0.1
# check again after 100ms
root.after(100, check_thread)
#-----------------------------------------------------
# counter displayed when thread is running
counter = 0
root = tk.Tk()
l = tk.Label(root)
l.pack()
b = tk.Button(root, text="Start", command=start_thread)
b.pack()
root.mainloop()