ProgressBar resume from where it stopped

纵然是瞬间 提交于 2021-01-28 06:20:21

问题


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.

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.

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!")```

The picture

Many thanks in advance,


回答1:


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)


来源:https://stackoverflow.com/questions/59630587/progressbar-resume-from-where-it-stopped

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!