1

So I'm trying to make a simple file downloader in Python 3.4.2 and PyQt5

QThreads seems to be the way but there's no tutorials online or examples that I could understand for PyQt5. All I could find was Qt5 Reference for C/C++ and bunch of PyQt4 tutorials that don't work for PyQt5 and Python 3

Here's the GUI screenshot: https://i.stack.imgur.com/FDsDh.png

And here's my code:

#!usr/bin/env python3

from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

from string import Template
import urllib.request
import sys


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

        lblUrl= QLabel("File URL:")
        self.txtURL = QLineEdit()
        self.bttDL = QPushButton("&Download")
        self.pbar = QProgressBar()
        self.pbar.setMinimum(0)

        buttonLayout1 = QVBoxLayout()
        buttonLayout1.addWidget(lblUrl)
        buttonLayout1.addWidget(self.txtURL)
        buttonLayout1.addWidget(self.bttDL)
        buttonLayout1.addWidget(self.pbar)

        self.bttDL.clicked.connect(self.bttPush)

        mainLayout = QGridLayout()
        mainLayout.addLayout(buttonLayout1, 0, 1)

        self.setLayout(mainLayout)
        self.setWindowTitle("pySFD")

    def bttPush(self):
        # check if the download is already running or just disable the button
        # while it's running
        url = self.txtURL.text()
        if url == "":
            QMessageBox.information(self, "Empty URL",
                    "Please enter the URL of the file you want to download.")
            return
        else:
            filename = str(QFileDialog.getSaveFileName(self, 'Choose the download location and file name', '.'))
            filename = filename[:-6]
            filename = filename.split("('",maxsplit=1)[1]

        self.dlThread = downloaderThread()
        self.dlThread.connect(dlThread.run)
        self.dlThread.start()
        self.dlThread.emit(url)

class downloaderThread(QThread):
    def __init__(self):
        QThread.__init__(self)

    def __del__(self):
        self.wait()

    def run(self, dlLink):
            while dlLink.length == 0:
                QThread.sleep(1)
                pass
            def report(block_count, block_size, total_size):
                if block_count == 0:
                    self.pbar.setValue(0)
                if (block_count * block_size) == total_size:
                    QMessageBox.information(self,"Done!","Download finished!")
                    return
                self.pbar.setValue(self.pbar.value() + block_size)

            urllib.request.urlretrieve(dlLink, filename, reporthook=report)
            self.terminate()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    screen = Form()
    screen.show()
    sys.exit(app.exec_())

I've tried a lot of ways and it just doesn't seem to work.

How do I make the progress bar (self.pbar) show the download progress in real time?

PS. This downloader is on my GitHub: https://github.com/dKatara/pySFD

dKatara
  • 31
  • 5
  • You have two options: use QThread and use signals/slots to have the threads talk; or use python threads and use queues to talk. http://stackoverflow.com/questions/26472069/how-to-access-the-gui-output/26472856#26472856 an example of the latter – mdurant Oct 23 '14 at 23:32

1 Answers1

2

Fixed it, it's not done yet, but threading works great!

Most recent version is on my GitHub here: https://github.com/dKatara/pySFD

#!usr/bin/env python3

from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

import urllib.request
import sys
import threading


dlThread = 0
hWindow = 0
fProgressCounter = 0.0

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

        global hWindow
        hWindow = self

        lblUrl = QLabel("File URL:")
        self.txtURL = QLineEdit()
        self.bttDL = QPushButton("&Download")
        self.pbar = QProgressBar()

        self.pbar.setMinimum(0)
        self.pbar.setMaximum(100)

        buttonLayout1 = QVBoxLayout()
        buttonLayout1.addWidget(lblUrl)
        buttonLayout1.addWidget(self.txtURL)
        buttonLayout1.addWidget(self.bttDL)
        buttonLayout1.addWidget(self.pbar)

        self.bttDL.clicked.connect(self.bttPush)

        mainLayout = QGridLayout()
        mainLayout.addLayout(buttonLayout1, 0, 1)

        self.setLayout(mainLayout)
        self.setWindowTitle("pySFD")
    def bttPush(self):
        global dlThread

        hSignals = sigHandling()
        hSignals.dlProgress_update.connect(hSignals.pbar_incrementer)
        hSignals.dlProgress_done.connect(hSignals.dlDone)

        url = self.txtURL.text()
        if url == "":
            QMessageBox.information(self, "Empty URL",
                    "Please enter the URL of the file you want to download.")
            return
        else:
            filename = str(QFileDialog.getSaveFileName(self, 'Choose the download location and file name', '.')) ## DETECT A CANCEL
            filename = filename[:-6]
            filename = filename.split("('",maxsplit=1)[1]

        self.bttDL.setEnabled(False)
        dlThread = threading.Thread(target=hSignals.runDL,args=(url, filename))
        dlThread.start()
        return

    def pbarIncValue(self, val):
        global fProgressCounter
        #print("pbarIncValue({0})\nfProgressCounter={1}".format(val,fProgressCounter))

        if self.pbar.value() >= 100:
            self.dlProgress_done.emit()
            return
        if fProgressCounter > 1.0: # FIX
            self.pbar.setValue(self.pbar.value() + 1)
            fProgressCounter -= 1.0
            fProgressCounter += val
        else:
            fProgressCounter += val

class sigHandling(QObject):
    dlProgress_update = pyqtSignal(float)
    dlProgress_done = pyqtSignal()

    @pyqtSlot(float)
    def pbar_incrementer(self, val):
        hWindow.pbarIncValue(val)

    @pyqtSlot()
    def dlDone(self):
        print("in dlDone")
        hWindow.pbar.setValue(100)
        hWindow.bttDL.setEnabled(True)

    def runDL(self, dlLink, filename):
        #print("in run")
        global dlThread, hWindow
        def report(block_count, block_size, total_size):
            if block_count == 0:
                #print("block_count == 0")
                self.dlProgress_update.emit(0)
            if (block_count * block_size) == total_size:
                self.dlProgress_done.emit()
            incAmount = float((100*block_size) / total_size)
            #print("BS={0} TS={1} incAmount={2}".format(block_size,total_size,incAmount))
            self.dlProgress_update.emit(incAmount)

        urllib.request.urlretrieve(dlLink, filename, reporthook=report)
        #print("emit dlProgress_done")
        self.dlProgress_done.emit()
        #print("about to leave dlThread")
        pass

if __name__ == '__main__':
    app = QApplication(sys.argv)
    screen = Form()
    screen.show()
    sys.exit(app.exec_())
dKatara
  • 31
  • 5