Showing posts with label PyQt. Show all posts
Showing posts with label PyQt. Show all posts

Thursday, May 28, 2009

[Python & PyQt] Python Loader

Download: PyQtLoader.zip
Introduction

It's wired that Python don't have the feature that can compile python code as binary file. Thus, I plan to write a simple program as python loader generator.

The technique of this program is quite low level, I just create a "c language" based binary file to execute the target python script.

Below are the core python script for this project
#! /usr/bin/python

######################################################################
#
# Project: PyQtLoader
# Version: 1.0
# Date: Thu May 28 00:00:00 2009
# Author: Xin-Yu Lin
# E-mail: nxforce@yahoo.com
# WebSite: http://nxforce.blogspot.com
# Description: Python Code Loader
#
######################################################################

############### import modules #######################################

import os
import py_compile
import Ui_PyQtLoaderClass
from PyQt4 import QtCore, QtGui

############### PyQtLoaderClass Start ################################

class PyQtLoaderClass(QtGui.QWidget):
    def __init__(self, parent = None):
        QtGui.QWidget.__init__(self, parent)
        self.ui = Ui_PyQtLoaderClass.Ui_PyQtLoaderClass()
        self.ui.setupUi(self)
        self.filePath = ''
        
    def onGenerateClicked(self):
        if(self.filePath == ''):
            self.onExportClicked()
        else:
            #py_compile.compile(self.filePath)
            
            splitext = os.path.splitext(self.filePath)
            srcFileName = splitext[0]+'.c'
            file = open(srcFileName, 'w')
            file.write("#include <stdio.h>\nint main(void){ system(\"python ./" + os.path.basename(self.filePath) + "\");return 0;}\n")
            file.close()
            os.system("gcc -o "+splitext[0]+" "+srcFileName)
            os.remove(srcFileName)
            
            QtGui.QMessageBox.information(self, 'Message', " Done! ", QtGui.QMessageBox.Ok)
            
    def onExportClicked(self):
        filePath = self.ui.fileDialog_export.getOpenFileName(self, 'Open File', QtCore.QDir.homePath()+'/Desktop', "python(*.py)")
        self.ui.lineEdit_path.setText(filePath)
        self.filePath = str(filePath)
        
        
############### PyQtLoaderClass End ##################################

As you can see, all coding, compiling processes can be done in runtime. After the processing completed, you will see a binary file which has the same filename with your python script.

Screenshot

The usage is very very simple, just click the left-hand side's button to load your python script, and than click the 'Generate' to create the binary loader.

#For each loader, it will takes about 60 KBs of memory.

Wednesday, May 27, 2009

[Python & PyQt] PyQt Socket

Introduction:
Practice python, UI in PyQt and improve "[python] Python Socket Tutorial"

Development Information:
  • Python 2.6
  • PyQt 4.4.4
  • Mac OS X 10.5.6
Code:
uiMessenger.py
#!/usr/local/bin/python

##################################################################
# Project: Python Messenger
# Filename: uiMessenger.py
# Description: User interface for messenger
# Date: 2009.05.27
# Programmer: Mantis
# E-mail: kill12688@yahoo.com.tw
# Website:http://importcode.blogspot.com
##################################################################

from PyQt4 import QtCore, QtGui

class Ui_MessengerClass(object):
def setupUi(self, MessengerClass):
MessengerClass.setObjectName("MessengerClass")
MessengerClass.resize(400, 600)

#add IP label, lineEdit
self.label_ip = QtGui.QLabel(MessengerClass)
self.label_ip.setGeometry(QtCore.QRect(10, 10, 25, 25))
self.label_ip.setObjectName("label_ip")

self.lineEdit_ip = QtGui.QLineEdit(MessengerClass)
self.lineEdit_ip.setGeometry(QtCore.QRect(45, 10, 155, 25))

#add Port label, lineEdit
self.label_port = QtGui.QLabel(MessengerClass)
self.label_port.setGeometry(QtCore.QRect(210, 10, 30, 25))
self.label_port.setObjectName("label_port")

self.lineEdit_port = QtGui.QLineEdit(MessengerClass)
self.lineEdit_port.setGeometry(QtCore.QRect(250, 10, 50, 25))

#add connect button
self.pushButton_connect = QtGui.QPushButton(MessengerClass)
self.pushButton_connect.setGeometry(QtCore.QRect(310, 10, 80, 25))
self.pushButton_connect.setObjectName("pushButton_connect")

#add display textEdit
self.textEdit_display = QtGui.QTextEdit(MessengerClass)
self.textEdit_display.setGeometry(QtCore.QRect(10, 45, 380, 475))

#add Nick Name label, lineEdit
self.label_nickname = QtGui.QLabel(MessengerClass)
self.label_nickname.setGeometry(QtCore.QRect(10, 530, 70, 27))
self.label_nickname.setObjectName("label_nickname")

self.lineEdit_nickname = QtGui.QLineEdit(MessengerClass)
self.lineEdit_nickname.setGeometry(QtCore.QRect(90, 530, 300, 27))

#add send lineEdit, button
self.lineEdit_send = QtGui.QLineEdit(MessengerClass)
self.lineEdit_send.setGeometry(QtCore.QRect(10, 565, 295, 25))

self.pushButton_send = QtGui.QPushButton(MessengerClass)
self.pushButton_send.setGeometry(QtCore.QRect(315, 565, 75, 25))
self.pushButton_send.setObjectName("pushButton_send")

self.retranslateUi(MessengerClass)

QtCore.QObject.connect(self.pushButton_connect,QtCore.SIGNAL("clicked()"),MessengerClass.onConnectClicked)
QtCore.QObject.connect(self.pushButton_send,QtCore.SIGNAL("clicked()"),MessengerClass.onSendClicked)
QtCore.QMetaObject.connectSlotsByName(MessengerClass)


def retranslateUi(self, MessengerClass):
MessengerClass.setWindowTitle(QtGui.QApplication.translate("MessengerClass", "ChatRoom", None, QtGui.QApplication.UnicodeUTF8))
self.label_ip.setText(QtGui.QApplication.translate("label_ip", "IP:", None, QtGui.QApplication.UnicodeUTF8))
self.label_port.setText(QtGui.QApplication.translate("label_port", "port:", None, QtGui.QApplication.UnicodeUTF8))
self.pushButton_connect.setText(QtGui.QApplication.translate("pushButton_connect", "Connect", None, QtGui.QApplication.UnicodeUTF8))
self.label_nickname.setText(QtGui.QApplication.translate("label_nickname", "Nick Name", None, QtGui.QApplication.UnicodeUTF8))
self.pushButton_send.setText(QtGui.QApplication.translate("pushButton_send", "Send", None, QtGui.QApplication.UnicodeUTF8))

server.py
t#!/opt/local/bin/python

##################################################################
# Project: Python Messenger
# Filename: server.py
# Description: server for messengers
# Date: 2009.05.27
# Programmer: Mantis
# E-mail: kill12688@yahoo.com.tw
# Website:http://importcode.blogspot.com
##################################################################

import sys
import socket
import threading
import uiMessenger
from PyQt4 import QtCore, QtGui

class serverThread(threading.Thread):
def __init__(self, connection, address, ui, serverMessenger):
self.connection = connection
self.address = address
self.ui =ui
self.serverMessenger = serverMessenger
self.onConnect = True
threading.Thread.__init__(self)
def run(self):
while self.onConnect:
recvData = self.connection.recv(1024)
if recvData != ":exit" and recvData != '':
self.ui.textEdit_display.append(recvData)
else:
break
self.connection.close()
self.ui.textEdit_display.append('Disconnect by ' + str(self.address))
self.serverMessenger.getConnect()

def close(self):
self.onConnect = False

class serverMessenger(QtGui.QMainWindow):
def __init__(self, parent = None):
QtGui.QMainWindow.__init__(self, parent)
self.ui = uiMessenger.Ui_MessengerClass()
self.ui.setupUi(self)
self.ui.lineEdit_ip.setText(str('127.0.0.1'))
self.ui.lineEdit_port.setText(str(8001))

def onConnectClicked(self):
host = str(self.ui.lineEdit_ip.text())
port = int(self.ui.lineEdit_port.text())
self.host = host
self.port = port
self.serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.serverSocket.bind((self.host, self.port))
self.serverSocket.listen(5)
self.getConnect()

def getConnect(self):
self.ui.textEdit_display.append('Waiting...')
self.connection, self.address = self.serverSocket.accept()
self.ui.textEdit_display.append('Enter :exit to disconnect')
self.ui.textEdit_display.append('Connect by ' + str(self.address))
thread = serverThread(self.connection, self.address, self.ui, self)
thread.start()

def onSendClicked(self):
sendData_temp = str(self.ui.lineEdit_send.text())
serverName = str(self.ui.lineEdit_nickname.text())
if serverName == '':
serverName = 'Server'
if sendData_temp == ':exit':
sys.exit(app.exec_())
sendData = str(serverName) + ': ' + sendData_temp
self.ui.textEdit_display.append(sendData)
self.connection.send(sendData)
self.ui.lineEdit_send.clear()

if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
serverMessenger = serverMessenger()
serverMessenger.show()
sys.exit(app.exec_())

client.py
#!/opt/local/bin/python

##################################################################
# Project: Python Messenger
# Filename: client.py
# Description: client for messenger
# Date: 2009.05.27
# Programmer: Mantis
# E-mail: kill12688@yahoo.com.tw
# Website:http://importcode.blogspot.com
##################################################################

import sys
import socket
import threading
import uiMessenger
from PyQt4 import QtCore, QtGui

class clientThread(threading.Thread):
def __init__(self, clientSocket, address, ui):
self.clientSocket = clientSocket
self.address = address
self.ui = ui
self.onConnect = True
threading.Thread.__init__(self)
def run(self):
while self.onConnect:
recvData = self.clientSocket.recv(1024)
if recvData != ":exit" and recvData != '':
self.ui.textEdit_display.append(recvData)
else:
break
self.clientSocket.close()
self.ui.textEdit_display.append('Disconnect by ' + str(self.address))
def close(self):
self.onConnect = False

class clientMessenger(QtGui.QMainWindow):
def __init__(self, parent = None):
QtGui.QMainWindow.__init__(self, parent)
self.ui = uiMessenger.Ui_MessengerClass()
self.ui.setupUi(self)
self.ui.lineEdit_ip.setText(str('127.0.0.1'))
self.ui.lineEdit_port.setText(str(8001))

def onConnectClicked(self):
host = str(self.ui.lineEdit_ip.text())
port = int(self.ui.lineEdit_port.text())
self.host = host
self.port = port
self.clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.clientSocket.connect((self.host, self.port))

self.address = (self.host, 8001)
self.ui.textEdit_display.append('Enter :exit to disconnect')
self.ui.textEdit_display.append('Connect to ' + str(self.address))
thread = clientThread(self.clientSocket, self.address, self.ui)
thread.start()

def onSendClicked(self):
sendData_temp = str(self.ui.lineEdit_send.text())
clientName = str(self.ui.lineEdit_nickname.text())
if clientName == '':
clientName = 'Client'
if sendData_temp == ':exit':
sys.exit(app.exec_())
sendData = str(clientName) + ': ' + sendData_temp
self.ui.textEdit_display.append(sendData)
self.clientSocket.send(sendData)
self.ui.lineEdit_send.clear()

if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
clientMessenger = clientMessenger()
clientMessenger.show()
sys.exit(app.exec_())

Screenshot:


Tuesday, May 26, 2009

[Python & PyQt] PyQt Code Generator

Download: PyQtCodeGenerator.zip

Introduction

If you are a python programmer, you might like to hear the news. Today I would like to show you an awesome tool which was created by me. ''PyQt Code Generator''.
To make the GUI coding more efficient, I write a program to help programmers to generate the basic PyQt code quickly, thus programmers don't need to waste their time on such kind of routine jobs. They can devoted their time on core algorithm design.

This tool is quite simple and easy to use, just follow few steps, and you will see a basic GUI program.
  • Open the PyQt Code Generator program
  • Enter the project name
  • Choose the UI type
  • Check the modules
  • Add the comment if needed
  • Specify the export location and generate it
Below are the detailed development information for this project:
  • Project: PyQt Code Generator
  • Version 1.0
  • Python:2.6
  • PyQt: 4.4.4
  • Ubuntu 9.04/WinXP sp3
Features

1. To separate 'Main Code', 'UI Code' and 'Main Class Code' into three individual files

2. Supporting QWidget, QMainWindow and customize ui file (which be created by Qt-Designer)

3. Pre-defined Modules

4. Comment section

5. Remember last time setting

Screenshopt


Notes

Due to the source code are too large to post on the blog, so please download it if you are interesting in this project.

Any suggestions would be appreciated!!!

Sunday, May 24, 2009

[Python & PyQt] Code Formatter

Download: CodeFormatter.zip

Introduction
In order to solve the layout problem of google code prettify in blogspot, I try to write a simple but useful tool for source code formating. So far, this program is only fully support python. For other programming languages, you can still get a little benefit from this tool.

This program will replace tab or white space with fixed size tab character. Besides, the special character '<' and '>' will be replaced by html code.

Code
UI
#! /usr/bin/python

##################################################################
# Project: Python Code Formatter
# Filename: ui_codeformatter.py
# Description: User interface for source code formatting
# Date: 2009.05.24
# Programmer: Lin Xin Yu
# E-mail: nxforce@yahoo.com
# Website:http://importcode.blogspot.com
##################################################################

#================= import modules ================================

from PyQt4 import QtCore, QtGui

#================= Ui_CodeFormatterClass Start ===================

class Ui_CodeFormatterClass(object):
    def setupUi(self, CodeFormatterClass):
        CodeFormatterClass.setObjectName("CodeFormatterClass")
        CodeFormatterClass.resize(800, 630)
        
        # plain text editor
        self.textEdit_raw = QtGui.QTextEdit(CodeFormatterClass)
        self.textEdit_raw.setGeometry(QtCore.QRect(10, 10, 780, 285))
        self.textEdit_raw.setObjectName("textEdit_raw")
        self.textEdit_raw.setTabStopWidth(20)
        
        # formatted text editor
        self.textEdit_fmt = QtGui.QTextEdit(CodeFormatterClass)
        self.textEdit_fmt.setGeometry(QtCore.QRect(10, 305, 780, 285))
        self.textEdit_fmt.setObjectName("textEdit_raw")
        self.textEdit_fmt.setTabStopWidth(20)
        
        # language type
        self.comboBox_langMode = QtGui.QComboBox(CodeFormatterClass)
        self.comboBox_langMode.setGeometry(QtCore.QRect(10, 600, 120, 25))
        self.comboBox_langMode.setObjectName("comboBox_langMode")
        self.comboBox_langMode.insertItem(0, 'Python')
        self.comboBox_langMode.insertItem(1, 'Other')
        
        # Tab Width label
        self.label_tabWidth = QtGui.QLabel(CodeFormatterClass)
        self.label_tabWidth.setGeometry(QtCore.QRect(140, 600, 70, 25))
        self.label_tabWidth.setObjectName("label_tabWidt")
        
        # Tab Width lineEdit
        self.lineEdit_tabWidth = QtGui.QLineEdit(CodeFormatterClass)
        self.lineEdit_tabWidth.setGeometry(QtCore.QRect(210, 600, 30, 25))
        self.lineEdit_tabWidth.setObjectName("lineEdit_tabWidth")
        self.lineEdit_tabWidth.setText('4')
        
        # 'Convert' button
        self.pushButton_convert = QtGui.QPushButton(CodeFormatterClass)
        self.pushButton_convert.setGeometry(QtCore.QRect(250, 600, 90, 25))
        self.pushButton_convert.setObjectName("pushButton_convert")
        
        # 'clear all' button
        self.pushButton_clear = QtGui.QPushButton(CodeFormatterClass)
        self.pushButton_clear.setGeometry(QtCore.QRect(350, 600, 90, 25))
        self.pushButton_clear.setObjectName("pushButton_clear")
        
        # 'Open' button
        self.pushButton_open = QtGui.QPushButton(CodeFormatterClass)
        self.pushButton_open.setGeometry(QtCore.QRect(450, 600, 90, 25))
        self.pushButton_open.setObjectName("pushButton_open")
        
        # 'SaveAs' button
        self.pushButton_saveAs = QtGui.QPushButton(CodeFormatterClass)
        self.pushButton_saveAs.setGeometry(QtCore.QRect(550, 600, 90, 25))
        self.pushButton_saveAs.setObjectName("pushButton_saveAs")
        
        # 'Copy to clipboard' button
        self.pushButton_clipboard = QtGui.QPushButton(CodeFormatterClass)
        self.pushButton_clipboard.setGeometry(QtCore.QRect(650, 600, 140, 25))
        self.pushButton_clipboard.setObjectName("pushButton_clipboard")
        
        # Open fileDialog
        self.fileDialog_open = QtGui.QFileDialog(CodeFormatterClass)
        
        # SaveAs fileDialog
        self.fileDialog_saveAs = QtGui.QFileDialog(CodeFormatterClass)
        
        self.retranslateUi(CodeFormatterClass)
        QtCore.QObject.connect(self.pushButton_convert, QtCore.SIGNAL("clicked()"), CodeFormatterClass.onConvertClicked)
        QtCore.QObject.connect(self.pushButton_clear, QtCore.SIGNAL("clicked()"), CodeFormatterClass.onClearAllClicked)
        QtCore.QObject.connect(self.pushButton_open,QtCore.SIGNAL("clicked()"),CodeFormatterClass.onOpenClicked)
        QtCore.QObject.connect(self.pushButton_saveAs, QtCore.SIGNAL("clicked()"), CodeFormatterClass.onSaveAsClicked)
        QtCore.QObject.connect(self.pushButton_clipboard, QtCore.SIGNAL("clicked()"), CodeFormatterClass.onCpClipboardClicked)
        QtCore.QObject.connect(self.lineEdit_tabWidth,QtCore.SIGNAL("returnPressed()"),CodeFormatterClass.onTabWidthSeted)
        QtCore.QMetaObject.connectSlotsByName(CodeFormatterClass)
        
    def retranslateUi(self, CodeFormatterClass):
        CodeFormatterClass.setWindowTitle(QtGui.QApplication.translate("CodeFormatterClass", "Code Formatter", None, QtGui.QApplication.UnicodeUTF8))
        self.pushButton_convert.setText(QtGui.QApplication.translate("CodeFormatterClass", "Convert", None, QtGui.QApplication.UnicodeUTF8))
        self.pushButton_clipboard.setText(QtGui.QApplication.translate("CodeFormatterClass", "Copy to Clipboard", None, QtGui.QApplication.UnicodeUTF8))
        self.pushButton_saveAs.setText(QtGui.QApplication.translate("CodeFormatterClass", "Save As...", None, QtGui.QApplication.UnicodeUTF8))
        self.pushButton_clear.setText(QtGui.QApplication.translate("CodeFormatterClass", "Clear All", None, QtGui.QApplication.UnicodeUTF8))
        self.label_tabWidth.setText(QtGui.QApplication.translate("CodeFormatterClass", "Tab width:", None, QtGui.QApplication.UnicodeUTF8))
        self.pushButton_open.setText(QtGui.QApplication.translate("CodeFormatterClass", "Open...", None, QtGui.QApplication.UnicodeUTF8))
        
#================= Ui_CodeFormatterClass Start ===================

Main Program
#! /usr/bin/python

################################################################
# Project: Python Code Formatter
# Filename: CodeFormatter.py
# Description: An useful tool for source code formatting
# Date: 2009.05.24
# Programmer: Lin Xin Yu
# E-mail: nxforce@yahoo.com
# Website:http://importcode.blogspot.com
################################################################

#================= import modules ==============================
import re
import sys
import ui_codeformatter
from PyQt4 import QtCore, QtGui

#================= CodeFormatter Class Start ===================

class CodeFormatter(QtGui.QMainWindow):
    def __init__(self, parent = None):
        QtGui.QMainWindow.__init__(self, parent)
        self.ui = ui_codeformatter.Ui_CodeFormatterClass()
        self.ui.setupUi(self)
        
        self.ui.textEdit_fmt.setEnabled(False)
        
        # default tab width is 4 for all language
        self.tabWidth = 4
        
        # init raw text
        self.rawText = ''
        
        # init formated text
        self.fmtText = ''
        
    # other code phrasing
    def otherCodePhrasing(self):
        # add the start css tag of google code prettify
        self.fmtText = '<code class="prettyprint">'
        
        for line in self.rawText:
            self.fmtText += line.replace('<', '&lt;').replace('>','&gt;') + '\n'
            
        # add the end css tag of google code prettify
        self.fmtText += '</code>'
        
        self.ui.textEdit_fmt.setPlainText(self.fmtText)
        
    # python source code phrasing
    def pythonCodePhrasing(self):
        numOfTabs = 0
        numOfSpaces = 0
        
        # list for recording the white spaces
        list = [0]
        
        # add the start css tag of google code prettify
        self.fmtText = '<code class="prettyprint">'
        
        for line in self.rawText:
            numOfChrs = 0
            nSpaces = 0
            
            matchAnySpace = re.match('[\s]+', line)
            if(matchAnySpace):
                nSpaces = matchAnySpace.group().count('\t') * self.tabWidth
                nSpaces += matchAnySpace.group().count(' ')
                numOfChrs += len(matchAnySpace.group())
                
            tmpLine = line[numOfChrs:].replace('<', '&lt;').replace('>','&gt;')
            
            # if this line is newline only
            if(tmpLine == ''):
                self.fmtText += ' ' * (numOfTabs * self.tabWidth) + '\n'
                continue
                
            ### reserved function for worst case ###
            # if this line is commecnt
            #matchComment = re.match('^#', tmpLine)
            #if(matchComment):
            # self.fmtText += ' ' * (numOfTabs * self.tabWidth) + tmpLine + '\n'
            # continue
            
            # update tab character count by verifying the white spaces
            if(nSpaces > numOfSpaces):
                list.append(nSpaces)
                numOfTabs += 1
            elif(nSpaces < numOfSpaces):
                while list[len(list)-1] != nSpaces:
                    list.pop()
                    numOfTabs -= 1
                    
            numOfSpaces = nSpaces
            
            # when the line is a new class or function, reset all counts
            if(numOfChrs == 0):
                numOfSpaces = numOfTabs = 0
                
            self.fmtText += ' ' * (numOfTabs * self.tabWidth) + tmpLine + '\n'
            
        # add the end css tag of google code prettify
        self.fmtText += '</code>'
        
        self.ui.textEdit_fmt.setPlainText(self.fmtText)
        
    # SLOT for convert button
    def onConvertClicked(self):
        self.ui.textEdit_fmt.setEnabled(True)
        self.rawText = str(self.ui.textEdit_raw.toPlainText()).split('\n')
        
        if(self.ui.comboBox_langMode.currentIndex() == 0):
            self.pythonCodePhrasing()
            return True
            
        if(self.ui.comboBox_langMode.currentIndex() == 1):
            self.otherCodePhrasing()
            return True
            
    def onClearAllClicked(self):
        self.ui.textEdit_raw.setText('')
        self.ui.textEdit_fmt.setText('')
        self.ui.textEdit_raw.setFocus()
        
    def onCpClipboardClicked(self):
        self.ui.textEdit_fmt.selectAll()
        self.ui.textEdit_fmt.copy()
        mimeData = QtGui.QApplication.clipboard().mimeData();
        
    def onOpenClicked(self):
        filename = self.ui.fileDialog_open.getOpenFileName(self, 'Open file', QtCore.QDir.homePath()+'/Desktop')
        if(filename):
            fileIn = open(filename, 'r')
            rawText = fileIn.read()
            fileIn.close()
            self.ui.textEdit_raw.setPlainText(rawText)
            
    def onSaveAsClicked(self):
        filename = self.ui.fileDialog_saveAs.getSaveFileName(self, 'Save file', QtCore.QDir.homePath()+'/Desktop')
        if(filename):
            fileOut = open(filename, 'w')
            fileOut.write(self.fmtText)
            fileOut.close()
            
    def onTabWidthSeted(self):
        self.tabWidth = int(self.ui.lineEdit_tabWidth.text())
        
        
#================= CodeFormatter Class End ===================

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    codeFormatter = CodeFormatter()
    codeFormatter.show()
    sys.exit(app.exec_())

Screenshot

Please make sure the tab width is correct before converting.

Thursday, May 21, 2009

[Python & PyQt] Ftp Client

Download:FtpClient.zip

Introduction

Recently, I'm looking for the suitable script language for GUI design. It seems Python will be the first candidate among those competitive programming languages(ex. Perl, Tcl, Awk...). Here I would like to show you a GUI-based ftp client with the combination of Python and PyQt

Project - Ftp Client

Language: Python 2.6.2
GUI: PyQt 4.4.4
OS: Ubuntu 8.04
Modules: ftplib

Pre-works

Even though this project didn't involved any difficult algorithm or complicated techniques, the program still needs lots of package for running. If you are really interest in this program, please install follow packages.

It's not too difficult to install those packages on your system. But there is an important thing I want to remind you. Don't forget to add the PyQt modules's path into your python. Below is a simple way to add the system path to your python. (For example, my PyQt modules' location is '/usr/local/lib/python2.6/site-package')

import sys
sys.path.append('/usr/local/lib/python2.6/site-package')

Maybe someone can help us to write a GUI-based environment path manager.

Coding

I split the whole source code into three parts, UiFtpCli, FileTransfer and FtpClient.

UiFtpCli
#! /usr/local/bin/python

###############################################################
# Project: Python Ftp Client
# Filename: UiFtpCli.py
# Description: The Qt-based GUI for ftp client
# Date: 2009.05.21
# Programmer: Lin Xin Yu
# E-mail: nxforce@yahoo.com
# Website: http://importcode.blogspot.com
###############################################################

#================= import modules =============================
import os
from PyQt4 import QtCore, QtGui

#================= Ui_FtpClientClass Class Begin ==============
class Ui_FtpClientClass(object):
def setupUi(self, FtpClientClass):
# MainWindow Widget
FtpClientClass.setObjectName("FtpClientClass")
FtpClientClass.resize(QtCore.QSize(QtCore.QRect(0,0,800,500).size()).expandedTo(FtpClientClass.minimumSizeHint()))
self.moveToCenter(FtpClientClass)

# Centeral Widget for containing all widgets except for menubar and statusbar
self.centralwidget = QtGui.QWidget(FtpClientClass)
self.centralwidget.setObjectName("centralwidget")

# Ftp Host label
self.label_address = QtGui.QLabel(self.centralwidget)
self.label_address.setGeometry(QtCore.QRect(10,10,70,25))
self.label_address.setObjectName("label_address")

# Ftp Host input
self.lineEdit_address = QtGui.QLineEdit(self.centralwidget)
self.lineEdit_address.setGeometry(QtCore.QRect(80,10,510,25))
self.lineEdit_address.setObjectName("lineEdit_address")

# Port Label
self.label_port = QtGui.QLabel(self.centralwidget)
self.label_port.setGeometry(QtCore.QRect(600,10,30,25))
self.label_port.setObjectName("label_port")

# Port input
self.lineEdit_port = QtGui.QLineEdit(self.centralwidget)
self.lineEdit_port.setGeometry(QtCore.QRect(640,10,50,25))
self.lineEdit_port.setObjectName("lineEdit_port")

# Ftp tranfer mode
self.transferMode = QtGui.QComboBox(self.centralwidget)
self.transferMode.setGeometry(QtCore.QRect(700,10,90,25))
self.transferMode.insertItem(0, 'Passive')
self.transferMode.insertItem(1, 'Active')

# Username label
self.label_username = QtGui.QLabel(self.centralwidget)
self.label_username.setGeometry(QtCore.QRect(10,40,70,25))
self.label_username.setObjectName("label_username")

# Username input
self.lineEdit_username = QtGui.QLineEdit(self.centralwidget)
self.lineEdit_username.setGeometry(QtCore.QRect(80,40,270,25))
self.lineEdit_username.setObjectName("lineEdit_username")

# Password label
self.label_password = QtGui.QLabel(self.centralwidget)
self.label_password.setGeometry(QtCore.QRect(360,40,70,25))
self.label_password.setObjectName("label_password")

# Password input
self.lineEdit_password = QtGui.QLineEdit(self.centralwidget)
self.lineEdit_password.setGeometry(QtCore.QRect(430,40,260,25))
self.lineEdit_password.setObjectName("lineEdit_password")
self.lineEdit_password.setEchoMode(QtGui.QLineEdit.Password)

# Connect/Disconnect button
self.pushButton_connect = QtGui.QPushButton(self.centralwidget)
self.pushButton_connect.setGeometry(QtCore.QRect(700,40,90,25))
self.pushButton_connect.setObjectName("pushButton_connect")

# TreeWidget for local file system
self.sysTree = QtGui.QTreeWidget(self.centralwidget)
self.sysTree.setGeometry(QtCore.QRect(400,75,390,400))
self.sysTree.setHeaderLabels(QtCore.QStringList(['Name', 'Size', 'Owner', 'Group','Path']));
self.sysTree.setObjectName("sysTree")
self.sysTree.setEnabled(False)
self.sysTree.setRootIsDecorated(False)
self.sysTree.header().setStretchLastSection(False)

# TreeWidget for remote file system
self.ftpTree = QtGui.QTreeWidget(self.centralwidget)
self.ftpTree.setGeometry(QtCore.QRect(10,75,390,400))
self.ftpTree.setObjectName("ftpTree")
self.ftpTree.setEnabled(False)
self.ftpTree.setRootIsDecorated(False)
self.ftpTree.setHeaderLabels(QtCore.QStringList(['Name', 'Size', 'Owner', 'Group', 'Premission']));
self.ftpTree.header().setStretchLastSection(False)

# Predefined icons
self.folderIconBlue = QtGui.QIcon("icon/folder1.png")
self.fileIconBlue = QtGui.QIcon("icon/file1.png")
self.folderIconGreen = QtGui.QIcon("icon/folder2.png")
self.fileIconGreen = QtGui.QIcon("icon/file2.png")

# Download/Upload PorgressBar
self.progressBar = QtGui.QProgressDialog(self.centralwidget)
self.progressBar.setGeometry(QtCore.QRect(200,200,200,50))
self.progressBar.setObjectName("progressBar")
self.progressBar.setMaximum(100)
self.progressBar.setMinimum(0)
self.moveToCenter(self.progressBar)

# MessageBox for welcome message
self.msgBox = QtGui.QMessageBox(self.centralwidget)
self.msgBox.setGeometry(QtCore.QRect(200,200,400,300))
self.msgBox.setObjectName("msgBox")
self.msgBox.setStandardButtons(QtGui.QMessageBox.Ok)
self.moveToCenter(self.msgBox)

# MessageBox for About
self.aboutMsg = QtGui.QMessageBox(self.centralwidget)
self.aboutMsg.setGeometry(QtCore.QRect(200,200,400,300))
self.aboutMsg.setObjectName("aboutMsg")
self.aboutMsg.setWindowTitle("Ftp Client 1.0")
self.aboutMsg.setStandardButtons(QtGui.QMessageBox.Ok)
self.aboutMsg.setInformativeText(QtGui.QApplication.translate("FtpClientClass", "Ftp Client 1.0\nAuthor: Lin Xin Yu\nE-mail:nxforce.yahoo.com\nBlog:http://importcode.blogspot.com", None, QtGui.QApplication.UnicodeUTF8))
self.moveToCenter(self.aboutMsg)

FtpClientClass.setCentralWidget(self.centralwidget)

# Menubar & actions
self.menubar = QtGui.QMenuBar(FtpClientClass)
self.menubar.setGeometry(QtCore.QRect(0, 0, 600, 22))
self.menubar.setObjectName("menubar")
self.menuFile = QtGui.QMenu(self.menubar)
self.menuFile.setObjectName("menuFile")
self.menuHelp = QtGui.QMenu(self.menubar)
self.menuHelp.setObjectName("menuHelp")
FtpClientClass.setMenuBar(self.menubar)
self.statusbar = QtGui.QStatusBar(FtpClientClass)
self.statusbar.setObjectName("statusbar")
self.statusbar.showMessage('Disconnect')
FtpClientClass.setStatusBar(self.statusbar)
self.actionConnect = QtGui.QAction(FtpClientClass)
self.actionConnect.setObjectName("actionConnect")
self.actionDisconnect = QtGui.QAction(FtpClientClass)
self.actionDisconnect.setObjectName("actionDisconnect")
self.actionQuit = QtGui.QAction(FtpClientClass)
self.actionQuit.setObjectName("actionQuit")
self.actionAbout = QtGui.QAction(FtpClientClass)
self.actionAbout.setObjectName("actionAbout")
self.menuFile.addAction(self.actionConnect)
self.menuFile.addAction(self.actionDisconnect)
self.menuFile.addSeparator()
self.menuFile.addAction(self.actionQuit)
self.menuHelp.addAction(self.actionAbout)
self.menubar.addAction(self.menuFile.menuAction())
self.menubar.addAction(self.menuHelp.menuAction())

self.retranslateUi(FtpClientClass)

# Signal & Slot
QtCore.QObject.connect(self.lineEdit_address,QtCore.SIGNAL("returnPressed()"),FtpClientClass.onSetAddress)
QtCore.QObject.connect(self.lineEdit_username,QtCore.SIGNAL("returnPressed()"),FtpClientClass.onSetUsername)
QtCore.QObject.connect(self.lineEdit_password,QtCore.SIGNAL("returnPressed()"),FtpClientClass.onSetPassword)
QtCore.QObject.connect(self.lineEdit_port,QtCore.SIGNAL("returnPressed()"),FtpClientClass.onSetPort)
QtCore.QObject.connect(self.pushButton_connect,QtCore.SIGNAL("clicked()"),FtpClientClass.onConnect)
QtCore.QObject.connect(self.sysTree,QtCore.SIGNAL("itemDoubleClicked(QTreeWidgetItem*,int)"),FtpClientClass.onSysItemDoubleClicked)
QtCore.QObject.connect(self.ftpTree,QtCore.SIGNAL("itemDoubleClicked(QTreeWidgetItem*,int)"),FtpClientClass.onFtpItemDoubleClicked)
QtCore.QObject.connect(self.transferMode, QtCore.SIGNAL("currentIndexChanged(int)"), FtpClientClass.onTransferModeChanged)
QtCore.QObject.connect(self.actionConnect, QtCore.SIGNAL('triggered(bool)'), FtpClientClass.onConnect)
QtCore.QObject.connect(self.actionDisconnect, QtCore.SIGNAL('triggered(bool)'), FtpClientClass.onConnect)
QtCore.QObject.connect(self.actionQuit, QtCore.SIGNAL('triggered(bool)'), FtpClientClass.close)
QtCore.QObject.connect(self.actionAbout, QtCore.SIGNAL('triggered(bool)'), FtpClientClass.onAboutTriggered)
QtCore.QMetaObject.connectSlotsByName(FtpClientClass)

# Text Translation for international format - UTF8
def retranslateUi(self, FtpClientClass):
FtpClientClass.setWindowTitle(QtGui.QApplication.translate("FtpClientClass", "Ftp Client", None, QtGui.QApplication.UnicodeUTF8))
self.label_address.setText(QtGui.QApplication.translate("FtpClientClass", "Ftp Host:", None, QtGui.QApplication.UnicodeUTF8))
self.label_port.setText(QtGui.QApplication.translate("FtpClientClass", "Port:", None, QtGui.QApplication.UnicodeUTF8))
self.lineEdit_port.setText(QtGui.QApplication.translate("FtpClientClass", "21", None, QtGui.QApplication.UnicodeUTF8))
self.label_username.setText(QtGui.QApplication.translate("FtpClientClass", "Username:", None, QtGui.QApplication.UnicodeUTF8))
self.label_password.setText(QtGui.QApplication.translate("FtpClientClass", "Password:", None, QtGui.QApplication.UnicodeUTF8))
self.pushButton_connect.setText(QtGui.QApplication.translate("FtpClientClass", "Connect", None, QtGui.QApplication.UnicodeUTF8))
self.progressBar.setWindowTitle(QtGui.QApplication.translate("FtpClientClass", "File Transfer", None, QtGui.QApplication.UnicodeUTF8))
self.menuFile.setTitle(QtGui.QApplication.translate("FtpClientClass", "File", None, QtGui.QApplication.UnicodeUTF8))
self.menuHelp.setTitle(QtGui.QApplication.translate("FtpClientClass", "Help", None, QtGui.QApplication.UnicodeUTF8))
self.actionConnect.setText(QtGui.QApplication.translate("FtpClientClass", "Connect", None, QtGui.QApplication.UnicodeUTF8))
self.actionDisconnect.setText(QtGui.QApplication.translate("FtpClientClass", "Disconnect", None, QtGui.QApplication.UnicodeUTF8))
self.actionQuit.setText(QtGui.QApplication.translate("FtpClientClass", "Quit", None, QtGui.QApplication.UnicodeUTF8))
self.actionAbout.setText(QtGui.QApplication.translate("FtpClientClass", "About", None, QtGui.QApplication.UnicodeUTF8))

# widget alignment
def moveToCenter(self, widget):
screen = QtGui.QDesktopWidget().screenGeometry()
size = widget.geometry()
widget.move((screen.width()-size.width())/2, (screen.height()-size.height())/2)

#================= Ui_FtpClientClass Class End ================

As you can see, everything just like C++, because I follow the original Qt style. Thus, it will be much easier to handle the source code.

FileTransfer
#! /usr/local/bin/python

###############################################################
# Project: Python Ftp Client
# Filename: FileTransfer.py
# Description: A TransferProgress class for displaing download
# or upload progress.
# Date: 2009.05.21
# Programmer: Lin Xin Yu
# E-mail: nxforce@yahoo.com
# Website:http://importcode.blogspot.com
###############################################################

#================= TransferProgress Class Start ===============

class TransferProgress:
def __init__(self, pBar, filename, filesize, msg, file=None):

# file descriptor will be used while downloding
self.file = file

# cause the value of TreeWidgetItem are all string, so typecast it
self.totalSize = int(filesize)
self.recvSize = 0

# QProgressBar
self.pBar = pBar
self.pBar.setWindowTitle(msg+filename)
self.pBar.show()

def uploadProgress(self, data):
self.recvSize += len(data)
self.pBar.setValue(self.recvSize*100 /self.totalSize)

def downloadProgress(self, data):
self.recvSize += len(data)
self.file.write(data)
self.pBar.setValue(self.recvSize*100 /self.totalSize)

#================= TransferProgress Class End =================

Displaying the downloading/uploading progress will be the most difficult part in this project. In order to get the filesize and received size, we need to create some callback functions for storbinary() and retrbinary(). uploadProgress() is the callback function for storbinary(). downloadProgress() is the callback function for retrbinary(). So, here I put all essential variables and functions into TransferProgress object.

FtpClient
#! /usr/local/bin/python

###############################################################
# Project: Python Ftp Client
# Filename: FtpClient.py
# Description: A very simple ftp client via PyQt
# Date: 2009.05.21
# Programmer: Lin Xin Yu
# E-mail: nxforce@yahoo.com
# Website:http://importcode.blogspot.com
###############################################################

#================= import modules =============================
import os
import sys
import ftplib
import UiFtpCli
import FileTransfer
from PyQt4 import QtCore, QtGui, QtNetwork

#================= FtpClient Class Start ======================

class FtpClient(QtGui.QMainWindow):
def __init__(self, parent = None):
QtGui.QMainWindow.__init__(self, parent)
self.ui = UiFtpCli.Ui_FtpClientClass()
self.ui.setupUi(self)

self.username = None
self.password = None
self.address = None
self.port = 21
self.ftp = None
self.passiveMode = True

# record current root location for local filelist
# default path is your 'Dekstop'
self.sysRootPath = QtCore.QDir.homePath()+"/Desktop"

# Update treewdiget for local filelist
self.updateSysFileList()

#=== SLOT for ftp host address input ===
def onSetAddress(self):
self.address = str(self.ui.lineEdit_address.text())

#=== SLOT for username input ===
def onSetUsername(self):
self.username = str(self.ui.lineEdit_username.text())

#=== SLOT for password input ===
def onSetPassword(self):
self.password = str(self.ui.lineEdit_password.text())

#=== SLOT for password input ===
def onSetPort(self):
self.port = str(self.ui.lineEdit_port.text())

#=== SLOT for connect/disconnect button ===
def onConnect(self):
if(self.ftp == None):
self.connectToHost()
else:
self.disconnectFromHost()

#=== connect ===
def connectToHost(self):
self.ui.lineEdit_address.emit(QtCore.SIGNAL("returnPressed()"), None)
self.ui.lineEdit_username.emit(QtCore.SIGNAL("returnPressed()"), None)
self.ui.lineEdit_password.emit(QtCore.SIGNAL("returnPressed()"), None)
self.ui.lineEdit_port.emit(QtCore.SIGNAL("returnPressed()"), None)

# user should at least enter the ftp host
if(self.address == ''):
self.ui.statusbar.showMessage('Please enter ftp host address!')
self.ui.lineEdit_address.setFocus()
return False

# username & password are not required for anonymous server

#if(self.username == ''):
# self.ui.statusbar.showMessage('Please enter username!')
# self.ui.lineEdit_username.setFocus()
# return False

#if(self.password == ''):
# self.ui.statusbar.showMessage('Please enter password!')
# self.ui.lineEdit_password.setFocus()
# return False

# Connect to ftp host
self.ftp = ftplib.FTP(self.address)

# set tranfer mode
self.ftp.set_pasv(self.passiveMode)

# login
self.ftp.login(self.username, self.password)

# Show welcome message
self.ui.msgBox.setWindowTitle(self.address)
self.ui.msgBox.setInformativeText(QtGui.QApplication.translate("FtpClientClass", self.ftp.getwelcome(), None, QtGui.QApplication.UnicodeUTF8))
self.ui.msgBox.show()

self.ui.statusbar.showMessage('Connect to '+self.address+' ...')

# update remote filelist
self.updateFtpFileList()

self.ui.sysTree.setEnabled(True)
self.ui.ftpTree.setEnabled(True)

self.ui.transferMode.setEnabled(False)
self.ui.lineEdit_address.setEnabled(False)
self.ui.lineEdit_port.setEnabled(False)
self.ui.lineEdit_username.setEnabled(False)
self.ui.lineEdit_password.setEnabled(False)
self.ui.pushButton_connect.setText('Disconnect')

return True

#=== disconnect ===
def disconnectFromHost(self):
# close ftp
self.ftp.close()

# empty object
self.ftp = None


self.ui.sysTree.setEnabled(False)
self.ui.ftpTree.setEnabled(False)

self.ui.transferMode.setEnabled(True)
self.ui.lineEdit_address.setEnabled(True)
self.ui.lineEdit_port.setEnabled(True)
self.ui.lineEdit_username.setEnabled(True)
self.ui.lineEdit_password.setEnabled(True)
self.ui.pushButton_connect.setText('Connect')
self.ui.statusbar.showMessage('Disconnected from '+self.address+' ...')

#=== upload ===
def upload(self, filename, fullname):
# open file for reading
fileout = open(fullname, "rb")

# get filesize
statinfo = os.stat(fullname)
filesize = statinfo.st_size

# create a FileTransfer object for displaying upload progress
tp = FileTransfer.TransferProgress(self.ui.progressBar, filename, filesize, 'Upload: ')

# send data to server
self.ftp.storbinary("STOR " + filename, fileout, 1024, tp.uploadProgress)

# close file
fileout.close()

#=== download ===
def download(self, filename, filesize):
# open file for writing
filein = open(self.sysRootPath+'/'+filename, "wb")

# create a FileTransfer object for displaying download progress
tp = FileTransfer.TransferProgress(self.ui.progressBar, filename, filesize, 'Download: ', filein)

# get data from server
self.ftp.retrbinary("RETR " + filename, tp.downloadProgress, 1024)

# close file
filein.close()


#=== Update treewidget for remote filelist ===
def updateFtpFileList(self):
# clear treewidget
self.ui.ftpTree.clear()

# get filelist from server
data = []
self.ftp.dir(data.append)

# re-format the structure and send into ftpTree
for line in data:
spline = line.split()
item = QtGui.QTreeWidgetItem()
item.setText(0, spline[8])
item.setText(1, spline[4])
item.setText(2, spline[2])
item.setText(3, spline[3])
item.setText(4, spline[0])

if(spline[0][0] == 'd'):
item.setIcon(0, self.ui.folderIconBlue)
else:
item.setIcon(0, self.ui.fileIconBlue)

self.ui.ftpTree.addTopLevelItem(item)


#=== Update treewidget for local filelist ===
def updateSysFileList(self):
# clear treewidget
self.ui.sysTree.clear()

# get local directory path
sysDir = QtCore.QDir(self.sysRootPath)
sysDir.setFilter(QtCore.QDir.Files | QtCore.QDir.Dirs | QtCore.QDir.NoSymLinks)
sysDir.setSorting(QtCore.QDir.DirsFirst | QtCore.QDir.Name)

# retrive the file info and pack into sysTree
fileList = sysDir.entryInfoList();
for i in range(0,len(fileList)):
fileInfo = fileList[i]
item = QtGui.QTreeWidgetItem()
item.setText(0, fileInfo.fileName())
item.setText(1, QtCore.QString.number(fileInfo.size()/1024, 10)+' KB')
item.setText(2, fileInfo.owner())
item.setText(3, fileInfo.group())
item.setText(4, fileInfo.filePath())

if(fileInfo.isDir()):
item.setIcon(0, self.ui.folderIconGreen)
else:
item.setIcon(0, self.ui.fileIconGreen)

self.ui.sysTree.addTopLevelItem(item)

#=== SLOT for ftpTree ===
def onFtpItemDoubleClicked(self, item):
permission = str(item.text(4))
if(permission[0] == 'd'):
self.ftp.cwd(str(item.text(0)))
self.updateFtpFileList()
else:
self.download(str(item.text(0)), str(item.text(1)))

#=== SLOT for sysTree ===
def onSysItemDoubleClicked(self, item):
path = str(item.text(4))
if(QtCore.QFileInfo(path).isDir()):
self.sysRootPath = path
self.updateSysFileList()
else:
self.upload(str(item.text(0)) ,path)

#=== SLOT for transfer mode combobox ===
def onTransferModeChanged(self, index):
if(index == 0):
self.passiveMode = True
else:
self.passiveMode = False

#=== SLOT for menubar's actions ===
def onAboutTriggered(self):
self.ui.aboutMsg.show()

#=== SLOT for close window ===
def closeEvent(self, event):
if(self.ftp != None):
self.ftp.close()

#================= FtpClient Class End ======================

if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
ftpClient = FtpClient()
ftpClient.show()
sys.exit(app.exec_())

I spend almost over 90 percent coding time on graphic user interface design, only 10 percent is core algorithm. So if you don't really understand the detailed concept of the source code. Don't worry about it, practice makes perfect.

Demo


Conclusion
I didn't implement too many function for this program, so ... maybe it's your turn.
  • If you are C++ & Qt expertise, don't try to switch to PyQt if no necessary.
  • There are too many Qt classes that overlap with python classes. Before using Qt class, please make sure the implementation is stable and completed
  • Interpreted language is much faster than compiled language while compiling.
  • For small-scale, non-commercial, client oriented software, python is still good for programmers.