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

问题描述

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

我正在尝试使用pyqt5线程工作程序同时下载多个文件

我正在使用回调(由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,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,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_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_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_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],1)
                self.gridLayout_2.addWidget(self.listSizeLabel[m],2)
                self.gridLayout_2.addWidget(self.listDateLabel[m],3)
                self.listProgressBar.append(QtWidgets.QProgressBar())
                self.listProgressBar[m].setValue(0)
                self.gridLayout_2.addWidget(self.listProgressBar[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)
        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,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,1)
        self.submit = QtWidgets.QPushButton(self.verticalLayoutWidget)
        self.submit.setobjectName("submit")
        self.gridLayout.addWidget(self.submit,4,1)
        self.componentLabel = QtWidgets.QLabel(self.verticalLayoutWidget)
        self.componentLabel.setobjectName("componentLabel")
        self.gridLayout.addWidget(self.componentLabel,2,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,3,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,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,20))
        self.searchArchive.setobjectName("searchArchive")
        self.checkBox = QtWidgets.QCheckBox(self.groupBox_2)
        self.checkBox.setGeometry(QtCore.QRect(170,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,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)
        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,727,207))
        self.scrollAreaWidgetContents.setobjectName("scrollAreaWidgetContents")
        self.gridLayout_2 = QtWidgets.qgridLayout(self.scrollAreaWidgetContents)
        self.gridLayout_2.setContentsMargins(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(使用{{3}添加) }。

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,percentage):
        self.progressBars[(serverName,fileName)].setValue(percentage)