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.

[Python] py2exe

For most of windows users, they don't like to install several packages for only one program. In this way, programmers might distribute their software as an executable file for windows platform. In python, we can use py2exe to convert our project as win32 application.

py2exe official website

http://www.py2exe.org/


Before running py2exe, you should install .net framework in your system

Microsoft .NET Framework 3.5 Service Pack 1


Here I take "PyQtCodeGenerator" for example:
setup.py
#! /usr/bin/python

from distutils.core import setup
import py2exe

setup(windows=[{"script":"PyQtCodeGenerator.py"}], options={"py2exe":{"includes":["sip"]}})


Put the 'setup.py' under 'PyQtCodeGenerator' folder, and typing following command for converting

python setup.py install

After waiting fews seconds, you will see lots of runtime essential files inside the 'dist' folder.

So far, py2exe can only support windows platform, so please do not use it on your linux system.

Or, you can write a py2bin for lots of python programmers.

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.

Tuesday, May 19, 2009

[Java]Diamond

public class Diamond {
public static int i;
public static int j;
public static int c;
public static int a;
public static int b;
public static int d;
public static void main(String args[]){
for(i=0;i<=5;i++){
for(j=0;j<5-i;j++)
System.out.print(" ");
for(c=0;c<=2*i;c++)
System.out.print("*");
System.out.print("\n");
}

for(a=0;a<=5;a++){
for(b=5;b>4-a;b--)
System.out.print(" ");
for(d=8;d>=2*a;d--)
System.out.print("*");
System.out.print("\n");
}
}
}


[想法]用三角形的原理將菱形呈現出來


-------------- @ @ .. Lin Xin Yu ------
Another method...

public class Diamond {
public static final int height = 6;
public static void main(String args[]){
int i,j;
for(i=0;i<height;i++){
for(j=0;j<i;j++)
System.out.print(" ");
for(j=0;j<2*(height-i)-1;j++)
System.out.print("*");
System.out.println();
}
}
}

Monday, May 18, 2009

[Perl] CPAN

Introduction

Comprehensive Perl Archive Network(CPAN) is an organization for collecting most of Perl modules from all over the world. Below is a simple way to install the CPAN on your system.
Perl -MCPAN -e "shell"
After the installation completed, you probably need to update the CPAN by typing follow command:
install Bundle::CPAN
Here is an example for the use of CPAN:

Perl Message-Digest Program


Get or update Digest module
sudo cpan -i Digest
Waiting for few minutes for this installation. If you get any error, try to follow the instruction to solve it.

Coding

Here I just use the most popular message-digest algorithm MD5 for this demo.
#! /usr/bin/perl -w

use strict;
use warnings;
use Digest::MD5;

# load filename from argument
my $filename = shift;

# if user didn't give a filename, ask it.
unless($filename){
print "Please enter a filename:";
chomp($filename = <STDIN>);
}

# open file
open(FILE,"< $filename") or die "Fail to open [$filename] for MD5 checksum\n";

# create a new cipher text
my $ctx = Digest::MD5->new;

# add file into cipher text
$ctx->addfile(*FILE);

# get MD5 checksum
my $digest = $ctx->digest;

# print MD5 as hex code.
print "MD5:".unpack('H*', "$digest"),"\n";

Demo
md5check.pl test.dat
MD5:9e1837461d81e918f73c8e6fe7a5d0e2

Saturday, May 16, 2009

[Perl & Qt] OC:Net -> Multiperson Chat

Introduction

Download: SimpleChat.zip

For multi-person chat programming, programmer might use pthread (multi-threading) or fork (multi-processing) for multiplexing. In the early of 1990s, select() was frequently used for non-blocking communication. and it still been used for nowadays server. As we know, socket is just a one of the file descriptor. Thus, someone could enable the non-blocking property through fcntl() (control open file descriptors) for non-blocking communcation. However, the performance issue might come after this setting. So, in this topic, I would like to show you how to use the conventional way to implement Chat Server via select(). For chat client, I will aslo introduce you two methods, one is select() in perl, another one is QThread in Qt.

Server-Side

#! /usr/bin/perl -w

###############################################################
# Project: Perl Server Socket for multiperson chat
# Description: A convention way to implement multiperson chat
# via select().
# Date: 2009.05.16
# Programmer: Lin Xin Yu
# Website:http://importcode.blogspot.com
###############################################################

use strict;
use warnings;
use IO::Socket;
use IO::Select;

#=============== Network Settings =============================
my $MAX_LEN = 1024;
my $host = 'localhost';
my $port = shift || 3456;

#==============================================================
#********** THIS IS THE MAIN PROGRAM **************************
#==============================================================

my $buf;

# Create the receiving socket
my $server = new IO::Socket::INET( LocalPort => $port,
Proto => 'tcp',
Listen => SOMAXCONN,
Reuse => 1,)
or die "Could not create socket: $!\n";

#=============== Select() Settings ============================

# create handle set for reading
my $readSet = new IO::Select();

# add server socket into readSet
$readSet->add($server);
$readSet->add(\*STDIN);
my %socketSet = ();

#=============== Main Process =================================

while (1) {

# get a set of readable socket (blocks until at least one socket is ready)
my ($readableSet) = IO::Select->select($readSet, undef, undef, 0);

# take all readable socket in turn
foreach my $socket (@$readableSet) {
# accpeting new client if the socket is serverSocket
if ($socket == $server) {
my $client = $server->accept();
$readSet->add($client);
$socketSet{$client} = $client;
}elsif($socket == \*STDIN){
$buf = <STDIN>;
if($buf =~ /^(exit|quit)/){
foreach my $closeSocket(values %socketSet){
syswrite($closeSocket, "Server is going to offline after 5 Seconds\n", $MAX_LEN);
}

sleep(5);
foreach my $closeSocket(values %socketSet){
close($closeSocket);
}
close($server);
exit(0);
}
}
# otherwise it is an ordinary socket and we should read and process the request
else {
sysread($socket, $buf, $MAX_LEN);
if($buf) {
if($buf =~ /^(:exit|:quit|:q)/i){
$buf =~ s/^(:exit|:quit|:q)[ ]+//;
delete $socketSet{$socket};
$readSet->remove($socket);
close($socket);
}
foreach my $closeSocket(values %socketSet){
if($closeSocket != $socket){
syswrite($closeSocket, $buf, $MAX_LEN);
}
}
} else { # the client has closed the socket or aborted
# remove the socket from the $readSet and close it
delete $socketSet{$socket};
$readSet->remove($socket);
close($socket);
}
}
}
}

#==============================================================
#********** END OF THE MAIN PROGRAM ***************************
#==============================================================

As you can see, server just simply check the available socket and given the suitable processing. In addition to use select(), programmer must remember to use system IO rather than buffering IO for non-blocking communication.

Client-Side

Perl Client

#! /usr/bin/perl
###############################################################
# Project: Perl Client Socket for multiperson chat
# Description: A convention way to implement multiperson chat
# via select().
# Date: 2009.05.16
# Programmer: Lin Xin Yu
# Website:http://importcode.blogspot.com
###############################################################

use strict;
use warnings;
use IO::Socket;
use IO::Select;

#=============== Network Settings =============================
my $MAX_LEN = 1024;
my $remote_host = shift || 'localhost';
my $remote_port = shift || 3456;
my $client;

#==============================================================
#********** THIS IS THE MAIN PROGRAM **************************
#==============================================================

# get use name from input
print "please enter your name:";
chomp(my $name = <STDIN>);
print STDERR $name.":";

# Socket connection for Client.
$client = new IO::Socket::INET( PeerAddr => $remote_host,
PeerPort => $remote_port,
Proto => 'tcp',
Type => SOCK_STREAM,
Timeout => 20)
or die "Couldn't connect to $remote_host:$remote_port : $@\n";

my $buf;

#=============== Select() Settings ============================

# create handle set for reading
my $readSet = new IO::Select();

# add client socket into readSet
$readSet->add($client);
$readSet->add(\*STDIN);


while (1) {

# get a set of readable socket (blocks until at least one socket is ready)
my ($readableSet) = IO::Select->select($readSet, undef, undef, 0);

# take all readable socket in turn
foreach my $socket (@$readableSet) {
if($socket == \*STDIN){
chomp($buf = <STDIN>);
if($buf =~ /^(:exit|:quit|:q)/){
close($client);
exit(0);
}else{
syswrite($client, "$name:$buf\n", $MAX_LEN);
print STDERR $name.":";
}
}
# otherwise it is an ordinary socket and we should read and process the request
else {
sysread($socket, $buf, $MAX_LEN);
if($buf) {
my $i=0;
while($i++<256){
print STDERR "\b";
}
print STDERR $buf;
print STDERR $name.":";
} else { # the server has close the socket or aborted
# remove the socket from the $readSet and close it
close($client);
exit(0);
}
}
}
}

#==============================================================
#********** END OF THE MAIN PROGRAM ***************************
#==============================================================


One of the major problem for this perl chat client is your standard output might be interrupted by incoming message. Do NOT report it as a bug, it's a characteristic of system IO.

Qt Client

I don't want to show the whole bunch source code on the blog. Here I just post the most important sections of incoming and outcoming.

incoming parts in charclient.cpp

bool ChatClient::onSendClicked(){
int ret;
char msg[256];

QString qmsg = nickName+":"+ui.lineEdit_input->text();

ui.textEdit_output->append(qmsg);
ui.lineEdit_input->setText("");
qmsg += "\n";
QStringToChar(qmsg, msg, qmsg.length());

try{
ret = client->send(msg);
if(ret <= 0 ) throw ret;
}catch(int err){
if(err == 0) ui.textEdit_output->append("Server is offline!\n");
else if(err == -1) ui.textEdit_output->append("Fail to send message to Server!\n");
}

return true;
}


outcoming parts in ClientRecvThread.cpp

void ClientRecvThread::run(){
int ret;
char buf[MAX_BUF];

while((ret = socket->recv(buf)) > 0){
buf[ret-1]='\0';
ui->textEdit_output->append(buf);
}

if(ret == 0){
ui->textEdit_output->append("Server is offline or close the connection");
}else{
ui->textEdit_output->append("Fail to receive message from server!");
}

exit(0);
}

The reasonable solution for chat client is a GUI program. Thus, we can separate input and output message into two widgets, and processing it respectively. Here, I use C++ and Qt framework for this fancy work.

After a successful connection, this program will have two threads at the same time. One is the main thread which was created by main program itself, another one is ClientRecvThread which will been activated when the connection success.

The ClientRecvThread class which inherit from QThread was created for listening the incoming message only. It must be a great challenge while doing the multi-threading programming in this project, especially when you encountering the race condition or thread collection. Any single mistabke might kick your program in the hell. For instance, in Qt chat client, those two threads(main and ClientRecvThread) will sometimes use the same resource (QTextEdit) at the same time, and probably crash the whole program. A possible solution for these situation is locking the resource while one thread is now processing with it. but in this simple demonstration, I didn't fix it.

#For rapidly development, it's better to use QThread and QTcpSocket rather than pthread and self-implemented socket.

#QThread is similar to Java's runnable, so you might feel comfortable if you already know Java.

Compiling

There is no need to tell you how to compile perl program ( casue it's a interpreted language ). For Qt Client, you just need to follow the following steps:

  • Download and unzip source code
  • Type 'make' to compile it
  • Waiting for fews seconds
  • Run the program or just click it

This program was developed with Qt 4.5.1, so I don't pretty sure that the old version compiler can work without any error.

Screenshot

Client - Xinyu


Client - Lin Xin Yu

Conclusion

  • Socket is a typical solution for the integration with several different programming languages.
  • Select() is still a good way for non-blocking communication.
  • Multi-threading can save more resources than multi-processing, but difficult to handle.
  • A good C++ style program can even have the same performance with C.
  • Let the heavy loading program as simple as possible.
Any suggestions would be appreciated, but may not be taken.
For more detailed information, please see the source code.

Tuesday, May 12, 2009

[Java] Guess number(修正)

import java.util.Random;
import java.util.Scanner;

public class Guess {
public static void main(String args[]){
int i = 1, j = 99, data;
Scanner input = new Scanner(System.in);
Random myRandom = new Random();
int num = myRandom.nextInt(99)+1;
do{
System.out.println("please input 1~99-->");
data = input.nextInt();

if(num > data){
i = data+1;
System.out.println(i+"~"+j);
}else if(num<data){
j = data-1;
System.out.println(i+"~"+j);
}
}while(num != data);
System.out.println("你命中了!!");
}
}

[解釋]亂數nextInt(99)回傳值0~98,所以我加上1後變為1~99

Monday, May 11, 2009

[Java]Triangle

public class Star {
public static void main(String args[]){
int i,j;
for(c=5;c>0;c--){
for(i=c;i>0;i--)
System.out.print("*");
System.out.print("\n");
}
}
}


[解釋]內圈是在將star output並且換行,外圈給定star的個數

Sunday, May 10, 2009

[Python] Python Socket Tutorial

Introduction:
This is my first socket program in python.

server.py
#! /usr/bin/python
################################################################################
# Name:Python Server Socket
# Description: A simple implementation for chat one to one in python socket.
# Date: 2009/5/10
# Programmer: Hsiang Hsien Hsiang
# Website:http://importcode.blogspot.com
###############################################################################

import socket
import threading

class serverThread(threading.Thread):
def __init__(self, connection, address):
self.connection = connection
self.address = address
self.onConnect = True
threading.Thread.__init__(self)
def run(self):
while self.onConnect:
recvData = self.connection.recv(1024)
if recvData != ":exit" and recvData != '':
print 'Client:' + recvData
else:
break
self.connection.close()
print "Connection Close"
def close(self):
self.onConnect = False

class server:
def __init__(self):
self.host = 'localhost' #host = 127.0.0.1
self.port = 8001 #port = 8001
self.onConnect = True
self.serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.serverSocket.bind((self.host, self.port))
self.serverSocket.listen(5)
self.connection, self.address = self.serverSocket.accept()
def serverRun(self):
print "Enter :exit to disconnect"
print 'Connected by', self.address
thread = serverThread(self.connection, self.address)
thread.start()
while 1:
sendData = raw_input()
try:
self.connection.send(sendData)
except socket.error:
print "No Body Online, Offline Now"
break
if sendData == ":exit":
thread.close()
break

if __name__ == '__main__':
server().serverRun()

client.py

#! /usr/bin/python
#############################################################################################
# Name:Python Client Socket
# Description: A simple implementation for chat one to one in python socket.
# Date: 2009/5/10
# Programmer: Hsiang Hsien Hsiang
# Website:http://importcode.blogspot.com
############################################################################################

import socket
import threading

class clientThread(threading.Thread):
def __init__(self, connection, address, client):
self.connection = connection
self.address = address
self.onConnect = True
self.client = client
threading.Thread.__init__(self)
def run(self):
while self.onConnect:
recvData = self.connection.recv(1024)
if recvData != ":exit" and recvData != '':
print 'Server:' + recvData
else:
print recvData
self.onConnect = False
self.client.close()
self.connection.close()
def close(self):
self.onConnect = False

class client:
def __init__(self):
self.onConnect = True
self.host = 'localhost' #host = 127.0.0.1
self.port = 8001 #port = 8001
self.clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.clientSocket.connect((self.host, 8001))
self.address = (self.host, 8001)
def clientRun(self):
print "Enter :exit to disconnect"
print 'Link to', self.address
thread = clientThread(self.clientSocket, self.address, self)
thread.start()
while self.onConnect:
sendData = raw_input()
try:
self.clientSocket.send(sendData)
except socket.error:
print "Server Offline, Disconnect Now"
break
if sendData == ':exit':
thread.close()
break
def close(self):
self.onConnect = False

if __name__ == '__main__':
client().clientRun()

Note:
There are many bug in this program.
Maybe you can improve it.



Thursday, May 7, 2009

[Perl] OC:Net -> upload & download Server-Client

Introduction

I would like to hold the first open project (or open competition) for network programming in 2009 at [import Code.*]. In this project, you can implement any kind of network program with any programming language. The basic requirement is sharing your code on blog. The winner will be next core founder (wtf...).

Below is my first Perl network program for this project. (Because someone forbids me to post my previous work - Network Programming in C, I switch to Perl. )

Server.pl
#! /usr/bin/perl

###############################################################
# Name:Perl Server Socket
# Description: A simple implementation for uploading &
# downloading in perl socket.
# Date: 2009.05.05
# Programmer: Lin Xin Yu
# Website:http://importcode.blogspot.com
###############################################################

use strict;
use warnings;
use IO::Socket;
use POSIX qw(:sys_wait_h);

#=============== Network Settings =============================

my $MAX_LEN = 1024;
my $host = 'localhost';
my $port = shift || 3456;

#==============================================================
#********** CLIENT ROUTINE ************************************
#==============================================================
sub client_jobs(@){
my $client = shift;

print $client "Welcome to Perl server.\n";
while(defined(my $buf = <$client>)){
if($buf =~ /^upload/){
# get filename from client.
chomp(my $filename = <$client>);

# open file for writing.
unless(open(DEST,"> $filename")){
print $client "not ready for uploading\n";
next;
}
print $client "ready for uploading\n";

# get filesize from client.
chomp(my $filesize = <$client>);

my $len = 0;
my $data = '';
while($len < $filesize && !($buf =~ /^quit/i) ){
$buf = <$client>;
$len += length($buf);
$data .= $buf;
}
chomp $data;

# write data into file
print DEST $data;

print "[$filename] Upload Completed\n";
unless(close DEST){
print "Can't close [$filename]\n";
next;
}
#or die "Can't close file: $filename\n";
}

if($buf =~ /^download/){
# get filename from client.
chomp(my $filename = <$client>);

# open file for writing.
unless(open(SRC,"< $filename")){
print $client "not ready for downloading\n";
next;
}
print $client "ready for downloading\n";

$buf = <$client>;
if($buf =~ /^not ready/){
#print $buf."\n";
unless(close SRC){
print "Can't close [$filename]\n";
next;
}
}

# send filesize to client.
my $filesize = -s $filename;
print $client $filesize."\n";

my $len = 0;
while($len < $filesize){
read(SRC, $buf, $MAX_LEN, 0) or die "Client Aborted!\n";
print $client $buf;
$len += length($buf);
}

# flush socket;
print $client "\n";

#print "[$filename] Download Completed\n";
unless(close SRC){
print "Can't close [$filename]\n";
next;
}
#or die "Can't close file: $filename\n";
}

last if !$buf || $buf =~ /^(?:quit|exit)$/i;
}
close($client);
}

#==============================================================
#********** THIS IS THE MAIN PROGRAM **************************
#==============================================================

# Socket connection for Server.
my $server = new IO::Socket::INET( LocalPort => $port,
Proto => 'tcp',
Listen => SOMAXCONN,
Timeout => 20,
Reuse => 1,)
or die "Socket could not be created,Reason:$!";

# Wait for accepting.
print "Waiting for connection...\n";
while (my $client = $server->accept()){
pipe(README, WRITEME);
if (my $pid = fork) {
# parent
close(WRITEME);
} else {
die "cannot fork: $!" unless defined $pid;
my $peer = gethostbyaddr($client->peeraddr,AF_INET) || $client->peerhost;
my $port = $client->peerport;
warn "Connection from [$peer,$port]\n";

client_jobs($client);
# child
close(README);
}
close(README);
}

close($server);

#=============================================================
#********** END OF THE MAIN PROGRAM **************************
#=============================================================



Client.pl
#! /usr/bin/perl
###############################################################
# Name: Perl Client Socket
# Description: A simple implementation for uploading &
# downloading in perl socket.
# Date: 2009.05.05
# Programmer: Lin Xin Yu
# Website:http://importcode.blogspot.com
###############################################################

use strict;
use warnings;
use IO::Socket;

#=============== Network Settings =============================
my $MAX_LEN = 1024;
my $remote_host = shift || 'localhost';
my $remote_port = shift || 3456;
my $client;

#==============================================================
#********** THIS IS THE MAIN PROGRAM **************************
#==============================================================
$SIG{'INT'}= sub{ print $client "quit\n"; };

# Socket connection for Client.
$client = IO::Socket::INET->new( PeerAddr => $remote_host,
PeerPort => $remote_port,
Proto => 'tcp',
Type => SOCK_STREAM,
Timeout => 20)
or die "Couldn't connect to $remote_host:$remote_port : $@\n";

# Print welcome message.
my $buf = <$client>;
print $buf;

# Wait for user input.
print ":";
while(chomp(my $oneLineCommand = <STDIN>)){
last if !$oneLineCommand || $oneLineCommand =~ /^(?:quit|exit)$/i;

my @commands = split(/[ ,]/, $oneLineCommand);
my $option = $commands[0];
shift(@commands);

###### for 'upload' command ######
if($option =~ /^upload/){
foreach my $filename(@commands){
unless (open(SRC,"< $filename")){
print "Fail to open [$filename] for uploading.\n";
next;
}

# send 'upload' command to server.
print $client $option."\n";

# send filename to server.
print $client $filename."\n";

# get upload state from server
$buf = <$client>;
if($buf =~ /^not ready/){
#print $buf."\n";
unless(close SRC){
print "Can't close [$filename]\n";
next;
}
}

# send filesize to server.
my $filesize = -s $filename;
print $client $filesize."\n";

# send data to server.
my $len = 0;
while($len < $filesize){
read(SRC, $buf, $MAX_LEN, 0);
print $client $buf;

$len += length($buf);
print STDERR "\b\b\b\b\b\b\b%".int($len/$filesize*100);
}

print " [$filename] Upload Completed!\n";

# flush socket;
print $client "\n";

unless(close SRC){
print "Can't close [$filename]\n";
next;
}
}
}

###### for 'download' command ######
if($option =~ /^download/){

foreach my $filename(@commands){
# send 'download' command to server.
print $client $option."\n";

# send filename to server.
print $client $filename."\n";

# get download state from server
$buf = <$client>;
if($buf =~ /^not ready/){
#print $buf."\n";
unless(close SRC){
print "Can't close [$filename]\n";
next;
}
}

# send downloading state to server.
unless(open(DEST,"> $filename")){
print "Fail to open [$filename] for downloading.\n";
print $client "not read for downloading\n";
next;
}
print $client "read for downloading\n";

# get filesize from server.
my $filesize = <$client>;
chomp($filesize);
print $filesize;

# get data to server.
my $len = 0;
my $data = '';
while($len < $filesize){
$buf = <$client>;
$len += length($buf);
$data .= $buf;
print STDERR "\b\b\b\b\b\b\b%".int($len/$filesize*100);
}
chomp $data;
print DEST $data;

print " [$filename] Download Completed!\n";

unless(close DEST){
print "Can't close [$filename]\n";
next;
}
}
}

if($option =~ /^(?:help|\?)$/i){
print "usage: \n\tdownload [filename 1] [filename 2] ...\n"
."\tupload [filename 1] [filename 2] ...\n";
}

print "\n:";
}

close($client);

#==============================================================
#********** END OF THE MAIN PROGRAM ***************************
#==============================================================



Note:
Cause I don't really familiar with Perl language, the code might incomplete and ugly.