python 中的多个文件下载和进度条更新

multiple file download and progress bar update in python

我正在尝试构建一个 GUI 日志文件下载器工具。

我正在尝试使用 pyqt5 thread worker 同时下载多个文件。

我正在使用回调(paramiko 支持)函数来确定下载百分比。我想同时更新特定下载的进度条。我无法弄清楚如何将 progressBar 详细信息传递给回调函数。 我想将“progressBar”传递给测试函数(它是一个回调函数)

from PyQt5 import QtCore, QtGui, QtWidgets
import threading
import time
from concurrent.futures import ThreadPoolExecutor
from configurationFileRead import readConfigFile
import paramiko
import os
from functools import partial


class FileDownload1(QtCore.QThread):
    percentage = QtCore.pyqtSignal(str, str)
    def __init__(self, fileList, serverList, listProgressBar):
        super().__init__()
        self.fileList = fileList
        self.serverList = serverList
        self.listProgressBar = listProgressBar
    def test(self, size, fileSize):
        sPercentage=((fileSize - size)/fileSize)*100
        if sPercentage != 100:
            sPercentage = 100 - sPercentage
        print(sPercentage)
        
    def _download(self, fileName, serverName, progressBar):
        userId = readConfigFile().getUserId(serverName)
        password = readConfigFile().getPassword(serverName)
        ipAddress = readConfigFile().getIpAddress(serverName)
        logPath = readConfigFile().getLogPath(serverName)
        ssh = paramiko.SSHClient()
        ssh.load_system_host_keys()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh.connect(ipAddress, username=userId, password=password)
        ftp_client=ssh.open_sftp()
        print(str(os.getcwd()))
        try:
            ftp_client.get(logPath+"/"+fileName,str(os.getcwd())+"/"+fileName, callback = lambda s, t : self.percetage.emit(s, t))
        except Excption as e:
            print(e)
        ftp_client.close()
        ssh.close()
    def run(self):
        j = 0
        with ThreadPoolExecutor(max_workers = 1) as executor:
            while(j < len(self.fileList)):
                if(self.fileList[j].isChecked()):
                    
                    future= executor.submit(self._download, self.fileList[j].text(), self.serverList[j].text(), self.listProgressBar[j])
                j = j+1
        print("Download END")


class logSearch(QtCore.QThread):
    new_signal = QtCore.pyqtSignal(dict)
    def __init__(self, serverList, serachString, archiveFlg, component):
        super().__init__()
        self.serverList = serverList
        self.searchString = serachString
        self.archiveFlg = archiveFlg
        self.component = component
        self.result = {}
    def fileSearch(self, server, searchText):
        try:

            userId = readConfigFile().getUserId(server.text())
            password = readConfigFile().getPassword(server.text())
            ipAddress = readConfigFile().getIpAddress(server.text())
            logPath = ""

            if(not self.archiveFlg):
                logPath = readConfigFile().getLogPath(server.text())
            else:
                logPath = readConfigFile().getLogArchivePath(server.text())
            ssh = paramiko.SSHClient()
            ssh.load_system_host_keys()
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            ssh.connect(ipAddress, username=userId, password=password)
            if(not self.archiveFlg):
                if self.component == "":
                    ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("cd "+logPath+";for i in *.log\ndo grep -li '"+self.searchString+"' $i\ndone")
                else:
                    ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("cd "+logPath+";for i in "+self.component+"_*.log\ndo grep -li '"+self.searchString+"' \"$i\"\ndone")
                    
            else:
                print(logPath)
                ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("cd "+logPath+";grep -lri '"+self.searchString+"' .")
            temp1 = ssh_stderr.read()
            print(temp1)
            temp = ssh_stdout.read().decode('utf-8')
            print(temp)
            temp =temp.strip()
            files = list(temp.split("\n"))
            files = list(filter(None,files))
            i = len(files)
            j=0
            tempDict = {}
            while(j < i):
                ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("cd "+logPath+";ls -l "+files[j])
                temp = ssh_stdout.read().decode('utf-8')
                tempLi = list(temp.split(" "))
                tempLi = list(filter(None,tempLi))
                fileSize = tempLi[4]
                tempFileSize  =  int(fileSize)
                if(tempFileSize < 1024):

                    fileSize = str(round(tempFileSize,2)) + " B"
                else:
                    tempFileSize = tempFileSize/1024
                    if(tempFileSize < 1024):
                        fileSize = str(round(tempFileSize,2)) + " KB"
                    else:
                        tempFileSize = tempFileSize/1024
                        if(tempFileSize < 1024):
                            fileSize = str(round(tempFileSize,2))+ " MB"
                        else:
                            tempFileSize = tempFileSize/1024
                            fileSize = str(round(tempFileSize,2)) + " GB"


                fileDate = str(tempLi[5])+" "+str(tempLi[6])+" "+str(tempLi[7])
                tempLi.clear()
                tempDict[files[j]] = {'Size':fileSize,'Date':fileDate}
                j = j+1
            self.result[server.text()]= tempDict
            ssh.close()

        except Exception as e:
            print(e)
    def run(self):
        i=len(self.serverList)
        mxW=0
        if(i == 1):
            mxW = 1
        elif(i == 2 or i == 3 or i == 4):
            mxW = 2
        elif(i == 5 or i == 6):
            mxW = 3
        else:
            mxW = 5
        with ThreadPoolExecutor(max_workers = mxW) as executor:
            j=0
            try:
                while(j < i):
                    future = executor.submit(self.fileSearch, self.serverList[j], self.searchString)
                    j = j+1
            except Exception as e:
                print(e)
        self.new_signal.emit(self.result)

class Ui_MainWindow(object):
    def _Validate (self):
        validateFlag = True
        if(str(self.searchString.text())== ""):
            self.searchString.setStyleSheet("border: 2px solid red;")
            self.searchString.setFocus()
            validateFlag = False
        else:
            self.searchString.setStyleSheet("border: 1px solid;")
        if(len(self.serverList.selectedItems())) == 0:
            self.serverList.setStyleSheet("border: 2px solid red;")
            validateFlag = False
        elif len(self.serverList.selectedItems()) > 8:
            self.selectedServer.setText("You can only Choose Upto 8 Server")
            self.selectedServer.setStyleSheet("color:blue;font:Bold")
            self.serverList.setStyleSheet("border: 2px solid red;")
            validateFlag = False
        elif len(self.serverList.selectedItems()) > 1 and self.specificFileFlag.isChecked() == True:
            self.selectedServer.setText("Select only one Server")
            self.selectedServer.setStyleSheet("color:red;font:Bold")
            validateFlag = False
        else:
            self.serverList.setStyleSheet("border: 1px solid;")
        if(validateFlag):
            self.downloadSelected.setDisabled(True)
            i=len(self.listProgressBar)
            n=0
            while(i > 0):
                self.listProgressBar[n].deleteLater()
                self.listCheckBox[n].deleteLater()
                self.listDateLabel[n].deleteLater()
                self.listSizeLabel[n].deleteLater()
                self.listServerLabel[n].deleteLater()
                i = i-1;
                n=n+1
            self.listProgressBar.clear()
            self.listCheckBox.clear()
            self.listDateLabel.clear()
            self.listSizeLabel.clear()
            self.listServerLabel.clear()
            self.submit.setDisabled(True)
            self.errorLabel.setText("Search Started...")
            self.logSearch = logSearch(self.serverList.selectedItems(), self.searchString.text(), self.searchArchive.isChecked(), self.comonentComboBox.currentText())
            self.logSearch.new_signal.connect(self.printResult)
            self.logSearch.start()
    def printResult(self, result):
        self.errorLabel.setText("Search Completed")
        serverList = list(result.keys())
        print(result)
        l = len(serverList)
        k=0
        m = 0
        i = 0
        while(i < l):
            logNameList = list(result[serverList[i]].keys())
            n= len(logNameList)
            j=0
            while(j < n):
                self.listCheckBox.append(QtWidgets.QCheckBox())
                self.listSizeLabel.append(QtWidgets.QLabel(result[serverList[i]][logNameList[j]]['Size']))
                self.listDateLabel.append(QtWidgets.QLabel(result[serverList[i]][logNameList[j]]['Date']))
                self.listCheckBox[m].setText(logNameList[j])
                self.gridLayout_2.addWidget(self.listCheckBox[m], m, 0)
                self.listServerLabel.append(QtWidgets.QLabel())
                self.listServerLabel[m].setText(serverList[i])
                self.gridLayout_2.addWidget(self.listServerLabel[m], m, 1)
                self.gridLayout_2.addWidget(self.listSizeLabel[m], m, 2)
                self.gridLayout_2.addWidget(self.listDateLabel[m], m, 3)
                self.listProgressBar.append(QtWidgets.QProgressBar())
                self.listProgressBar[m].setValue(0)
                self.gridLayout_2.addWidget(self.listProgressBar[m], m, 4)
                j = j+1
                m=m+1
            i = i+1
        self.submit.setDisabled(False)
        self.downloadSelected.setDisabled(False)
    def hello122(self):
        print("invoked")
    def _FileDownload(self):
        self.test = FileDownload1(self.listCheckBox, self.listServerLabel, self.listProgressBar)
        self.test.percentage.connect(self.hello122)
        self.test.start()
    def _ArchiveFlag(self):
        if (self.searchArchive.isChecked()):
            self.autoDownload.setChecked(True)
            self.autoDownload.setDisabled(True)
        else:
            self.autoDownload.setDisabled(False)
    def _FileDownloadFlag(self):
        if(self.specificFileFlag.isChecked()):
            self.label.setText("<html><head/><body><p><span style=\" font-weight:600; color:#000000;\">File Name</span><span style=\" font-weight:600; color:#ff0000;\">*</span></p></body></html>")
            self.submit.setText("Download File")
            self.autoDownload.setChecked(False)
            self.autoDownload.setDisabled(True)
            self.searchArchive.setDisabled(True)
            self.comonentComboBox.hide()
            self.downloadSelected.setDisabled(True)
        else:
             self.label.setText("<html><head/><body><p><span style=\" font-weight:600; color:#000000;\">Search String</span><span style=\" font-weight:600; color:#ff0000;\">*</span></p></body></html>")
             self.submit.setText("Search")
             self.comonentComboBox.show()
             self.autoDownload.setDisabled(False)
             self.searchArchive.setDisabled(False)
    def _serverList(self):
            if len(self.serverList.selectedItems()) > 8 :
                self.selectedServer.setText("You can only Choose Upto 8 Server")
                self.selectedServer.setStyleSheet("color:red;font:Bold")
            elif len(self.serverList.selectedItems()) > 1 and self.specificFileFlag.isChecked() == True:
                self.selectedServer.setText("Select only one Server")
                self.selectedServer.setStyleSheet("color:red;font:Bold")
            elif len(self.serverList.selectedItems()) == 0:
                self.selectedServer.setText("")
            else:
                self.selectedServer.setText(str([item.text() for item in self.serverList.selectedItems()]))
                self.selectedServer.setStyleSheet("color:black;font:regular")
                self.serverList.setStyleSheet("border: 1px solid;")


    def setupUi(self,MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(823, 588)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth())
        MainWindow.setSizePolicy(sizePolicy)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
        self.verticalLayoutWidget.setGeometry(QtCore.QRect(20, 40, 761, 60))
        self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout.setObjectName("verticalLayout")
        self.gridLayout = QtWidgets.QGridLayout()
        self.gridLayout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
        self.gridLayout.setObjectName("gridLayout")
        self.label = QtWidgets.QLabel(self.verticalLayoutWidget)
        self.label.setObjectName("label")
        self.gridLayout.addWidget(self.label, 0, 0, 1, 1, QtCore.Qt.AlignLeft)
        self.searchString = QtWidgets.QLineEdit(self.verticalLayoutWidget)
        self.searchString.setMaximumSize(QtCore.QSize(673, 16777215))
        self.searchString.setObjectName("searchString")
        self.gridLayout.addWidget(self.searchString, 0, 1, 1, 1)
        self.submit = QtWidgets.QPushButton(self.verticalLayoutWidget)
        self.submit.setObjectName("submit")
        self.gridLayout.addWidget(self.submit, 0, 4, 1, 1)
        self.componentLabel = QtWidgets.QLabel(self.verticalLayoutWidget)
        self.componentLabel.setObjectName("componentLabel")
        self.gridLayout.addWidget(self.componentLabel, 0, 2, 1, 1)
        self.comonentComboBox = QtWidgets.QComboBox(self.verticalLayoutWidget)
        self.comonentComboBox.setObjectName("comonentComboBox")
        self.comonentComboBox.addItem("")
        lengthOfComponent = len(readConfigFile().getComponentList())
        m=0
        while(m < lengthOfComponent):
            self.comonentComboBox.addItem(readConfigFile().getComponentList()[m])
            m = m+1
        self.gridLayout.addWidget(self.comonentComboBox, 0, 3, 1, 1)
        self.verticalLayout.addLayout(self.gridLayout)
        self.errorLabel = QtWidgets.QLabel(self.centralwidget)
        self.errorLabel.setGeometry(QtCore.QRect(30, 10, 751, 16))
        self.errorLabel.setText("")
        self.errorLabel.setObjectName("errorLabel")
        self.groupBox = QtWidgets.QGroupBox(self.centralwidget)
        self.groupBox.setGeometry(QtCore.QRect(40, 140, 361, 151))
        self.groupBox.setObjectName("groupBox")
        self.serverList = QtWidgets.QListWidget(self.groupBox)
        self.serverList.setEnabled(True)
        self.serverList.setGeometry(QtCore.QRect(10, 20, 331, 121))
        self.serverList.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
        self.serverList.setResizeMode(QtWidgets.QListView.Fixed)
        self.serverList.setObjectName("serverList")
        item = QtWidgets.QListWidgetItem()
        lengthOfServers = len(readConfigFile().getServerList())
        k=0
        while( k < lengthOfServers):
            if readConfigFile().getServerList()[k] != "Component":
                self.serverList.addItem(readConfigFile().getServerList()[k])
            k = k+1
        self.groupBox_2 = QtWidgets.QGroupBox(self.centralwidget)
        self.groupBox_2.setGeometry(QtCore.QRect(410, 140, 361, 151))
        self.groupBox_2.setObjectName("groupBox_2")
        self.autoDownload = QtWidgets.QCheckBox(self.groupBox_2)
        self.autoDownload.setGeometry(QtCore.QRect(10, 30, 121, 20))
        self.autoDownload.setObjectName("autoDownload")
        self.specificFileFlag = QtWidgets.QCheckBox(self.groupBox_2)
        self.specificFileFlag.setGeometry(QtCore.QRect(10, 60, 171, 20))
        self.specificFileFlag.setObjectName("specificFileFlag")
        self.downloadSelected = QtWidgets.QPushButton(self.groupBox_2)
        self.downloadSelected.setGeometry(QtCore.QRect(10, 97, 141, 31))
        self.downloadSelected.setObjectName("downloadSelected")
        self.searchArchive = QtWidgets.QCheckBox(self.groupBox_2)
        self.searchArchive.setGeometry(QtCore.QRect(170, 30, 141, 20))
        self.searchArchive.setObjectName("searchArchive")
        self.checkBox = QtWidgets.QCheckBox(self.groupBox_2)
        self.checkBox.setGeometry(QtCore.QRect(170, 60, 191, 21))
        self.checkBox.setObjectName("checkBox")
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(640, 540, 161, 20))
        self.label_2.setObjectName("label_2")
        self.selectedServer = QtWidgets.QLabel(self.centralwidget)
        self.selectedServer.setGeometry(QtCore.QRect(30, 110, 751, 21))
        self.selectedServer.setText("")
        self.selectedServer.setObjectName("selectedServer")
        self.serverList.itemSelectionChanged.connect(self._serverList)
        self.submit.clicked.connect(self._Validate)
        self.horizontalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
        self.horizontalLayoutWidget.setGeometry(QtCore.QRect(40, 310, 731, 211))
        self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
        self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.scrollArea = QtWidgets.QScrollArea(self.horizontalLayoutWidget)
        self.scrollArea.setWidgetResizable(True)
        self.scrollArea.setObjectName("scrollArea")
        self.scrollAreaWidgetContents = QtWidgets.QWidget()
        self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 727, 207))
        self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
        self.gridLayout_2 = QtWidgets.QGridLayout(self.scrollAreaWidgetContents)
        self.gridLayout_2.setContentsMargins(10, 10, 10, 10)
        self.gridLayout_2.setObjectName("gridLayout_2")
        self.scrollArea.setWidget(self.scrollAreaWidgetContents)
        self.horizontalLayout.addWidget(self.scrollArea)
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.listCheckBox = []
        self.listProgressBar = []
        self.listServerLabel = []
        self.listSizeLabel = []
        self.listDateLabel = []
        self.searchArchive.toggled.connect(self._ArchiveFlag)
        self.specificFileFlag.toggled.connect(self._FileDownloadFlag)
        self.downloadSelected.clicked.connect(self._FileDownload)
        self.downloadSelected.setDisabled(True)
        self.checkBox.setDisabled(True)
        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.label.setText(_translate("MainWindow", "<html><head/><body><p><span style=\" font-weight:600; color:#000000;\">Search String</span><span style=\" font-weight:600; color:#ff0000;\">*</span></p></body></html>"))
        self.submit.setText(_translate("MainWindow", "Search"))
        self.groupBox.setTitle(_translate("MainWindow", "Server List"))
        __sortingEnabled = self.serverList.isSortingEnabled()
        self.serverList.setSortingEnabled(False)
        self.serverList.setSortingEnabled(__sortingEnabled)
        self.groupBox_2.setTitle(_translate("MainWindow", "Other Configuration"))
        self.autoDownload.setToolTip(_translate("MainWindow", "<html><head/><body><p><br/></p></body></html>"))
        self.autoDownload.setText(_translate("MainWindow", "Auto Download"))
        self.specificFileFlag.setToolTip(_translate("MainWindow", "<html><head/><body><p>write the file name in &quot;Search String&quot; and Click on Search Button</p></body></html>"))
        self.specificFileFlag.setText(_translate("MainWindow", "Download Specific file"))
        self.downloadSelected.setText(_translate("MainWindow", "Download Selected"))
        self.searchArchive.setText(_translate("MainWindow", "Search in Archive"))
        self.checkBox.setToolTip(_translate("MainWindow", "<html><head/><body><p>This option will try to compress the file in server before downloading</p></body></html>"))
        self.checkBox.setText(_translate("MainWindow", "Compress Before Download"))
        self.label_2.setText(_translate("MainWindow", "<html><head/><body><p><span style=\" color:#969696;\">Created by - Sudipto Khan</span></p></body></html>"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

你不能在Qt主线程之外访问GUI元素,所以你必须将地址列表发送到QThread并使用自定义信号来通知进程,然后在主线程中你可以使用地址作为参考每个地址栏。

我假设您可能正在使用 QTableWidget,因此以下示例使用第一列作为服务器名称,第二列作为文件名,第三列显示 QProgressBar(使用 setCellWidget()).

FileDownload1(QtCore.QThread):
    percentage = QtCore.pyqtSignal(str, str, int)
    def __init__(self, urlData):
        super().__init__()
        self.urlData = urlData

    def _download(self, fileName, serverName):
        # ...
        try:
            ftp_client.get(
                logPath+"/"+fileName, 
                str(os.getcwd())+"/"+fileName, 
                callback = lambda s, t: self.percentage.emit(server, path, int(s / t * 100)))
        # ...

    def run(self):
        j = 0
        with ThreadPoolExecutor(max_workers = 1) as executor:
            for serverName, fileName in self.urlData:
                future = executor.submit(self._download, serverName, fileName)

class MainWindow(QtWidgets.QMainWindow):
    # ...
    def startDownload(self):
        urlData = []
        self.progressBars = {}
        for row in range(self.fileTable.rowCount()):
            serverName = self.fileTable.item(row, 0).text()
            fileName = self.fileTable.item(row, 1).text()
            urlData.append((serverName, fileName))
            progressBar = self.fileTable.cellWidget(row, 2)
            self.progressBars[(serverName, fileName)] = progressBar
        self.downloader = FileDownload1(urlData)
        self.downloader.percentage.connect(self.percentageUpdate)
        self.downloader.start()

    def percentageUpdate(self, serverName, fileName, percentage):
        self.progressBars[(serverName, fileName)].setValue(percentage)