【问题标题】:Why does QtNetwork.QFtp.get download fail in a for-loop for multiple files?为什么 QtNetwork.QFtp.get 下载在多个文件的 for 循环中失败?
【发布时间】:2017-05-11 15:41:08
【问题描述】:

我正在尝试使用 for 循环从 ftp 站点下载多个文件。在 python.exe 关闭窗口弹出之前,以下代码似乎仅适用于循环中的前 2 个文件。两个下载的文件是完美的,但是第三个下载的文件在关机时是空的。我没有得到其余的文件。知道可能是什么问题吗?

from PyQt4 import QtCore, QtGui, QtNetwork


class FtpWindow(QtGui.QDialog):

    def __init__(self, parent=None):
        self.fileList = QtGui.QTreeWidget()
        self.ftp = QtNetwork.QFtp(self)
        self.progressDialog = QtGui.QProgressDialog(self)
        self.downloadAllButton.clicked.connect(self.downloadAllFile)
        self.ftp.commandFinished.connect(self.ftpCommandFinished)

    def downloadAllFile(self):
        for jj in range(self.fileList.topLevelItemCount()): # how many files in a particular folder
            fileName = self.fileList.topLevelItem(jj).text(0) 
            self.outFile = QtCore.QFile(fileName)

            self.ftp.get(fileName, self.outFile) #download one file at a time
            self.progressDialog.setLabelText("Downloading %s..." % fileName)    
            self.progressDialog.exec_()

    def ftpCommandFinished(self, _, error):
        self.setCursor(QtCore.Qt.ArrowCursor)
        if self.ftp.currentCommand() == QtNetwork.QFtp.Get:
            if error:
                self.statusLabel.setText("Canceled download of %s." % self.outFile.fileName())
                self.outFile.close()
                self.outFile.remove()
            else:
                self.statusLabel.setText("Downloaded %s to current directory." % self.outFile.fileName())
                self.outFile.close()

            self.outFile = None
            self.enableDownloadButton()
            self.progressDialog.hide()

【问题讨论】:

  • 我需要更多代码来说明。 self.progressDialog.exec_() 应该是一个阻塞模式对话框。看起来 ftp 获取是非阻塞的,因此您必须使用 commandFinished() 信号等到下载完成。覆盖 outFile 变量可能会导致问题。 pyside.github.io/docs/pyside/PySide/QtNetwork/…
  • @HashSplat,我使用:self.progressDialog = QtGui.QProgressDialog(self)。
  • @HashSplat 基本上,我尝试创建一个单击以下载所有基于this version的文件

标签: python progressdialog qtreewidget qftp


【解决方案1】:

self.progressDialog.exec_() 应该是一个阻塞模式对话框。使用self.progressDialog.show() 进行非阻塞调用。

看起来 ftp 获取是非阻塞的,所以您必须使用 commandFinished() 信号等待下载完成。

我的猜测是循环中的每次迭代都会覆盖 self.outFile,因此没有任何 python 引用该对象。每当python进行垃圾收集时,这都会使对象死亡。我的猜测是您的前两个文件小而快,而您的第三个文件较大,因此其他文件能够在垃圾收集之前下载。对于最后一个文件,无论是那个还是垃圾收集都更快。

http://pyside.github.io/docs/pyside/PySide/QtNetwork/QFtp.html#PySide.QtNetwork.PySide.QtNetwork.QFtp.get

class FtpWindow(QtGui.QDialog):

    def __init__(self, parent=None):
        self.fileList = QtGui.QTreeWidget()
        self.ftp = QtNetwork.QFtp(self)
        self.progressDialog = QtGui.QProgressDialog(self)
        self.progressDialog.canceled.connect(self.ftp.abort)
        self.downloadAllButton.clicked.connect(self.downloadAllFile)
        self.ref_holder = {}
        self.ftp.commandFinished.connect(self.ftpCommandFinished)

    def download_file(self, filename):
        """Non blocking start downloading a file."""
        outFile = QtCore.QFile(filename)
        cmd_id = self.ftp.get(filename, outFile) # Non blocking just start downloading

        # This keeps the object alive and doesn't overwrite them.
        self.ref_holder[cmd_id] = [filename, outFile]

    def downloadAllFile(self):
        self.progressDialog.reset()
        num_downloads = self.fileList.topLevelItemCount()
        self.progressDialog.setMaximum(num_downloads)
        self.progressDialog.setValue(0)
        self.progressDialog.setLabelText("Downloading %d files ..." % num_downloads)
        self.progressDialog.show()
        for jj in range(num_downloads): # how many files in a particular folder
            fileName = self.fileList.topLevelItem(jj).text(0) 
            self.download_file(fileName) # Non blocking, and doesn't overwrite self.outFile with every iteration

    def ftpCommandFinished(self, cmd_id, error=None):
        """Increased the number of items finished."""
        self.progressDialog.setValue(self.progressDialog.value()+1)
        item = self.ref_holder.pop(cmd_id) # Remove the reference for the finished item
        if error:
            self.progressDialog.setLabelText("Error downloading %s" % item[0])

        # Check if all downloads are done
        if len(self.ref_holder) == 0:
            self.progressDialog.setValue(self.progressDialog.maximium())
            self.progressDialog.close() # This shouldn't be needed

我上面的示例将保存文件名和 outFile 对象引用,直到命令完成。命令完成后,引用被删除,允许 python 清理对象。

【讨论】:

  • 非常感谢您的代码,它有效!但是,我发现即使下载已处理,这些文件也不会显示在我的本地目的地中。我必须添加“self.outFile.open(QtCore.QIODevice.WriteOnly)”来启用本地下载和保存。为什么?我在下面包含了我的版本
【解决方案2】:

感谢 HashSplat 的意见。我有一些更新以使其功能齐全:

class FtpWindow(QtGui.QDialog):

def __init__(self, parent=None):
    self.fileList = QtGui.QTreeWidget()
    self.ftp = QtNetwork.QFtp(self)
    self.progressDialog = QtGui.QProgressDialog(self)
    self.progressDialog.canceled.connect(self.ftp.abort)
    self.downloadAllButton.clicked.connect(self.downloadAllFile)
    self.ref_holder = {}
    self.ftp.commandFinished.connect(self.ftpCommandFinished)

def download_file(self, fileName):
    """Non blocking start downloading a file."""
    self.outFile = QtCore.QFile(fileName)

    """ Need this to have files saved locally """
    if not self.outFile.open(QtCore.QIODevice.WriteOnly):  
        QtGui.QMessageBox.information(self, "FTP",
                "Unable to save the file %s." % fileName)
        self.outFile = None
        return
    cmd_id = self.ftp.get(filename, self.outFile) # Non blocking just start downloading

    # This keeps the object alive and doesn't overwrite them.
    self.ref_holder[cmd_id] = [filename, self.outFile]

def downloadAllFile(self):
    self.progressDialog.reset()
    self.num_downloads = self.fileList.topLevelItemCount()
    self.counter=1
    self.progressDialog.setLabelText("Downloading %d/%d files ..." % (self.counter, self.num_downloads))
    self.progressDialog.show()
    for jj in range(num_downloads): # how many files in a particular folder
        fileName = self.fileList.topLevelItem(jj).text(0) 
        self.download_file(fileName) # Non blocking, and doesn't overwrite self.outFile with every iteration

def ftpCommandFinished(self, cmd_id, error=None):
    """Increased the number of items finished."""
    self.progressDialog.setValue(self.progressDialog.value()+1)
    item = self.ref_holder.pop(cmd_id) # Remove the reference for the finished item
    if error:
        self.progressDialog.setLabelText("Error downloading %s" % item[0])

    # Check if all downloads are done
    if len(self.ref_holder) == 0:
        self.progressDialog.close() # This closes the extra window
        self.outFile.close()   # You need this to have the last file saved
    else: 
        self.counter+=1
        self.progressDialog.setLabelText("Downloading %d/%d files ..." % (self.counter, self.num_downloads))

def updateDataTransferProgress(self, readBytes, totalBytes):
    self.progressDialog.setMaximum(totalBytes)
    self.progressDialog.setValue(readBytes)

【讨论】:

  • 在 ftpCommandFinished where self.outFile.close() # You need this to have the last file saved.您可能应该在 if 语句之前移动它并关闭每个 outFile。 item[1].close() # Close the outFileif len(self.ref_holder) == 0: ...
猜你喜欢
  • 2020-09-13
  • 2021-07-01
  • 2016-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多