如何从新线程更新Kivy进度条值? [英] How to update kivy progress bar value from new thread?

查看:0
本文介绍了如何从新线程更新Kivy进度条值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个图像下载器,作为新的线程和弹出式窗口,其中包含进度条。进度条在下载过程中不会更新,但在下载之后(下载器是用请求编写的,gui应用程序是用kivy制作的)。有什么办法解决这个问题吗?

下载器: 它被分隔在另一个文件中

class Downloader(threading.Thread):

    def __init__(self, url: str, download_monitor):
        super(Downloader, self).__init__(daemon=True)  # daemon dies when main die
        self.url = url
        self.download_monitor = download_monitor  # url popup

    def run(self) -> None:
        # Reset
        self.download_monitor.reset()

        file_name = self.url.split('/')[-1]

        # Less RAM usage
        with requests.get(self.url, stream=True) as req:  # stream=True not to read at once
            req.raise_for_status()
            with open('temp/'+file_name, 'wb') as file:
                chunks = list(enumerate(req.iter_content(chunk_size=8192)))
                self.download_monitor.downloading_progress.max = chunks[-1][0]  # last element
                for progress, chunk in chunks:
                    self.download_monitor.downloading_progress.value = progress
                    file.write(chunk)

弹出.py: 它被分隔在另一个文件中

class UrlPopup(Popup):
    url_input = ObjectProperty()
    downloading_progress = ObjectProperty()

    def __init__(self, **kwargs):
        super(UrlPopup, self).__init__(**kwargs)
        
    def download(self):
        # https://www.nasa.gov/sites/default/files/thumbnails/image/hubble_ngc2903_potw2143a.jpg.jpg
        if self.url_input.text.startswith('https://'):  # if it is url address
            download(self.url_input.text, self)

    def on_dismiss(self):
        self.reset()
        self.url_input.text = ''

    def reset(self):
        self.downloading_progress.max = 0
        self.downloading_progress.value = 0

弹出.kv: 它被分隔在另一个文件中

<UrlPopup>:
    url_input: url_input
    downloading_progress: downloading_progress

    id: downloader
    title: 'URL address'
    size_hint: .25, None
    height: 157

    BoxLayout:
        orientation: 'vertical'
        size_hint_y: None
        height: 64

        TextInput:
            id: url_input

            multiline: False
            size_hint_y: None
            height: 32
            font_size: 16

        ProgressBar:
            id: downloading_progress

            size_hint_y: None
            height: 32

        BoxLayout:
            orientation: 'horizontal'
            size_hint_y: None
            height: 32

            Button:
                text: 'Download'
                on_press: root.download()
            Button:
                text: 'Close'
                on_press: root.dismiss()

EDIT1 ApuCoder我按照你写的做了,但下载后进度仍在更新。 还有别的主意吗? Popup.py

class UrlPopup(Popup):
    url_input = ObjectProperty()
    downloading_progress = ObjectProperty()
    progress_value = NumericProperty()

    def update_progress(self, dt):
        self.progress_value += 1

下载程序.py

 with requests.get(self.url, stream=True) as req:  # stream=True not to read at once
            req.raise_for_status()
            with open('temp/'+file_name, 'wb') as file:
                chunks = list(enumerate(req.iter_content(chunk_size=8192)))
                self.download_monitor.downloading_progress.max = chunks[-1][0]  # last element
                Clock.schedule_interval(self.download_monitor.update_progress, .1)
                for progress, chunk in chunks:
                    #self.download_monitor.downloading_progress.value = progress
                    file.write(chunk)

弹出.kv

ProgressBar:
            id: downloading_progress

            value: root.progress_value
            size_hint_y: None
            height: 32

EDIT2 这与类Downloader在同一个文件中。我在按下按钮时调用此函数

def download(url: str, download_monitor):
    """Other thread"""
    downloader = Downloader(url, download_monitor)
    downloader.start()

推荐答案

假设您要下载一些内容并显示Kivy中正在进行的进程(或当前状态),我更新并修改了您的一些代码以创建一个最小的示例。

在这种情况下,不需要创建新的Thread类,而是每次都创建一个新的线程对象,并将target设置为用于在磁盘中读取和写入二进制数据的某种方法(这里是start_download)。因此,可以在此方法内控制进度,从而不需要调度。

from threading import Thread
import requests

from kivy.app import runTouchApp
from kivy.lang import Builder
from kivy.properties import (
    BooleanProperty,
    NumericProperty,
    ObjectProperty,
)
from kivy.uix.popup import Popup
from kivy.uix.screenmanager import Screen



Builder.load_string("""

<DownLoadScreen>:

    Button:
        text: "Open Downloader"
        on_release: root.open_downloader()


<UrlPopup>:
    url_input: url_input
    title: 'URL address'
    size_hint: .75, None
    height: "450dp"

    BoxLayout:
        orientation: "vertical"

        TextInput:
            id: url_input
            text: "https://www.nasa.gov/sites/default/files/thumbnails/image/hubble_ngc2903_potw2143a.jpg.jpg"
            multiline: False
            size_hint_y: None
            height: "64dp"
            font_size: "16sp"

        ProgressBar:
            pos_hint: {"center_x" : 0.5}
            value: root.prog_val
            max: root.tot_size

        Label:
            id: lbl
            text: "Downloading file...({:.0%})".format(root.prog_val/root.tot_size) if root.has_started else ""

        BoxLayout:
            size_hint_y: None
            height: dp(48)

            Button:
                text: 'Download'
                on_release: root.download()

            Button:
                text: 'Close'
                on_press: root.dismiss()
""")



class UrlPopup(Popup):

    url_input = ObjectProperty()
    prog_val = NumericProperty(0) # To capture the current progress.
    tot_size = NumericProperty(1) # Total size of the file/content. Setting the default value to 1 to avoid ZeroDivisionError, though will not affect anyhow.
    has_started = BooleanProperty(False) # Just to manipulate the label text.

    def start_download(self):
        self.has_started = True
        self.url = self.url_input.text
#       file_name = self.url.split('/')[-1]
        with requests.get(self.url, stream=True) as req:
            if req.status_code == 200: # Here, you can create the binary file.
#               chunks = list(enumerate(req.iter_content(chunk_size=8192))) # This may take more memory for larger file.
                self.tot_size = int(req.headers["Content-Length"])
                item_size = 2048 # Reducing the chunk size increases writing time and so needs more time in progress.
                for i, chunk in enumerate(req.iter_content(chunk_size = item_size)):
                    self.prog_val = i*item_size
#                   file.write(chunk)
                self.ids.lbl.text = "Download completed." # A confirmation message.

    def download(self):
        """A new thread object will be created each time this method is revoked. But be careful about the threads already created."""
        Thread(target = self.start_download).start()

    def on_dismiss(self):
        self.url_input.text = ""
        self.has_started = False



class DownLoadScreen(Screen):

    def open_downloader(self):
        UrlPopup().open()


runTouchApp(DownLoadScreen())

如果符合您的需要,请让我知道。

这篇关于如何从新线程更新Kivy进度条值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆