多线程应用程序
在本文中,将介绍如何在 Tkinter 应用程序中使用多线程来提高应用程序的响应速度。
1、何时在 Tkinter 应用程序中使用 Thread
在 Tkinter 应用程序中,主循环应始终在主线程中开始。 它负责处理事件和更新 GUI。如果有一个需要时间的后台操作,应该在一个单独的线程中执行它。否则,应用程序将无法响应。 在最坏的情况下,它会在操作运行时冻结。要在 Tkinter 应用程序中创建和控制多个线程,可以使用 Python 的thread 模块。线程模块包含在 Python 的标准库中,因此无需安装它。
2、应用实例
下面代码将构建一个简单的程序,下载由 URL 指定的网页并在 Text 小部件中显示其内容:
要下载网页,我们将使用 requests 模块。
首先,通过执行以下命令安装 requests 模块:
pip install requests
接下来,导入 tkinter、threading 和 requests 模块:
import tkinter as tk
from tkinter import ttk
from tkinter.messagebox import showerror
from threading import Thread
import requests
然后,定义一个继承自 Thread 类的名为 AsyncDownload 的新类:
class AsyncDownload(Thread):
def __init__(self, url):
super().__init__()
self.html = None
self.url = url
def run(self):
response = requests.get(self.url)
self.html = response.text
AsyncDownload 类的工作原理:
接下来,创建继承自 Tk 类的 App 类。 App 类代表根窗口。
根窗口由三个包含所有小部件的框架组成。 我们不会关注如何使用网格几何管理器创建小部件并将它们放置在窗口上。
当点击下载按钮时,程序会执行 App 类的 handle_download() 方法。
在 handle_download() 方法中,检查是否提供了 url。 如果是,则创建 AsyncDownload 类的新实例并启动线程。 此外,禁用下载按钮并清除 Text 小部件的内容。
另外,调用monitor()方法来监控线程的状态。
def handle_download(self):
url = self.url_var.get()
if url:
self.download_button['state'] = tk.DISABLED
self.html.delete(1.0, "end")
download_thread = AsyncDownload(url)
download_thread.start()
self.monitor(download_thread)
else:
showerror(title='Error',
message='Please enter the URL of the webpage.')
在 monitor() 方法中,我们安排了一个动作,如果线程仍然处于活动状态,它将在 100 毫秒后运行 monitor() 方法。
如果下载完成,我们会更新 Entry 小部件的内容并重新启用下载按钮:
def monitor(self, thread):
if thread.is_alive():
self.after(100, lambda: self.monitor(thread))
else:
self.html.insert(1.0, thread.html)
self.download_button['state'] = tk.NORMAL
完整代码如下:
import tkinter as tk
from tkinter import ttk
from tkinter.messagebox import showerror
from threading import Thread
import requests
class AsyncDownload(Thread):
def __init__(self, url):
super().__init__()
self.html = None
self.url = url
def run(self):
response = requests.get(self.url)
self.html = response.text
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title('Webpage Download')
self.geometry('680x430')
self.resizable(0, 0)
self.create_header_frame()
self.create_body_frame()
self.create_footer_frame()
def create_header_frame(self):
self.header = ttk.Frame(self)
self.header.columnconfigure(0, weight=1)
self.header.columnconfigure(1, weight=10)
self.header.columnconfigure(2, weight=1)
self.label = ttk.Label(self.header, text='URL')
self.label.grid(column=0, row=0, sticky=tk.W)
self.url_var = tk.StringVar()
self.url_entry = ttk.Entry(self.header,
textvariable=self.url_var,
width=80)
self.url_entry.grid(column=1, row=0, sticky=tk.EW)
self.download_button = ttk.Button(self.header, text='Download')
self.download_button['command'] = self.handle_download
self.download_button.grid(column=2, row=0, sticky=tk.E)
self.header.grid(column=0, row=0, sticky=tk.NSEW, padx=10, pady=10)
def handle_download(self):
url = self.url_var.get()
if url:
self.download_button['state'] = tk.DISABLED
self.html.delete(1.0, "end")
download_thread = AsyncDownload(url)
download_thread.start()
self.monitor(download_thread)
else:
showerror(title='Error',
message='Please enter the URL of the webpage.')
def monitor(self, thread):
if thread.is_alive():
self.after(100, lambda: self.monitor(thread))
else:
self.html.insert(1.0, thread.html)
self.download_button['state'] = tk.NORMAL
def create_body_frame(self):
self.body = ttk.Frame(self)
self.html = tk.Text(self.body, height=20)
self.html.grid(column=0, row=1)
scrollbar = ttk.Scrollbar(self.body,
orient='vertical',
command=self.html.yview)
scrollbar.grid(column=1, row=1, sticky=tk.NS)
self.html['yscrollcommand'] = scrollbar.set
self.body.grid(column=0, row=1, sticky=tk.NSEW, padx=10, pady=10)
def create_footer_frame(self):
self.footer = ttk.Frame(self)
self.footer.columnconfigure(0, weight=1)
self.exit_button = ttk.Button(self.footer,
text='Exit',
command=self.destroy)
self.exit_button.grid(column=0, row=0, sticky=tk.E)
self.footer.grid(column=0, row=2, sticky=tk.NSEW, padx=10, pady=10)
if __name__ == "__main__":
app = App()
app.mainloop()
运行结果如下:
|