【发布时间】:2020-01-01 20:36:08
【问题描述】:
我正在尝试在 tkinter 中使用 GUI 时实现多线程。我来了this solution,但我没有实现它。
所以基本上我需要知道:
我需要如何更改我的代码以使进度条交互顺畅且顺畅,即当 GUI 失去焦点时不会进入无响应模式?
这是我的代码:
from tkinter import *
import queue
import threading
from tkinter.ttk import *
class ThreadedTask(threading.Thread):
def __init__(self, queue):
threading.Thread.__init__(self)
self.queue = queue
# Gui class
class MyGui(Frame):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
self.queue = queue.Queue()
self.init_ui()
# define a button to fulfill long task
def init_ui(self):
self.frame = Frame(self, relief=RAISED, borderwidth=1)
self.frame.grid(row=0, column=0, columnspan=1, sticky='ns')
self.status_frame = Frame(self, relief=RAISED, borderwidth=1, height=20)
self.status_frame.grid(row=1, column=0, columnspan=3, sticky='nesw')
self.status_frame.grid_configure(padx=3, pady=3)
self.button = Button(self.frame, text='do Stuff', command=self.do_stuff)
self.button.grid(padx=3, pady=3)
self.progress = Progressbar(self.status_frame, orient="horizontal", length=80, mode="determinate")
self.progress.grid(row=1, column=0, pady=3, sticky='nesw')
self.grid()
# start ThreadedTask here
def do_stuff(self):
ThreadedTask(self.queue).start()
self.queue_process(DoStuffClass(ui=self))
def queue_process(self, process, retry_time=10):
self.master.after(retry_time, process)
def update_status(self):
self.parent.update_idletasks()
class DoStuffClass:
def __init__(self, ui=None):
self.ui = ui
self.long_task()
def long_task(self):
# do stuff here and update the progressbar from MyGui
self.ui.progress['maximum'] = 10000
# some lengthy task ...
for i in range(10000):
print(i)
self.ui.progress.step()
self.ui.parent.update_idletasks()
# main
root = Tk()
root.geometry("150x80+50+50")
MyGui(root)
root.mainloop()
root.quit()
我认为现在我的问题是队列和线程的错误实现,即self.queue_process(DoStuffClass(ui=self))... 行为与我根本不使用队列和多线程一样。进度条工作,只要它保持“焦点”,这意味着我没有点击桌面上的其他任何东西。一旦我单击桌面上的其他位置并且 GUI 失去焦点,GUI 就会进入“无响应”模式,并且进度条不再更新。还有,有时Tcl会关闭错误的线程,导致整个程序崩溃。
【问题讨论】:
-
你把你的代码简化得太多了。当我尝试运行它时:
AttributeError: 'MyGui' object has no attribute 'preview_button'来自self.preview_button.grid(padx=3, pady=3)行。顺便说一句,主线程是 Python 解释器首先执行的线程。 -
@martineau 你好,Martineau,我已经更新了我的代码。我对其进行了测试,它一直有效,直到
self.queue_process(DoStuffClass(ui=self))行,这是我不确定的部分。但是,您确实会在执行冗长的任务时看到进度条在移动,并且当您更改 minigui 的焦点时,它将进入无响应模式(对于我的原始程序 - 有时会崩溃)。 -
您的问题非常广泛,并且子问题太多(其中一些毫无意义 - 即没有子线程这样的东西)。主线程是 Python 解释器开始运行的第一个代码。你可以在任何你想要的地方实现
ThreadedTask类。线程不是从它产生的,它的实例是个单独的线程。要记住的基本事项是只有一个线程(通常是主线程)应该更新 GUI(通过after())。其他线程需要将东西放入Queues,主线程应该检查它们并根据它找到的内容更新 GUI。 -
您还可以查看 .after() 方法(在 Tkinter 中),该方法允许您在 Tkinter 中运行函数而无需暂停 GUI。这也将消除使用线程模块的需要。
标签: python python-3.x multithreading user-interface tkinter