Search code examples
pythonpyqtpyqt5qthread

How To Apply PyQt QThread on this Piece of code


# -*- coding: utf-8 -*-
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.uic import loadUiType
import youtube_dl
import pafy
import urllib.request
import urllib.parse
from urllib.parse import *
import win32clipboard
import sys
import os
import humanize
import subprocess
import time
import shutil
import re
from pySmartDL import SmartDL
from os.path import splitext, basename
from os import path


Close = False
ShutDown = False
Sleep = False
Restart = False
Paused = False
Stopped = False
Start = True
Resume = True

if getattr(sys, 'frozen', False):
    # frozen
    dir_ = os.path.dirname(sys.executable)
else:
    # unfrozen
    dir_ = os.path.dirname(os.path.realpath(__file__))
FORM_CLASS, _ = loadUiType(path.join(dir_, "main.ui"))


class MainApp(QMainWindow, FORM_CLASS):

    def __init__(self, parent=None):
        super(MainApp, self).__init__(parent)
        QMainWindow.__init__(self)
        self.setupUi(self)
        self.lineEdit.installEventFilter(self)
        self.lineEdit_3.installEventFilter(self)
        self.lineEdit_6.installEventFilter(self)
        self.Handle_Ui()
        self.Handle_Buttons()



    def closeEvent(self, evnt):
        if self._want_to_close:
            super(MainApp, self).closeEvent(evnt)
            sys.exit()

    def Handle_Ui(self):
        #self.lineEdit.setFocus()
        self.setFixedSize(861,441)


    def Handle_Buttons(self):
        self.pushButton_3.clicked.connect(self.open_file_loction)
        self.pushButton_2.clicked.connect(self.Start)
        self.pushButton_13.clicked.connect(self.Pause)
        self.pushButton_14.clicked.connect(self.Stop)
        self.pushButton.clicked.connect(self.Handle_Browse)
        self.pushButton_4.clicked.connect(self.Download_youtube_video)
        self.pushButton_10.clicked.connect(self.get_quality)
        self.pushButton_5.clicked.connect(self.Browse2)
        self.pushButton_6.clicked.connect(self.open_file_location2)
        self.pushButton_11.clicked.connect(self.Search_Qualities)
        self.pushButton_7.clicked.connect(self.Browse3)
        self.pushButton_9.clicked.connect(self.download_playlist)
        self.pushButton_8.clicked.connect(self.open_file_location3)
        self.pushButton_12.clicked.connect(self.open_video)
        self.comboBox_2.currentIndexChanged.connect(self.Action_Happened)
        self.comboBox_3.currentIndexChanged.connect(self.Action_Happened)
        self.comboBox_4.currentIndexChanged.connect(self.Action_Happened)

    def Start(self):
        global Start
        global Stopped
        global Paused
        Start = True
        Stopped = False
        Paused = False
        self.Download()

    def Pause(self):
        global Paused
        global Start
        global Resume
        if self.pushButton_13.text()=="Pause Downloading":
            Paused = True
            Start = False
            Stopped = False
            Resume = False
            self.pushButton_13.setText("Resume Downloading")
            QApplication.processEvents()
        elif self.pushButton_13.text()=="Resume Downloading":
            Start = True
            Paused = False
            Resume = True
            Stopped = False
            self.pushButton_13.setText("Pause Downloading")
            QApplication.processEvents()



    def Stop(self):
        global Stopped
        global Start
        Stopped = True
        Start = False
        Paused = False
        self.Download()


    def Download(self):
        directory = os.path.expanduser("~") + "\AppData\Local\Temp\pySmartDL"
        if not os.path.exists(directory):
            os.makedirs(directory)
        try:
            global Paused
            global Stopped
            global Start
            global XX
            url = self.lineEdit.text()
            save_location = self.lineEdit_2.text()
            obj = SmartDL(url, progress_bar=False)
            if Start == True:
                try:
                    obj.start(blocking=False)
                    while True:
                        self.progressBar.setValue(obj.get_progress()*100)
                        self.label_8.setText("Downloaded: " + str(obj.get_dl_size(human=True)))
                        self.label_38.setText("Speed: " + str(obj.get_speed(human=True)))
                        self.label_39.setText("Remaining Time: " + str(obj.get_eta(human=True)))
                        time.sleep(0.2)
                        QApplication.processEvents()
                        if Paused == True:
                            obj.pause()
                            QApplication.processEvents()
                        if Resume == True:
                                obj.unpause()
                                QApplication.processEvents()
                        if obj.isFinished():
                            break
                        if Stopped == True:
                            obj.stop()
                            self.progressBar.setValue(0)
                            break

                    if obj.isSuccessful():
                        #os.rename(obj.get_dest(), save_location)
                        shutil.move(obj.get_dest(), save_location)

                    if Close == True:
                        QApplication.quit()
                    elif ShutDown == True:
                        os.system('shutdown -s')
                    elif Sleep == True:
                        os.system("rundll32.exe powrprof.dll,SetSuspendState 0,1,0")
                    elif Restart == True:
                        subprocess.call(["shutdown", "/r"])
                    if Stopped == False:
                        QMessageBox.information(self, "Download Completed", "Your Download is Completed")
                except:
                    QMessageBox.warning(self, "Download Error", "Download Failed")
                    pass
        except Exception as e:
            pass



def main():
    app = QApplication(sys.argv)
    window = MainApp()
    window.show()
    app.exec_()

if __name__ == '__main__':
    main()

I've been looking around for help on how to Apply Qthread on the above piece of code to update the progress Bar Dynamically with no "Not Responding problem"

I've read many topics and I could understand the main concept of Qthread but still can't get the idea on how to Apply it to my Code taking in consideration that download function is connected with a download button not running infinitely.

should I make a sub Qthread class or what can I do ?

If You Can help me with an example on how to use it with the above piece of code and I will Apply it on the remaining code of my Gui App..

Thanks in advance.


Solution

  • When implementing a QThread the task to be performed should be done in the run () method, if you want to update the GUI with data provided by the thread should not be done directly but through signals since the GUI should only be updated in the main thread called by this the GUI thread.

    class DownloadThread(QThread):
        dataChanged = pyqtSignal(int, str, str, str)
        Started, Paused, Resume, Stopped = range(4)
        downloadError = pyqtSignal()
        downloadFinished = pyqtSignal()
    
        def __init__(self, parent=None):
            QThread.__init__(self, parent)
            self.state = DownloadThread.Stopped
            self.params = {"url": "", "save_location": ""}
    
        def setParams(self, params):
            self.params = params
    
        def setState(self, state):
            self.state = state
    
        def run(self):
            obj = SmartDL(self.params["url"], progress_bar=False)
            try:
                obj.start(blocking=False)
                while True:
                    self.dataChanged.emit(obj.get_progress() * 100,
                                          str(obj.get_dl_size(human=True)),
                                          str(obj.get_speed(human=True)),
                                          str(obj.get_eta(human=True)))
                    time.sleep(0.2)
                    if self.state == DownloadThread.Paused:
                        obj.pause()
                    if self.state == DownloadThread.Resume:
                        obj.unpause()
                        self.state = DownloadThread.Started
                    if obj.isFinished():
                        break
                    if self.state == DownloadThread.Stopped:
                        obj.stop()
                        self.progressBar.setValue(0)
                        break
                if obj.isSuccessful():
                    # os.rename(obj.get_dest(), save_location)
                    shutil.move(obj.get_dest(), self.params["save_location"])
                    if self.state == DownloadThread.Started:
                        self.downloadFinished.emit()
            except:
                self.downloadError.emit()
    

    You should avoid using global variables in addition that you can reduce several variables that are all updated at once by only one variable so I had to modify the GUI code.

    class MainApp(QMainWindow, FORM_CLASS):
        def __init__(self, parent=None):
            # ...
            self.Handle_Buttons()
            self.download = DownloadThread(self)
            self.download.dataChanged.connect(self.onDataChanged)
            self.download.downloadError.connect(self.errorDownload)
            self.download.downloadFinished.connect(self.successfulDownload)
    
        def closeEvent(self, evnt):
            # ...
    
        def Handle_Ui(self):
            # self.lineEdit.setFocus()
            self.setFixedSize(861, 441)
    
        def Handle_Buttons(self):
            # ...
    
        def onDataChanged(self, progress, downloaded, speed, remain):
            self.progressBar.setValue(progress)
            self.label_8.setText("Downloaded: " + downloaded)
            self.label_38.setText("Speed: " + speed)
            self.label_39.setText("Remaining Time: " + remain)
    
        def Start(self):
            directory = os.path.expanduser("~") + "\AppData\Local\Temp\pySmartDL"
            if not os.path.exists(directory):
                os.makedirs(directory)
            params = {"url": self.lineEdit.text(),
                      "save_location": self.lineEdit_2.text()}
            self.download.setParams(params)
            self.download.setState(DownloadThread.Started)
            self.download.start()
    
        def Pause(self):
            if self.pushButton_13.text() == "Pause Downloading":
                self.download.setState(DownloadThread.Paused)
                self.pushButton_13.setText("Resume Downloading")
            elif self.pushButton_13.text() == "Resume Downloading":
                self.download.setState(DownloadThread.Resume)
                self.pushButton_13.setText("Pause Downloading")
    
        def Stop(self):
            self.download.setState(DownloadThread.Stopped)
            self.progressBar.setValue(0)
    
        def errorDownload(self):
            QMessageBox.warning(self, "Download Error", "Download Failed")
    
        def successfulDownload(self):
            QMessageBox.information(self, "Download Completed", "Your Download is Completed")