ProgressBar 从它停止的地方恢复

ProgressBar resume from where it stopped

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

代码将从(lineEdit.text())中获取视频link,标签为"URL"并将其保存在本地(lineEdit_2.text()) 中标记为 "SAVE AS" 的目录。如果下载因任何原因停止,将通过按下开始下载按钮再次恢复。此外,ProgressBar 会随着视频的下载从 1% 开始一直到 100%。一切顺利。

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

```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 因为它是阻塞的,而你必须在另一个线程中执行它并将信息发送到主线程以便显示。

进入正题,想法是当您计算百分比时使用以下公式:

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

考虑到上面的解决方案是:

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)