2016-12-27 3 views
0

for-loopを使用してftpサイトから複数のファイルをダウンロードしようとしています。次のコードは、python.exeのシャットダウンウィンドウがポップアップする前に、ループ内の最初の2つのファイルに対してのみ機能するようです。ダウンロードした2つのファイルは完璧ですが、シャットダウン時に3番目にダウンロードされたファイルは空です。私は残りのファイルを手に入れません。どのようなアイデアが問題になるのでしょうか?QtNetwork.QFtp.getのダウンロードが複数のファイルのfor-loopで失敗するのはなぜですか?

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() 
+0

私は伝えるためにもっとコードが必要です。 'self.progressDialog.exec _()'はブロックモーダルダイアログでなければなりません。 ftp getが非ブロッキングであるように見えるので、ダウンロードがcommandFinished()シグナルを使って終了するまで待たなければなりません。 outFile変数を上書きすると、問題が発生している可能性があります。 http://pyside.github.io/docs/pyside/PySide/QtNetwork/QFtp.html#PySide.QtNetwork.PySide.QtNetwork.QFtp.get – HashSplat

+0

@HashSplat、私は使用します:self.progressDialog = QtGui.QProgressDialog(self) 。 – Curiosity

+0

@HashSplat基本的に、私は[このバージョン](http://stackoverflow.com/questions/1995046/creating-an-ftp-client-with-python)に基づいてすべてのファイルをダウンロードするためのワンクリックを作成しようとしました – Curiosity

答えて

0

self.progressDialog.exec_()はブロックモーダルダイアログでなければなりません。非ブロッキング呼び出しの場合はself.progressDialog.show()を使用してください。

ftp getが非ブロックであるように見えるので、ダウンロードがcommandFinished()シグナルを使用して終了するまで待つ必要があります。

私の推測では、ループ内のすべての反復はself.outFileを上書きしているので、オブジェクトへのPython参照はありません。これは、Pythonがガベージコレクションを行うたびにオブジェクトを消滅させます。私の推測では、最初の2つのファイルは小さくて素早く、3番目のファイルは大きいので、他のファイルはガベージコレクションの前にダウンロードすることができました。最後のファイルのほうがガベージコレクションが速かったです。コマンドが終了するまで上記

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がオブジェクトをクリーンアップできるように参照が削除されます。

+0

ありがとうあなたのコードの多くは、それは動作します!しかし、ダウンロードが処理されても、ローカルの宛先にファイルが表示されないことがわかりました。ダウンロードと保存をローカルで有効にするには、「self.outFile.open(QtCore.QIODevice.WriteOnly)」を追加する必要があります。それはなぜです? ftpCommandFinishedの中で私のバージョンを – Curiosity

0

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) 
+0

以下に含めました。 'self.outFile.close()#最後に保存したファイルが必要です.'おそらくif文の前に移動し、すべてのoutFileを閉じるべきです。 'item [1] .close()#もしlen(self.ref_holder)== 0:...であれば、outFile''を閉じます。 – HashSplat

関連する問題