gpt4 book ai didi

python - 在不锁定事件循环的情况下使用 Kivy 应用程序下载文件

转载 作者:行者123 更新时间:2023-11-28 22:36:21 25 4
gpt4 key购买 nike

这是一个带有按钮和进度条的 Kivy 应用程序。当按下按钮时,一个 ZIP 文件将从网上下载并解压缩。进度条前进以标记下载进度。

问题是,下载锁定了 Kivy 事件循环,在下载过程中卡住了应用程序。如何在后台下载和解压缩文件?

from __future__ import division
import os
import requests
import zipfile
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout

ZIP_URL = 'https://www.python.org/ftp/python/3.5.1/python-3.5.1-embed-win32.zip'
ZIP_FILENAME = 'Python351.zip'

kv_string = """
<RootWidget>
BoxLayout:
orientation: "vertical"
Button:
id: download_button
text: "Download content"
on_press: self.parent.parent.download_content()
ProgressBar:
id: download_progress_bar
max: 1
value: 0.1
"""

Builder.load_string(kv_string)


class RootWidget(BoxLayout):
def __init__(self, **kwargs):
super(RootWidget, self).__init__(**kwargs)

def download_content(self):
self.ids["download_button"].disabled = True

total_size_KiB = 6023182 / 1024 # DEBUG: hardcoded
print("Downloading file: %s" % ZIP_URL)
with open(ZIP_FILENAME, 'wb') as handle:
response = requests.get(ZIP_URL, stream=True)

if not response.ok:
print("Something went wrong.")
return

current_size_KiB = 0
for block in response.iter_content(1024):
percent_downloaded = current_size_KiB / total_size_KiB
if not current_size_KiB % 100:
print("%.2f%% downloaded so far" % (percent_downloaded * 100))
self.ids['download_progress_bar'].value = percent_downloaded

handle.write(block)

current_size_KiB += 1

self.unzip_content()

def unzip_content(self):
print("Unzipping file")
fh = open(ZIP_FILENAME, 'rb')
z = zipfile.ZipFile(fh)
ZIP_EXTRACT_FOLDER = ZIP_FILENAME + '_extracted'
if not os.path.exists(ZIP_EXTRACT_FOLDER):
os.makedirs(ZIP_EXTRACT_FOLDER)
z.extractall(ZIP_EXTRACT_FOLDER)
fh.close()
os.remove(ZIP_FILENAME)

print("Done")


class MyApp(App):

def build(self):
return RootWidget()


if __name__ == '__main__':
MyApp().run()

最佳答案

首先,正如@Nykakin 所建议的,要使下载异步,请使用kivy.network.urlrequest.UrlRequest:

def download_content(self):
self.ids["download_button"].disabled = True
req = UrlRequest(ZIP_URL, on_progress=self.update_progress,
chunk_size=1024, on_success=self.unzip_content,
file_path=ZIP_FILENAME)

def update_progress(self, request, current_size, total_size):
self.ids['download_progress_bar'].value = current_size / total_size

其次,如@KeyWeeUsr 所建议的那样,通过使用 threading 模块更改解压缩方法,使其不会阻塞事件循环:

def unzip_content(self, req, result):
threading.Thread(target=self.unzip_thread).start()

def unzip_thread(self):
# ... (same as unzip_content method in question)

这是完整的新版本:

from __future__ import division
import os
import zipfile
import threading
import time
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.network.urlrequest import UrlRequest

ZIP_URL = 'https://www.python.org/ftp/python/3.5.1/python-3.5.1-embed-win32.zip'
ZIP_FILENAME = 'Python351.zip'

kv_string = """
<RootWidget>
BoxLayout:
orientation: "vertical"
Button:
id: download_button
text: "Download content"
on_press: self.parent.parent.download_content()
ProgressBar:
id: download_progress_bar
max: 1
value: 0
"""

Builder.load_string(kv_string)


class RootWidget(BoxLayout):

stop = threading.Event()

def __init__(self, **kwargs):
super(RootWidget, self).__init__(**kwargs)

def download_content(self):
self.ids["download_button"].disabled = True
req = UrlRequest(ZIP_URL, on_progress=self.update_progress,
chunk_size=1024, on_success=self.unzip_content,
file_path=ZIP_FILENAME)

def update_progress(self, request, current_size, total_size):
self.ids['download_progress_bar'].value = current_size / total_size

def unzip_content(self, req, result):
threading.Thread(target=self.unzip_thread).start()

def unzip_thread(self):
print("Unzipping file")
fh = open(ZIP_FILENAME, 'rb')
z = zipfile.ZipFile(fh)
ZIP_EXTRACT_FOLDER = ZIP_FILENAME + '_extracted'
if not os.path.exists(ZIP_EXTRACT_FOLDER):
os.makedirs(ZIP_EXTRACT_FOLDER)
z.extractall(ZIP_EXTRACT_FOLDER)
fh.close()
os.remove(ZIP_FILENAME)
time.sleep(4) # DEBUG: stretch out the unzip method to test threading

print("Done")


class MyApp(App):

def on_stop(self):
# The Kivy event loop is about to stop, set a stop signal;
# otherwise the app window will close, but the Python process will
# keep running until all secondary threads exit.
self.root.stop.set()

def build(self):
return RootWidget()


if __name__ == '__main__':
MyApp().run()

关于python - 在不锁定事件循环的情况下使用 Kivy 应用程序下载文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37502084/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com