Python 使用Kivy应用程序下载文件,无需锁定事件循环
这是一个Kivy应用程序,它有一个按钮和一个进度条。按下按钮后,将从web下载一个ZIP文件并解压缩。进度条前进以标记下载进度 问题是,下载锁定了Kivy事件循环,在下载过程中冻结了应用程序。如何在后台下载和解压缩文件Python 使用Kivy应用程序下载文件,无需锁定事件循环,python,kivy,Python,Kivy,这是一个Kivy应用程序,它有一个按钮和一个进度条。按下按钮后,将从web下载一个ZIP文件并解压缩。进度条前进以标记下载进度 问题是,下载锁定了Kivy事件循环,在下载过程中冻结了应用程序。如何在后台下载和解压缩文件 from __future__ import division import os import requests import zipfile from kivy.app import App from kivy.lang import Builder from kivy.ui
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()
来自未来进口部的
导入操作系统
导入请求
进口拉链
从kivy.app导入应用程序
从kivy.lang导入生成器
从kivy.uix.boxlayout导入boxlayout
邮政编码https://www.python.org/ftp/python/3.5.1/python-3.5.1-embed-win32.zip'
ZIP_文件名='Python351.ZIP'
kv_字符串=”“
盒子布局:
方向:“垂直”
按钮:
id:下载按钮
文本:“下载内容”
按:self.parent.parent.download\u content()
进度条:
id:下载进度条
最高:1
数值:0.1
"""
建筑商。荷载串(千伏串)
类RootWidget(BoxLayout):
定义初始(自我,**kwargs):
超级(RootWidget,self)。\uuuuu init\uuuuuu(**kwargs)
def下载内容(自我):
self.ids[“下载按钮”].disabled=True
总大小=6023182/1024调试:硬编码
打印(“下载文件:%s”%ZIP\u URL)
以open(ZIP_文件名,'wb')作为句柄:
response=requests.get(ZIP\u URL,stream=True)
如果没有响应。确定:
打印(“出了问题。”)
返回
当前大小=0
对于响应中的块。iter_内容(1024):
下载百分比=当前大小/总大小
如果不是当前大小,请输入%100:
打印(“%.2f%%下载到目前为止”%(下载百分比*100))
self.ids['download\u progress\u bar'].value=下载百分比
handle.write(块)
当前大小\u KiB+=1
self.unzip_content()
def解压_内容(自身):
打印(“解压缩文件”)
fh=打开(ZIP_文件名'rb')
z=zipfile.zipfile(fh)
ZIP_EXTRACT_FOLDER=ZIP_FILENAME+“_EXTRACT”
如果不存在os.path(ZIP\u EXTRACT\u文件夹):
os.makedirs(ZIP\u EXTRACT\u文件夹)
z、 extractall(ZIP\u EXTRACT\u文件夹)
fh.close()
删除(ZIP_文件名)
打印(“完成”)
类别MyApp(应用程序):
def生成(自):
返回RootWidget()
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
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所建议的,通过使用线程化模块,更改解压方法,使其不会阻塞事件循环:
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()
来自未来进口部的
导入操作系统
进口拉链
导入线程
导入时间
从kivy.app导入应用程序
从kivy.lang导入生成器
从kivy.uix.boxlayout导入boxlayout
从kivy.network.urlrequest导入urlrequest
邮政编码https://www.python.org/ftp/python/3.5.1/python-3.5.1-embed-win32.zip'
ZIP_文件名='Python351.ZIP'
kv_字符串=”“
盒子布局:
方向:“垂直”
按钮:
id:下载按钮
文本:“下载内容”
按:self.parent.parent.download\u content()
进度条:
id:下载进度条
最高:1
数值:0
"""
建筑商。荷载串(千伏串)
类RootWidget(BoxLayout):
stop=threading.Event()
定义初始(自我,**kwargs):
超级(RootWidget,self)。\uuuuu init\uuuuuu(**kwargs)
def下载内容(自我):
self.ids[“下载按钮”].disabled=True
req=UrlRequest(ZIP\u URL,on\u progress=self.update\u progress,
chunk\u size=1024,on\u success=self.unzip\u content,
文件路径=ZIP文件名)
def更新进度(自身、请求、当前大小、总大小):
self.ids['download\u progress\u bar'].value=当前大小/总大小
def解压_内容(自身、请求、结果):
threading.Thread(target=self.unzip_Thread).start()
def解压_线程(自):
打印(“解压缩文件”)
fh=打开(ZIP_文件名'rb')
z=zipfile.zipfile(fh)
ZIP_EXTRACT_FOLDER=ZIP_FILENAME+“_EXTRACT”
如果不存在os.path(ZIP\u EXTRACT\u文件夹):
os.makedirs(ZIP\u EXTRACT\u文件夹)
z、 extractall(ZIP\u EXTRACT\u文件夹)
fh.close()
删除(ZIP_文件名)
time.sleep(4)#DEBUG:扩展解压方法以测试线程
打印(“完成”)
类别MyApp(应用程序):
def on_停止(自):
#Kivy事件循环即将停止,设置停止信号;
#否则,应用程序窗口将关闭,但Python进程将关闭
#继续运行,直到所有次线程退出。
self.root.stop.set()
def生成(自):
返回RootWidget()
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
MyApp().run()
+您可以使用,以便在web上发出异步请求。您还可以在“进度”属性中将回调设置为,以更新进度条。