2016-12-27 11 views
0

for-loop를 사용하여 ftp 사이트에서 여러 파일을 다운로드하려고합니다. 다음 코드는 python.exe 종료 창이 팝업되기 전에 루프의 처음 두 파일에서만 작동하는 것으로 보입니다. 두 개의 다운로드 파일은 완벽하지만 종료 할 때 다운로드 한 세 번째 파일은 비어 있습니다. 나는 나머지 파일들을 얻지 못한다. 어떤 아이디어가 문제 일 수 있습니까?여러 파일에 대한 for-loop에서 QtNetwork.QFtp.get 다운로드가 실패하는 이유는 무엇입니까?

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을 덮어 쓰고 있으므로 객체에 대한 파이썬 참조가 없습니다. 이것은 파이썬이 가비지 수집을 할 때마다 객체를 죽게 만든다. 제 생각에 처음 두 파일은 크기가 작고 빠르며 세 번째 파일은 더 크므로 다른 파일은 가비지 수집 전에 다운로드 할 수있었습니다. 마지막 파일에서는 가비지 콜렉션이 더 빠릅니다.

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)"을 추가해야합니다. 왜 그럴까요? 내 버전을 – 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

아래에 포함 시켰습니다. ftpCommandFinished에서'self.outFile.close() # 마지막 파일을 저장해야합니다 .' 아마 if 문 앞에 이것을 옮기고 모든 outFile을 닫아야합니다. 'item [1] .close() # outFile''if len (self.ref_holder) == 0 : ...'을 닫는다. – HashSplat