【问题标题】:Is there a way to stop or cancel a urlretrieve in python?有没有办法在 python 中停止或取消 urlretrieve?
【发布时间】:2020-04-18 18:14:02
【问题描述】:

所以我基本上用 tkinter 和 urllib.request 在 python 中编写了一个程序,它应该可以作为下载器工作,但是每个下载器都必须有一个暂停或取消按钮,但我不能似乎无论如何都可以做到这一点!最近我在stackoverflow(链接:Is it possible to stop (cancel) urlretrieve process?)中遇到了同样的问题,似乎我必须使用 threads 或 multi-processing 但我不知道该怎么做这!顺便说一句,线程或多处理将如何帮助取消暂停下载?有人可以向我解释我该怎么做吗?无论如何都可以在没有线程或多处理的情况下做到这一点?如果没有,你能解释一下如何在这个程序中使用线程或多处理,因为我不知道该怎么做!请帮我解决这个问题。 我的代码:

from tkinter import *
from tkinter import font as tkFont
import random
import urllib.request
import requests


def printsth():
    print("Yay it works! ")


def main_menu():
    root = Tk()
    root.title('8-bit downloader ')
    root.iconbitmap(r"C:\Users\rayanravesh\PycharmProjects\GUI_Calculator\icon.ico")
    root.geometry("600x280")
    # the top menu
    num = IntVar()
    chum = IntVar()
    # var = IntVar()
    menu = Menu(root)
    root.config(menu=menu)
    submenu = Menu(menu)
    menu.add_cascade(label="Settings", menu=submenu)

    def custom_op():
        custom = Toplevel()
        custom.iconbitmap(r"C:\Users\rayanravesh\PycharmProjects\GUI_Calculator\icon.ico")
    submenu.add_command(label="Customization ", command=custom_op)

    def settings_op():
        global gps
        set_win = Toplevel()
        set_win.iconbitmap(r"C:\Users\rayanravesh\PycharmProjects\GUI_Calculator\icon.ico")
        path_label = Label(set_win, text="Current default download path: ")
        path_entry = Entry(set_win, width=30)
        file_read = open('Data.txt', 'r')
        data_base = file_read.read()
        path_entry.insert(0, data_base)
        file_read.close()

        def default_output():
            global location
            file_read2 = open('Data.txt', 'r+')
            file_read2.truncate(0)
            file_read2.close()
            write_file2 = open('Data.txt', 'w')
            write_file2.write(path_entry.get())
            write_file2.close()
            location = path_entry.get() + "\\"
            default_location = location.replace("\\", "\\\\")
        path_btn = Button(set_win, text="Submit ", command=default_output)
        path_label.pack(anchor=CENTER, expand=1)
        path_entry.pack(anchor=CENTER, expand=1)
        path_btn.pack(anchor=CENTER, expand=1)
    submenu.add_command(label="Settings ", command=settings_op)
    submenu.add_separator()
    submenu.add_command(label="Exit", command=root.destroy)

    # the section menu
    editmenu = Menu(menu)
    menu.add_cascade(label="Sections(soon)", menu=editmenu)
    editmenu.add_command(label="Downloader", command=printsth)
    editmenu.add_command(label="Converter", command=printsth)
    editmenu.add_command(label="Media Player", command=printsth)
    editmenu.add_command(label="Editor", command=printsth)
    # the tool bar
    toolbar = Frame(root, bg="light gray")
    insert_button = Button(toolbar, text="Insert an image", command=printsth)
    insert_button.pack(side=LEFT, padx=2, pady=2)
    print_button = Button(toolbar, text="Print", command=printsth)
    print_button.pack(side=LEFT, padx=2, pady=2)
    toolbar.pack(side=TOP, fill=X)

    # the download function
    def download_image():
        global formatname
        if num.get() == 1:
            name = random.randrange(1, 1000000)
        else:
            name = str(name_entry.get())
        formatname = str(format_entry.get())
        '''if var.get() == 1:
            operator = str(url_entry.get())
            formatname = '.' + operator[-3] + operator[-2] + operator[-1]
        else:
            pass'''
        fullname = str(name) + formatname
        url = str(url_entry.get())
        fw = open('file-size.txt', 'w')
        file_size = int(requests.head(url, headers={'accept-encoding': ''}).headers['Content-Length'])
        fw.write(str(file_size))
        fw.close()
        if chum.get() == 1:
            filee = open('Data.txt', 'r')
            destination = filee.read()
            path = destination
            output_entry.insert(0, destination)
            filee.close()
        else:
            output_entry.delete(0, END)
            path = str(output_entry.get()) + "\\"
        urllib.request.urlretrieve(url, path.replace("\\", "\\\\") + fullname)

    # the status bar
    status_bar = Label(root, text="Downloading...", bd=1, relief=SUNKEN, anchor=W)
    status_bar.pack(side=BOTTOM, fill=X)

    # the download frame
    body_frame = Frame(root, bg="light blue")
    download_button = Button(body_frame, text="Download! ", command=download_image, border=3, width=20, height=5)
    download_design = tkFont.Font(size=12, slant='italic')
    download_button['font'] = download_design
    download_button.pack(side=LEFT, pady=5, padx=5)
    body_frame.pack(side=LEFT, fill=Y)
    # the main interaction menu
    inter_frame = Frame(root)
    url_entry = Entry(inter_frame, width=30)
    label = Label(inter_frame, text="Enter the image URL: ")
    file_format = Label(inter_frame, text="Choose your file format: ")
    format_entry = Entry(inter_frame, width=30)
    file_name = Label(inter_frame, text="File's name: ")
    name_entry = Entry(inter_frame, width=30)
    check_name = Checkbutton(inter_frame, text="Give a random name", variable=num)
    # check_format = Checkbutton(inter_frame, text="Download with default format", variable=var)
    check_default = Checkbutton(inter_frame, text="Download to default path", variable=chum)
    output_path = Label(inter_frame, text="Choose output path: ")
    output_entry = Entry(inter_frame, width=30)
    file_name.pack(anchor=CENTER, expand=1)
    name_entry.pack(anchor=CENTER, expand=1)
    check_name.pack(anchor=CENTER, expand=1)
    label.pack(anchor=CENTER, expand=1)
    url_entry.pack(anchor=CENTER, expand=1)
    file_format.pack(anchor=CENTER, expand=1)
    format_entry.pack(anchor=CENTER, expand=1)
    format_entry.insert(0, '.')
    # check_format.pack(anchor=CENTER)
    output_path.pack(anchor=CENTER, expand=1)
    output_entry.pack(anchor=CENTER, expand=1)
    check_default.pack(anchor=CENTER, expand=1)
    inter_frame.pack(expand=1)
    root.mainloop()

    # the end!


main_menu()

【问题讨论】:

标签: python tkinter urllib urllib3 cancel-button


【解决方案1】:

当在单个线程或进程中运行像urlretrieve 这样的函数时,没有办法停止它,因为只有一个线程或进程。

在这种情况下,您从tkinter 调用urlretrieve 回调。这些由 tkinter mainloop 调用,有效地中断了mainloop

对于通过快速连接从主机进行的少量下载,这可能不是问题。但是如果下载需要几秒钟或几分钟,那么你就有问题了。因为当urlretrieve 正在进行时,您的GUI 没有响应,因为mainloop 正在等待您的回调完成。

因此,即使您有一个“取消”按钮,只要urlretrieve 正在运行,它也不会响应

从 Python 目录中的文件 urllib/request.py 中读取 urlretrieve 函数。这不是一个很大的功能,应该相对容易理解。 在内部,urlretrieve 包含一个循环,reads 从 URL 写入文件。默认情况下,这样的读取 wait 直到有任何东西要读取。事情是;没有办法中断这个循环。

因此,无论如何,您将不得不重新编写 urlretrieve。在这个重新编写的版本中,如果您应该继续,您应该检查内部循环的每次迭代。

你基本上有两种选择;

  1. urlretrieve 的功能加入事件循环中。
  2. urlretrieve 的功能工作到不同的线程中。

各有利弊。如果您使用的是 Python 3,则启动 threading.Thread 可能是最简单的操作,因为 Python 3 中的 tkinter 是线程安全的。

有关第一种方法的示例,请参阅我在 github 上的脚本存储库中的 unlock-excel.pyw。在这个应用程序中,一个长操作被分成几个小步骤,通过after方法从tkinter事件循环中调用。

对于使用线程的方法,我没有方便的示例。基本上你必须重写urlretrieve 来检查一个变量(例如threading.Event),它表明它是否应该在内部while 循环中停止。

【讨论】:

  • 因为 Python 3 中的 tkinter 是线程安全的:你有这方面的参考吗?
  • @stovfl 见docs.python.org/3/library/tk.html。 Quote: “此外,内部模块_tkinter 提供了允许Python 和Tcl 交互的线程安全机制。”
  • @stovfl 事实证明,这不是无条件正确的,看看多年来提出的不同相关问题。现实似乎比二进制是/否更复杂。在 stackoverflow 上有一些示例,其中从不同线程调用的 tkinter 函数可以正常工作。
  • _tkinter.c我发现“线程情况很复杂。Tcl不是线程安全的,除非配置了--enable-threads。...从其他线程调用命令是可能;_tkinter 将为解释器线程排队一个事件"
  • @stovfl 我也发现了。在我的平台上,默认构建一个线程化的 tkinter。
猜你喜欢
  • 2019-05-14
  • 2013-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多