ProgressBar резюме с того места, где он остановился - PullRequest
1 голос
/ 07 января 2020

Я создал настольное приложение с помощью PYQT5 и python 3,7 , чтобы загрузить видео, нажав кнопку загрузки, и сохранить его локально в P C.

Код будет извлекать ссылку на видео из (lineEdit.text ()), помеченной "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!")```

Изображение

enter image description here

Большое спасибо заранее

1 Ответ

1 голос
/ 07 января 2020

Прежде чем указывать на решение, я должен указать, что вы не должны запускать 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)
...