ProgressBar 从它停止的地方恢复 [英] ProgressBar resume from where it stopped

查看:49
本文介绍了ProgressBar 从它停止的地方恢复的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我通过 PYQT5python 3.7 创建了一个桌面应用程序,通过单击下载按钮下载视频并将其保存在 PC 本地.

I have created a desktop application by PYQT5 and python 3.7 to download a video by clicking the download button and save it locally in the PC.

该代码将从 (lineEdit.text()) 中获取标记为 "URL" 的视频链接,并将其保存在 (lineEdit_2.text()) 中标记为 <强>另存为".如果下载因任何原因停止,将通过按下开始下载按钮再次恢复.此外,ProgressBar 会随着下载视频从 1% 开始到 100%.一切顺利.

The code will fetch the video link from (lineEdit.text()) which is labeled "URL" and save it in the local directory in (lineEdit_2.text()) which is labeled "SAVE AS". If the download stops for any reason, it will be resumed again by press the start download button. In addition, the ProgressBar start from 1% until 100% along with downloading the video. Everything working smoothly.

问题是,一旦视频因任何原因停在中间,它就会再次恢复下载,但 ProgressBar 应该从停止的地方开始,但事实并非如此.例如,如果它在 50% 停止,那么应该从 50% 恢复并继续.但是,它从 0% 开始(从头开始).

The question is, once the video stops in the middle for any reason then it resumes the downloading again but the ProgressBar should start from where it stopped but it is not. For example, if it stops in 50% then should be resumed from 50% and continue. However, it starts from 0% (from the beginning).

```def curl_progress(self,total, existing, totalfrac,fracmb):

    global frac,tsize,size,save_location

    try:

        frac= float(existing)/float(total)
        self.progressBar.setValue(totalfrac)
        QApplication.processEvents()

    except (ZeroDivisionError, RuntimeError, TypeError, NameError):
        frac = 0

    self.textBrowser.append("Downloaded %d/%d %d%%" % (existing, total, totalfrac))


    if frac ==1.0:
        self.textBrowser.append("")
        size = os.path.getsize(save_location)
        tsize= (size /1024 /1024)
        QMessageBox.information(self,"Download Completed", "The Download is Finished and the size is %03.2f MB" %(tsize,))
        self.textBrowser.append('Size of file is %03.2f MB' %(tsize,))
        self.progressBar.setValue(0)
        self.lineEdit.setText('')
        self.lineEdit_2.setText('')
        QMessageBox.close(self)


    else:
        self.textBrowser.append("Downloaded %d/%d %d%%" % (existing, total, totalfrac))


def curl_limit_rate(self,rate_limit):
    global tsize,size,save_location
    url= self.lineEdit.text()
    save_location = self.lineEdit_2.text()
    if len(url) == 0 and len(save_location) == 0:
        QMessageBox.information(self, "Error", "Please put the links")
        return
    if len(url) > 0 and len(save_location) == 0:
        QMessageBox.information(self, "Error", "Please put the location")
        return

    if len(url) == 0 and len(save_location) > 0:
        QMessageBox.information(self, "Error", "Please put the link")
        return

    if len(url) > 0 and len(save_location) > 0:

        c = pycurl.Curl()
        c.setopt(pycurl.CAINFO, certifi.where())
        c.setopt(c.URL,url)
        c.setopt(c.MAX_RECV_SPEED_LARGE, rate_limit)
        if os.path.exists(save_location):
            file_id = open(save_location, "ab")
            c.setopt(c.RESUME_FROM, os.path.getsize(save_location))
        else:
            file_id = open(save_location, "wb")

        c.setopt(c.WRITEDATA, file_id)
        c.setopt(c.NOPROGRESS, 0)
        c.setopt(c.PROGRESSFUNCTION, self.curl_progress)
        c.perform()
        c.close()
    else:
        QMessageBox.information(self, "Error", "Unknown error!")```

图片

非常感谢,

推荐答案

在指出解决方案之前,我必须指出你不应该在主线程中运行 pycurl,因为它是阻塞的,你必须在另一个线程中执行它并将信息发送到主线程以便显示.

Before pointing out the solution, I must point out that you should not run pycurl in the main thread since it is blocking, instead you must execute it in another thread and send the information to the main thread so that it can be shown.

切入正题,当您计算百分比时,它使用以下公式:

Going to the point, the idea is that when you calculate the percentage it is using the following formula:

progress = 100 * (bytes_downloaded + size_of_resume_file) / (total_bytes + size_of_resume_file)

考虑到上述解决方案是:

Considering the above the solution is:

import os

import certifi
import pycurl

from PyQt5 import QtCore, QtWidgets


class Downloader(QtCore.QObject):
    started = QtCore.pyqtSignal()
    finished = QtCore.pyqtSignal()
    progressChanged = QtCore.pyqtSignal(int)
    error = QtCore.pyqtSignal(int, str)

    bytesChanged = QtCore.pyqtSignal(int, int)

    @QtCore.pyqtSlot(str, str)
    def download(self, url, save_location):
        pass


class PycURLDownloader(Downloader):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._resume_size = 0
        self._c = pycurl.Curl()
        self._flag_stop = 0

    def download(self, url, save_location):
        self._flag_stop = 0
        exist_path = os.path.exists(save_location)
        self.started.emit()
        with open(save_location, "ab" if exist_path else "wb") as file_id:
            self._c.setopt(pycurl.CAINFO, certifi.where())
            self._c.setopt(pycurl.URL, url)
            self._c.setopt(pycurl.MAX_RECV_SPEED_LARGE, 1024)
            if exist_path:
                self._c.setopt(pycurl.RESUME_FROM, os.path.getsize(save_location))
                self._resume_size = os.path.getsize(save_location)
            else:
                self._resume_size = 0
            self._c.setopt(pycurl.WRITEDATA, file_id)
            self._c.setopt(pycurl.NOPROGRESS, 0)
            self._c.setopt(pycurl.PROGRESSFUNCTION, self._progress_callaback)
            try:
                self._c.perform()
            except pycurl.error as e:
                self.error.emit(*e.args)
            else:
                self.finished.emit()
                self._c.close()

    @QtCore.pyqtSlot()
    def stop(self):
        self._flag_stop = 1

    def _progress_callaback(self, total, existing, totalfrac, fracmb):
        frac = 0

        if existing > 0 and total > 0:
            frac = int(
                100 * (existing + self._resume_size) / (total + self._resume_size)
            )
            self.bytesChanged.emit(existing, total)

        self.progressChanged.emit(frac)
        if QtCore.QThread.currentThread().isInterruptionRequested():
            return 1


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.url_lineedit = QtWidgets.QLineEdit()
        self.save_location_lineedit = QtWidgets.QLineEdit()
        browse_button = QtWidgets.QPushButton(self.tr("Browse"))
        self.download_progressbar = QtWidgets.QProgressBar(minimum=0, maximum=100)
        self.download_log_browser = QtWidgets.QTextBrowser()
        self.start_download_button = QtWidgets.QPushButton(self.tr("Start Download"))

        widget = QtWidgets.QWidget()
        widget.setContentsMargins(0, 0, 0, 0)
        hlay = QtWidgets.QHBoxLayout(widget)
        hlay.addWidget(self.save_location_lineedit)
        hlay.addWidget(browse_button)
        hlay.setContentsMargins(0, 0, 0, 0)

        flay = QtWidgets.QFormLayout()
        flay.addRow("URL", self.url_lineedit)
        flay.addRow("Save as", widget)
        flay.addRow("", self.download_progressbar)
        flay.addRow("", QtWidgets.QLabel(self.tr("Packets output in Bytes")))
        flay.addRow("", self.download_log_browser)

        hlay2 = QtWidgets.QHBoxLayout()
        hlay2.addStretch()
        hlay2.addWidget(self.start_download_button)
        hlay2.addStretch()

        vlay = QtWidgets.QVBoxLayout(self)
        vlay.addLayout(flay)
        vlay.addLayout(hlay2)

        self.start_download_button.clicked.connect(self.start_download)
        browse_button.clicked.connect(self.select_save_location)

        self._thread = QtCore.QThread(self)
        self._thread.start()

        self._downloader = PycURLDownloader()
        self._downloader.moveToThread(self._thread)

        self._downloader.progressChanged.connect(self.download_progressbar.setValue)
        self._downloader.bytesChanged.connect(self.on_bytesChanged)
        self._downloader.started.connect(self.on_started)
        self._downloader.finished.connect(self.on_finished)

        self.url_lineedit.setText("http://techslides.com/demos/sample-videos/small.mp4")

    @QtCore.pyqtSlot()
    def start_download(self):
        url = self.url_lineedit.text()
        save_location = self.save_location_lineedit.text()
        if not url:
            QtWidgets.QMessageBox.information(self, "Error", "Please put the links")
            return
        elif not save_location:
            QtWidgets.QMessageBox.information(self, "Error", "Please put the location")
            return

        wrapper = partial(self._downloader.download, url, save_location)
        QtCore.QTimer.singleShot(0, wrapper)

    @QtCore.pyqtSlot()
    def select_save_location(self):
        filename, _ = QtWidgets.QFileDialog.getSaveFileName(self, "Select")
        if filename:
            self.save_location_lineedit.setText(filename)

    @QtCore.pyqtSlot(int, str)
    def on_error(self, t, msg):
        QtWidgets.QMessageBox.information(self, "Error", msg)

    @QtCore.pyqtSlot(int, int)
    def on_bytesChanged(self, existing, total):
        self.download_log_browser.append(
            "Downloaded %d/%d %d%%" % (existing, total, 100 * existing / total)
        )

    @QtCore.pyqtSlot()
    def on_started(self):
        self.start_download_button.setEnabled(False)

    @QtCore.pyqtSlot()
    def on_finished(self):
        self.start_download_button.setEnabled(True)

    def closeEvent(self, event):
        self._thread.requestInterruption()
        self._thread.quit()
        self._thread.wait()
        super().closeEvent(event)


if __name__ == "__main__":
    from functools import partial
    import sys

    app = QtWidgets.QApplication(sys.argv)

    w = Widget()
    w.resize(640, 480)
    w.show()

    ret = app.exec_()

    sys.exit(ret)

这篇关于ProgressBar 从它停止的地方恢复的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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