#!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2010 Riverbank Computing Limited. ## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ## All rights reserved. ## ## This file is part of the examples of PyQt. ## ## $QT_BEGIN_LICENSE:BSD$ ## You may use this file under the terms of the BSD license as follows: ## ## "Redistribution and use in source and binary forms, with or without ## modification, are permitted provided that the following conditions are ## met: ## * Redistributions of source code must retain the above copyright ## notice, this list of conditions and the following disclaimer. ## * Redistributions in binary form must reproduce the above copyright ## notice, this list of conditions and the following disclaimer in ## the documentation and/or other materials provided with the ## distribution. ## * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor ## the names of its contributors may be used to endorse or promote ## products derived from this software without specific prior written ## permission. ## ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ## $QT_END_LICENSE$ ## ############################################################################# # This is only needed for Python v2 but is harmless for Python v3. import sip sip.setapi('QString', 2) from PyQt4 import QtCore, QtGui, QtNetwork try: import ftp_rc3 except ImportError: import ftp_rc2 class FtpWindow(QtGui.QDialog): def __init__(self, parent=None): super(FtpWindow, self).__init__(parent) self.isDirectory = {} self.currentPath = '' self.ftp = None self.outFile = None ftpServerLabel = QtGui.QLabel("Ftp &server:") self.ftpServerLineEdit = QtGui.QLineEdit('ftp.trolltech.com') ftpServerLabel.setBuddy(self.ftpServerLineEdit) self.statusLabel = QtGui.QLabel("Please enter the name of an FTP server.") self.fileList = QtGui.QTreeWidget() self.fileList.setEnabled(False) self.fileList.setRootIsDecorated(False) self.fileList.setHeaderLabels(("Name", "Size", "Owner", "Group", "Time")) self.fileList.header().setStretchLastSection(False) self.connectButton = QtGui.QPushButton("Connect") self.connectButton.setDefault(True) self.cdToParentButton = QtGui.QPushButton() self.cdToParentButton.setIcon(QtGui.QIcon(':/images/cdtoparent.png')) self.cdToParentButton.setEnabled(False) self.downloadButton = QtGui.QPushButton("Download") self.downloadButton.setEnabled(False) self.quitButton = QtGui.QPushButton("Quit") buttonBox = QtGui.QDialogButtonBox() buttonBox.addButton(self.downloadButton, QtGui.QDialogButtonBox.ActionRole) buttonBox.addButton(self.quitButton, QtGui.QDialogButtonBox.RejectRole) self.progressDialog = QtGui.QProgressDialog(self) self.fileList.itemActivated.connect(self.processItem) self.fileList.currentItemChanged.connect(self.enableDownloadButton) self.progressDialog.canceled.connect(self.cancelDownload) self.connectButton.clicked.connect(self.connectOrDisconnect) self.cdToParentButton.clicked.connect(self.cdToParent) self.downloadButton.clicked.connect(self.downloadFile) self.quitButton.clicked.connect(self.close) topLayout = QtGui.QHBoxLayout() topLayout.addWidget(ftpServerLabel) topLayout.addWidget(self.ftpServerLineEdit) topLayout.addWidget(self.cdToParentButton) topLayout.addWidget(self.connectButton) mainLayout = QtGui.QVBoxLayout() mainLayout.addLayout(topLayout) mainLayout.addWidget(self.fileList) mainLayout.addWidget(self.statusLabel) mainLayout.addWidget(buttonBox) self.setLayout(mainLayout) self.setWindowTitle("FTP") def sizeHint(self): return QtCore.QSize(500, 300) def connectOrDisconnect(self): if self.ftp: self.ftp.abort() self.ftp.deleteLater() self.ftp = None self.fileList.setEnabled(False) self.cdToParentButton.setEnabled(False) self.downloadButton.setEnabled(False) self.connectButton.setEnabled(True) self.connectButton.setText("Connect") self.setCursor(QtCore.Qt.ArrowCursor) return self.setCursor(QtCore.Qt.WaitCursor) self.ftp = QtNetwork.QFtp(self) self.ftp.commandFinished.connect(self.ftpCommandFinished) self.ftp.listInfo.connect(self.addToList) self.ftp.dataTransferProgress.connect(self.updateDataTransferProgress) self.fileList.clear() self.currentPath = '' self.isDirectory.clear() url = QtCore.QUrl(self.ftpServerLineEdit.text()) if not url.isValid() or url.scheme().lower() != 'ftp': self.ftp.connectToHost(self.ftpServerLineEdit.text(), 21) self.ftp.login() else: self.ftp.connectToHost(url.host(), url.port(21)) user_name = url.userName() if user_name: try: # Python v3. user_name = bytes(user_name, encoding='latin1') except: # Python v2. pass self.ftp.login(QtCore.QUrl.fromPercentEncoding(user_name), url.password()) else: self.ftp.login() if url.path(): self.ftp.cd(url.path()) self.fileList.setEnabled(True) self.connectButton.setEnabled(False) self.connectButton.setText("Disconnect") self.statusLabel.setText("Connecting to FTP server %s..." % self.ftpServerLineEdit.text()) def downloadFile(self): fileName = self.fileList.currentItem().text(0) if QtCore.QFile.exists(fileName): QtGui.QMessageBox.information(self, "FTP", "There already exists a file called %s in the current " "directory." % fileName) return self.outFile = QtCore.QFile(fileName) if not self.outFile.open(QtCore.QIODevice.WriteOnly): QtGui.QMessageBox.information(self, "FTP", "Unable to save the file %s: %s." % (fileName, self.outFile.errorString())) self.outFile = None return self.ftp.get(self.fileList.currentItem().text(0), self.outFile) self.progressDialog.setLabelText("Downloading %s..." % fileName) self.downloadButton.setEnabled(False) self.progressDialog.exec_() def cancelDownload(self): self.ftp.abort() def ftpCommandFinished(self, _, error): self.setCursor(QtCore.Qt.ArrowCursor) if self.ftp.currentCommand() == QtNetwork.QFtp.ConnectToHost: if error: QtGui.QMessageBox.information(self, "FTP", "Unable to connect to the FTP server at %s. Please " "check that the host name is correct." % self.ftpServerLineEdit.text()) self.connectOrDisconnect() return self.statusLabel.setText("Logged onto %s." % self.ftpServerLineEdit.text()) self.fileList.setFocus() self.downloadButton.setDefault(True) self.connectButton.setEnabled(True) return if self.ftp.currentCommand() == QtNetwork.QFtp.Login: self.ftp.list() 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() elif self.ftp.currentCommand() == QtNetwork.QFtp.List: if not self.isDirectory: self.fileList.addTopLevelItem(QtGui.QTreeWidgetItem(["<empty>"])) self.fileList.setEnabled(False) def addToList(self, urlInfo): item = QtGui.QTreeWidgetItem() item.setText(0, urlInfo.name()) item.setText(1, str(urlInfo.size())) item.setText(2, urlInfo.owner()) item.setText(3, urlInfo.group()) item.setText(4, urlInfo.lastModified().toString('MMM dd yyyy')) if urlInfo.isDir(): icon = QtGui.QIcon(':/images/dir.png') else: icon = QtGui.QIcon(':/images/file.png') item.setIcon(0, icon) self.isDirectory[urlInfo.name()] = urlInfo.isDir() self.fileList.addTopLevelItem(item) if not self.fileList.currentItem(): self.fileList.setCurrentItem(self.fileList.topLevelItem(0)) self.fileList.setEnabled(True) def processItem(self, item): name = item.text(0) if self.isDirectory.get(name): self.fileList.clear() self.isDirectory.clear() self.currentPath += '/' + name self.ftp.cd(name) self.ftp.list() self.cdToParentButton.setEnabled(True) self.setCursor(QtCore.Qt.WaitCursor) def cdToParent(self): self.setCursor(QtCore.Qt.WaitCursor) self.fileList.clear() self.isDirectory.clear() dirs = self.currentPath.split('/') if len(dirs) > 1: self.currentPath = '' self.cdToParentButton.setEnabled(False) self.ftp.cd('/') else: self.currentPath = '/'.join(dirs[:-1]) self.ftp.cd(self.currentPath) self.ftp.list() def updateDataTransferProgress(self, readBytes, totalBytes): self.progressDialog.setMaximum(totalBytes) self.progressDialog.setValue(readBytes) def enableDownloadButton(self): current = self.fileList.currentItem() if current: currentFile = current.text(0) self.downloadButton.setEnabled(not self.isDirectory.get(currentFile)) else: self.downloadButton.setEnabled(False) if __name__ == '__main__': import sys app = QtGui.QApplication(sys.argv) ftpWin = FtpWindow() ftpWin.show() sys.exit(ftpWin.exec_())