线程更新进度条
在本文中,将介绍在 Tkinter 应用程序中运行线程时显示进度条。
本教程假设您知道如何使用 after() 方法并了解线程在 Python 中是如何工作的。 此外,您应该知道如何使用 tkraise() 方法在帧之间切换。
在本文中,将构建一个网络图片查看器,使用其 API 显示来自 unsplash.com
的随机图片。
HTTP 请求URL如下:
https://source.unsplash.com/random/640x480
将会得到一个随机的640x480
图片
当您单击 Next Picture 按钮时,程序会从 unsplash.com 调用 API 以下载随机图片并将其显示在窗口上。
在图片下载过程中还会显示一个进度条,表示正在下载:
第一步,应用程序使用到request
模块,因此需要安装:
pip install request
第二步,定义一个继承自 Thread 类的新类:
class PictureDownload(Thread):
def __init__(self, url):
super().__init__()
self.picture_file = None
self.url = url
def run(self):
""" download a picture and save it to a file """
# download the picture
response = requests.get(self.url, proxies=proxyDict)
picture_name = self.url.split('/')[-1]
picture_file = f'./assets/{picture_name}.jpg'
# save the picture to a file
with open(picture_file, 'wb') as f:
f.write(response.content)
self.picture_file = picture_file
在此 PictureDownload 类中,run()
方法使用 requests 模块调用 API。
run()
方法下载图片并将其保存到 /assets/ 文件夹。 此外,它将下载图片的路径分配给图片文件实例属性。
第三步,定义一个继承Tk类的App类。 App 类代表根窗口。
根窗口有两个Frame,一个用于显示进度条,另一个用于显示保存下载图片的 Canvas:
def __init__(self, canvas_width, canvas_height):
super().__init__()
self.resizable(0, 0)
self.title('Image Viewer')
# Progress frame
self.progress_frame = ttk.Frame(self)
# configrue the grid to place the progress bar is at the center
self.progress_frame.columnconfigure(0, weight=1)
self.progress_frame.rowconfigure(0, weight=1)
# progressbar
self.pb = ttk.Progressbar(
self.progress_frame, orient=tk.HORIZONTAL, mode='indeterminate')
self.pb.grid(row=0, column=0, sticky=tk.EW, padx=10, pady=10)
# place the progress frame
self.progress_frame.grid(row=0, column=0, sticky=tk.NSEW)
# Picture frame
self.picture_frame = ttk.Frame(self)
# canvas width & height
self.canvas_width = canvas_width
self.canvas_height = canvas_height
# canvas
self.canvas = tk.Canvas(
self.picture_frame,
width=self.canvas_width,
height=self.canvas_height)
self.canvas.grid(row=0, column=0)
self.picture_frame.grid(row=0, column=0)
当单击 Next Picture 按钮时,handle_download() 方法将执行:
def handle_download(self):
""" Download a random photo from unsplash """
self.start_downloading()
url = 'https://source.unsplash.com/random/640x480'
download_thread = PictureDownload(url)
download_thread.start()
self.monitor(download_thread)
handle_download() 方法通过调用 start_downloading() 方法显示进度Frame并启动进度条:
def start_downloading(self):
self.progress_frame.tkraise()
self.pb.start(20)
它还创建了一个新的线程,下载随机图片并调用monitor()方法来监控线程的状态。下面为 monitor() 方法:
def monitor(self, download_thread):
""" Monitor the download thread """
if download_thread.is_alive():
self.after(100, lambda: self.monitor(download_thread))
else:
self.stop_downloading()
self.set_picture(download_thread.picture_file)
monitor() 方法检查线程的状态。 如果线程正在运行,它会在 100 毫秒后安排另一次检查。
否则,monitor() 方法调用 stop_downloading() 方法停止进度条,显示相框,显示图像。
下面为stop_downloading() 方法:
def stop_downloading(self):
self.picture_frame.tkraise()
self.pb.stop()
整完代码如下:
import requests
import tkinter as tk
from threading import Thread
from PIL import Image, ImageTk
from tkinter import ttk
class PictureDownload(Thread):
def __init__(self, url):
super().__init__()
self.picture_file = None
self.url = url
def run(self):
""" download a picture and save it to a file """
# download the picture
response = requests.get(self.url)
picture_name = self.url.split('/')[-1]
picture_file = f'./assets/{picture_name}.jpg'
# save the picture to a file
with open(picture_file, 'wb') as f:
f.write(response.content)
self.picture_file = picture_file
class App(tk.Tk):
def __init__(self, canvas_width, canvas_height):
super().__init__()
self.resizable(0, 0)
self.title('Image Viewer')
# Progress frame
self.progress_frame = ttk.Frame(self)
# configrue the grid to place the progress bar is at the center
self.progress_frame.columnconfigure(0, weight=1)
self.progress_frame.rowconfigure(0, weight=1)
# progressbar
self.pb = ttk.Progressbar(
self.progress_frame, orient=tk.HORIZONTAL, mode='indeterminate')
self.pb.grid(row=0, column=0, sticky=tk.EW, padx=10, pady=10)
# place the progress frame
self.progress_frame.grid(row=0, column=0, sticky=tk.NSEW)
# Picture frame
self.picture_frame = ttk.Frame(self)
# canvas width & height
self.canvas_width = canvas_width
self.canvas_height = canvas_height
# canvas
self.canvas = tk.Canvas(
self.picture_frame,
width=self.canvas_width,
height=self.canvas_height)
self.canvas.grid(row=0, column=0)
self.picture_frame.grid(row=0, column=0)
# Button
btn = ttk.Button(self, text='Next Picture')
btn['command'] = self.handle_download
btn.grid(row=1, column=0)
def start_downloading(self):
self.progress_frame.tkraise()
self.pb.start(20)
def stop_downloading(self):
self.picture_frame.tkraise()
self.pb.stop()
def set_picture(self, file_path):
""" Set the picture to the canvas """
pil_img = Image.open(file_path)
# resize the picture
resized_img = pil_img.resize(
(self.canvas_width, self.canvas_height),
Image.ANTIALIAS)
self.img = ImageTk.PhotoImage(resized_img)
# set background image
self.bg = self.canvas.create_image(
0,
0,
anchor=tk.NW,
image=self.img)
def handle_download(self):
""" Download a random photo from unsplash """
self.start_downloading()
url = 'http://cn.naturewallpaperfree.com/mobile/hai\'an/ziran-bizhi-640x480-434-e5a14488.jpg'
download_thread = PictureDownload(url)
download_thread.start()
self.monitor(download_thread)
def monitor(self, download_thread):
""" Monitor the download thread """
if download_thread.is_alive():
self.after(100, lambda: self.monitor(download_thread))
else:
self.stop_downloading()
self.set_picture(download_thread.picture_file)
if __name__ == '__main__':
app = App(640, 480)
app.mainloop()
运行结果如下: