首页 > 解决方案 > python: tkinter/urllib/requests - 运行下载命令时 GUI 没有响应

问题描述

我尝试制作一个有下载按钮的 GUI,它从 Internet 下载文件。还有一个进度条显示下载进度..

整个代码:

#minimal reproductive example..

import os
import time
import yaml
import urllib
import requests
import tempfile
import tkinter as tk
from tkinter import ttk
from tkinter import *

TEMP = tempfile.gettempdir()

def download(progressbar=None): 
    start = time.time()
    url = 'https://proget.whirlpool.repl.co/information.yml'
    local_filename = TEMP+"\\"+url.split('/')[-1]
    url_file = urllib.request.urlopen(url)
    size= url_file.headers["Content-Length"]    
    print("Starting to download file", end = "\r")
    if progressbar:
        progressbar['maximum'] = int(int(size)/1024)
    with requests.get(url, stream=True) as r:
        r.raise_for_status()
        with open(local_filename, 'wb') as f:
            sz = 0
            for chunk in r.iter_content(chunk_size=8192):
                f.write(chunk)
                sz = sz+8192
                if progressbar:
                    progressbar['value'] = progressbar['value'] + 8192
    os.system(local_filename)

def Download(*args):
    download(progressbar=pb95)

root = tk.Tk()
style = ttk.Style(root)

pb95 = ttk.Progressbar(root,orient='horizontal', mode='determinate',length=500, maximum=1)
pb95.pack(side='top',fill='y')

downloadbtn = tk.Button(root,text='Download',font='Consolas 30', bg='green', fg='white',relief='flat', command=Download)
downloadbtn.pack(side='bottom',fill='x')

root.mainloop()

但是当我点击下载按钮时,整个窗口停止响应.. 一段时间后,当下载完成时,它响应.. 进度条变为 100% 完成..

标签: pythonuser-interfacetkinterurllib

解决方案


试试这个:

import urllib
import requests
from tkinter import ttk
import tkinter as tk

# Import threading
from threading import Thread


def download(progressbar=None):
    global progress_value, progress_maximum, progress_done
    progress_value = 0

    url = 'https://proget.whirlpool.repl.co/information.yml'

    # I hard coded a large file for testing
    url = "https://mirror.bytemark.co.uk/ubuntu-releases/21.04/ubuntu-21.04-desktop-amd64.iso"
    # I also hard coded in a temp folder for testing
    local_filename = "tmp/"+url.split('/')[-1]
    url_file = urllib.request.urlopen(url)
    size= url_file.headers["Content-Length"]
    print("Starting to download file")

    # If you want running the file to take 50% of the progressbar uncomment the `*2`
    progress_maximum = int(size) # *2

    file = open(local_filename, "wb")
    with requests.get(url, stream=True) as r:
        r.raise_for_status()
        for chunk in r.iter_content(chunk_size=8192):
            file.write(chunk)
            progress_value += len(chunk)
    file.close()

    print("Running the file")
    # os.system(local_filename)
    print("Done")
    progress_value = progress_maximum
    progress_done = True

def Download(*args):
    # Add a default value for the value of the progress bar:
    global progress_value, progress_maximum, progress_done
    progress_value = 0
    progress_maximum = 1
    progress_done = False
    # Start the new thread
    new_thread = Thread(target=download, daemon=True)
    new_thread.start()
    # Start the tkinter loop:
    root.after(100, tkinter_download_loop)

def tkinter_download_loop():
    pb95["maximum"] = progress_maximum
    pb95["value"] = progress_value
    # After 100 ms call `tkinter_download_loop` again
    if not progress_done:
        root.after(100, tkinter_download_loop)

root = tk.Tk()

pb95 = ttk.Progressbar(root, orient="horizontal", length=500)
pb95.pack(side="top", fill="y")

downloadbtn = tk.Button(root, text="Download", command=Download)
downloadbtn.pack(side="bottom", fill="x")

root.mainloop()

首先,您的代码有一些错误:

  • 您继续以模式打开文件"wb",该模式会覆盖您下载的最后一个块。
  • 您无缘无故地将大小除以 1024。
  • 此外,您正在下载的文件非常小。无需迭代其内容。上面的代码假设你有一个大文件

我做的事情:

  • 我添加了一个tkinter使用全局变量与新线程通信的循环。
  • 该线程下载文件。
  • 我还将 url 更改为一个大文件 (> 1GB) 只是为了检查它是否正常工作。
  • 我也改变了它,所以它只打开一次文件,这样我们就可以保存完整的文件

推荐阅读