Thursday, July 29, 2010

[PyQt] IORedirection

Source : IORedirection.zip

在PyQt程式撰寫過程中,我習慣使用terminal去執行來觀察一些debug訊息,而在一些程式設計的需求下,有時候我們必須將標準輸入資訊給導向至圖形介面輸出,這時候就必須將I/O給重新導向,在python裡要將standard output/error給導向至Qt的textEdit方法如下:

class OutLog:
    def __init__(self, edit, color=None):
        self.edit = edit
        self.color = color
        
    def write(self, msg):
        if self.color:
            tc = self.edit.textColor()
            self.edit.setTextColor(self.color)
            
        self.edit.insertPlainText(msg)
        
        if self.color:
            self.edit.setTextColor(tc)
            
            
class IOReDirectionClass(QtGui.QWidget):
    def __init__(self, parent = None):
        QtGui.QWidget.__init__(self, parent)
        self.ui = Ui_IOReDirectionClass.Ui_IOReDirectionClass()
        self.ui.setupUi(self)
        
        sys.stdout = OutLog( self.ui.textEdit_output)
        sys.stderr = OutLog( self.ui.textEdit_output, QtGui.QColor(0,0,255))
        
        
    def onRedirectClicked(self):
        print "Standard Output"
        print >> sys.stderr, "Standard Error"
        print "\nYour python environment path:"
        for path in sys.path:
            print path
            
    def systemOutput(self):
        self.ui.textEdit_output.setText(sys.stdout)

簡單來說就是重寫stdout和stderr的write()函式,將stdout/stderr的message傳送到QtGui.QTextEdit裡顯示。這樣的技巧可以應用在一些網路程式顯示連線資訊的部份。

[PyQt] PyQtMerger

Source: PyQtMerger.zip

以往在Linux底下要合併.001 .002 .003 ...這類的檔案都會在terminal底下使用cat這個指令來完成,但是一旦要合併的檔案數量很大時,輸入指令的工作就顯得很沒效率,為了快速解決這個問題我決定自行實做了類似HJsplit的程式,只是PyQtMerger只實現基本的合併部份,並沒有去實作分割的功能。

程式的概念很簡單,先判斷檔案的副檔名結構,再抓取要合併的檔案名稱串列,程式片段如下:
if(spPath[1] == ".001" or spPath[1] == ".000"):
    self.outputPath = spPath[0]
    self.ui.labExtensionValue.setText(".001 .002 .003 .004 .005 ...")
    self.ui.labMergeFileValue.setText(self.outputPath)
    for i in range(001,10):
        ext = ".%03d" % i
        if(os.path.exists(spPath[0]+ext)):
            self.mergeList.append(spPath[0]+ext)

最後透過要合併的檔案串列資訊,搭配簡易的io操作就可以合併檔案了:
if (self.outputPath != ''):
    self.ui.statusbar.showMessage('Merging...')
    output = open(self.outputPath, 'wb')
    
    for path in self.mergeList:
        intput = open(path, 'rb')
        
        while(True):
            buff = intput.read(self.buffSize)
            if(len(buff) == 0):
                break
            output.write(buff)
        intput.close()
        index += 1
        self.ui.pBar.setProperty("value", float(index)/len(self.mergeList)*100)
        
    output.close()

如果要讓此程式功能更完善,可以從副檔名的判斷著手,增加一些非數字的連續姓字母偵測能力,或者是把split的功能給整合進去吧!

Wednesday, July 28, 2010

[PyQt] FtpClient

Source: FtpClient.zip

這支程式只是純粹要實做圖形介面的Ftp客戶端程式,透過python的ftplib和Qt的widgets的組合產生了這麼一個簡易的FtpClient程式。


由於現在的Ftp還多了Http Ftp的型態,不像傳統的Ftp會有基本的"."以及".."(本目錄,前一目錄)的資料夾,面對這樣的情況就必須自行偵測並手動加上".."(parent directory)這個目錄。

for line in data:
    spline = line.split()
    if(spline[8][0] != '.' and self.flagParentDir == False):
        item = QtGui.QTreeWidgetItem()
        item.setText(0, '..')
        item.setText(1, '1024')
        item.setText(2, spline[2])
        item.setText(3, spline[3])
        item.setText(4, spline[0])
        item.setIcon(0, self.ui.folderIconBlue)
        self.ui.ftpTree.addTopLevelItem(item)
        self.flagParentDir = True
    else:
        self.flagParentDir = True

ftplib所提供的dir指令回傳回來的檔案結構如下:
['drwxr-xr-x', '4', '2000', '0', '512', 'Jun', '10', '21:20', 'Linux']

所以在處理treeitem時,必須注意一下index位置所對應的檔案類型,否則成列出來的資料就會錯亂。

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])

上下傳的進度顯示方面則是搭配了retrbinary()以及storbinary()所接收的callback函式的格式,程式片段如下:
class TransferProgress:
    def __init__(self, pBar, filename, filesize, msg, file=None):
    
        # file descriptor will be used while downloding
        self.file = file
        
        self.totalSize = int(filesize)
        self.recvSize = 0
        
        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的類別,裡面包含了uploadProgress(),downloadProgress()這兩個函式,分別對應到上傳和下載的進度顯示。要注意的是uploadProgress(),downloadProgress()它們都接收了一個名為data的變數,這個data變數裡面紀錄著每一次retrbinary()和storbinary()在傳送接收資料時的檔案資料區塊(可能是一個由ftplib所內定的buffersize),藉由這個data變數我們才能顯示檔案傳輸進度以及作資料存取。

def download(self, filename, filesize):
    filein = open(self.sysRootPath+'/'+filename, "wb")
    tp = FileTransfer.TransferProgress(self.ui.progressBar, filename, filesize, 'Download: ', filein)
    self.ftp.retrbinary("RETR " + filename, tp.downloadProgress, 1024)
    filein.close()
以下載為例,先建立一個檔案物件,再建立一個TransferProgress的物件tp,傳入tp.downloadProgress這個callback function給retrbinary函式,而檔案寫入的動作則是由callback過程中將data傳至downloadPorgress()裡,並於tp一開始建立所設定的檔案物件filein來寫入,整個上下傳部份顯得有點複雜了一些。

另外值得注意的是當你要從QString轉型到python string這個過程中必須要使用str()這個函式來做型態轉型,否則資料會解讀錯誤。反之,則不需要。
def onFtpItemDoubleClicked(self, item):
    permission = str(item.text(4))
    if(permission[0] == 'd'):
        self.ftp.cwd(str(item.text(0)))
    else:
        self.download(str(item.text(0)), str(item.text(1)))
    self.updateFtpFileList()
    
    
def onSysItemDoubleClicked(self, item):
    path = str(item.text(4))
    if(QtCore.QFileInfo(path).isDir()):
        self.sysRootPath = path
    else:
        self.upload(str(item.text(0)) ,path)
    self.updateSysFileList()

整個FtpClient只有實做到連線上下傳的部份,進一步的功能如續傳,進階檔案變更,往後有這個需要再來撰寫吧!

Sunday, July 11, 2010

cx_Freeze :standalone python application

Official Website:cx_Freeze

使用PyQt設計者一定會發現一件事情,如果今天想要將已寫好的script放在其他Linux底下執行時,是否也要先一一安裝python, qt, sip, pyqt等等套件之後才能正常用運行我們使用PyQt寫出來的程式,答案基本上是肯定的,但是光想到這裡許多設計師可能會因此怯步,改換回其他GUI設計平台。為了解決這樣的一個問題,網路上已有一些套件可以製作standalone python app,如cx_Freeze, pyinstaller等,由於pyinstaller支援程度還不是很完善,這裡就介紹cx_Freeze為主。

從官方下載原始碼來編譯,編譯方式如下:
python setup.py build
安裝cx_Freeze:
python setup.py install
透過上述兩道指令後就可以將cx_Freeze安裝至你的python的site-packages裡。cx_Freeze的使用方式也很簡單,操作方式如下:

找一個你已經寫好的PyQt script,這裡以PyQtLoader.py為例:
cxfreeze PyQtLoader.py
產生好的檔案會放置在dist這個資料夾裡,如果你觀察夠仔細的話你會發現它只是把一些執行PyQtLoader.py所需要的library給彙整打包起來,但是光只是這麼一個簡單的PyQtLoader程式,彙整出來的獨立執行檔總共需要約略135mb的檔案空間,檔案會這麼大多是因為Qt的Library所造成的(基本上執行一個單純的python程式只需要概略為5mb的libray),如何產生高效能且檔案小的PyQt獨立原始檔,這部份可能要牽扯到如何精簡化Library,僅保留所需的函式,但是這部份只有Qt開發團隊才有權利去更動。所以如果你仍然期望著檔案小效能高的程式,建議你還是轉回Qt甚至使用Gtk+吧!

PyQt Script Loader

Source: PyQtLoader.zip
PyQtCodeGenerator之後,又一個懶人程式,以往在執行python的script使用terminal是理所當然的事情,但是一旦搭配了Qt的GUI介面之後總是希望執行PyQt的程式也可以透過滑鼠的點擊就可以執行,為此我一直找不到解決方案,只好自行使用C語言以圖法煉鋼的模式產生一個Script Loader來完成。

整個程式介面很單純,只有兩個按鈕跟一個LineEdit:


執行完畢後會跳出下面的選擇功能:


操作介面非常簡單,你只需要提供想要產生的script位置以及檔名,按下Generate之後此程式就會產生一個由C語言編譯出來的程式,以PyQtCodeGenerator為例,透過PyQtLoader產生之後的資料夾裡的圖示如下:


往後要執行PyQtCodeGenerator我只要點擊如上圖被選取的執行檔即可。

程式解說

程式部份需要多作說明的是下面這一段:
reply = messageBox.exec_()

if messageBox.clickedButton() == actionRun:
    subprocess.call(self.filePath)
    exit(0)
    
if messageBox.clickedButton() == actionOpen:
    subprocess.Popen(['nautilus' , os.path.dirname(self.filePath)])
    exit(0)
    
if messageBox.clickedButton() == actionClose:
    exit(0)

呼叫外部程式我使用了subprocess,而不是os.system()或os.popen*(),因為在python2.6版之後已經不建議使用上述的兩個modules,取而代之的是subprocess.call()或者是subprocess.Popen(),值得注意的是使用subprocess.Popen()時你要將要傳入的檔名跟參數以list的方式傳入,且之中不可有多餘的空白字元,否則會無法正常執行。

關於subprocess的詳細用法可以參考下面的網頁
http://docs.python.org/library
/subprocess.html

http://www.logilab.org/blogentry/20469

另外在檔案路徑處理方面,os.path模組節省了我們自行處理文字分離的部份,詳細使用資訊參考下面的網頁:
http://docs.python.org/library/os.path.html

Saturday, July 10, 2010

gedit color scheme

Official Website:http://live.gnome.org/GtkSourceView/StyleSchemes


在Linux底下寫程式時我多是使用Bluefish或者是gedit,而gedit本身自帶的color scheme中我則比較偏好Oblivion,當然除了這個style之外,可以從gnome網站上下載其他scheme,或者是你也可以手動編輯scheme的xml來客製化配色。

Sunday, July 04, 2010

PyQt Code Generator

Source: PyQtCodeGenerator.zip

每次在撰寫PyQt程式總是要先建立一些必備的程式片段,天性懶惰的我總希望能夠簡化這個步驟,但是又不希望使用以往寫C的模式套用在Python,這樣會有損其結構性,於是我就靈機一動不如來寫個PyQt Code Generator吧!

整個程式碼解開之後包含了以下目錄:

/PyQtCodeGenerator
---/icon
------Folder.png << 圖檔
---Ui_CodeGenerator.py << UI widgets, signal等定義
---CodeGeneratorClass.py << slots和程式功能的實做
---XmlParseHandler.py << 使用SAX來解讀.ui檔案
---PyQtCodeGenerator.py << 主程式

執行範例畫面如下:


整個程式沒有什麼技術深度,只是做一些基本的條件判斷輸出而已,其中稍微特別一點的是使用xml.sax來處理xml結構的檔案,關於xml.sax的資料可以參考下面的網站:
http://docs.python.org/library/xml.sax.html
除此之外你也會發現到程式畫面上有幾個模組是不可選擇的(例如QtUiTools等),有一部分是因為有些modules僅適用於windows環境(如QAxContainer, QAxServer),還有一些則是因為PyQt還尚未支援這些模組,當然你還是透過以下的修改打開這些模組,以QtDBus為例。

在Ui_CodeGenerator.py原始碼裡找到下面這一行:
self.checkBox_dbus.setEnabled(False)
把它改成如下(或者把下面這一行註解掉,甚至刪除掉效果都一樣):
self.checkBox_dbus.setEnabled(True)
PyQtCodeGenerator同時還支援.ui檔案的處理,你可以從QtCreator裡先設計好圖形介面,再由PyQtCodeGenerator裡的UI Type最後一個選項 Load .ui File來載入設計好的ui,最後透過PyQtCodeGenerator整合出一個基本的PyQt程式。PyQtCodeGenerator在小型的project設計能發揮作用,但如果是要設計多層ui介面的整合,那我到要想想還有什麼解決方案了。

PyQt

Official Website:PyQt

有一段時間我試著在Linux底下找尋圖形介面程式開發的解決方案,我試用過效能非常高的Gtk+, 也使試用過平台支援很完善的Qt,當然也玩了一些wxWidget,至於那個平台獨立運行在JRE的Java就不在主要的考量範圍內。Gtk+確實帶來非常好的執行效能,但是使用它來寫GUI猶如在Windows底下使用Win32 API來寫GUI一樣的痛苦,延長的開發時程,大大降低我原本設計的需求。而Qt雖然有QtCreator做為良好的IDE,畢竟它還是以C++為主軸設計,面對運算能力較差的CPU環境裡,編譯過程也會是一種惡夢。最後我決定使用Script語言的直譯特性與widgets資源豐富的Qt整合出來的PyQt作為我在Linux底下
GUI設計的主要開發環境。

PyQt Installation


不同於使用LGPL授權的Qt,PyQt是使用授權較為嚴謹一點的GPLv2/v3,在非商業用途下,這些授權協議並不會對你在設計圖形系統程式上有太大的影響。PyQt基本上使用C++所撰寫出來的,所以在編譯過程中你必須要先裝G++。在編譯PyQt之前,你的系統裡必須要有以下幾個程式,這幾個套件你可以從我先前的文章或者其官方網站上取得編譯資源。
安裝完上述的套件後,我們可以開始正式編譯PyQt了,編譯過程跟一般的程式沒有兩樣,只是configure是使用python來撰寫的,自然要使用python來執行。
python ./configure.py
make
make install

如果你的CPU運算能力並不是很好,這個編譯過程可能會花上你不少時間。編譯完成之後你可以從原始碼裡的examples挑出一些範例來執行,這裡我們執行一個名為stickman的程式(/examples/animation/stickman)


PyQt Resource

至今還沒有一個完整的PtQt Reference,其中原因也可能是因為PyQt幾乎跟Qt是一比一的對應,只是在語法上必須從C++轉到Python上來表示,對於要學習PyQt的人除了藉由pyuic將原先qt裡的ui轉換過來之外,也可以導覽以下的tutorial來學習

http://www.commandprompt.com/community/pyqt/

http://zetcode.com/tutorials/pyqt4/

Saturday, July 03, 2010

Vimeo

你也許早已聽過且使用過Youtube,但你可能還不知道網路上還有這麼一個富有許多高質感的影片網站 - Vimeo,有別於以往影音娛樂為主的線上影音網站(Youtube, Hulu),Viemo提供的是一種短片藝術設計,設計者透過音樂與影像來傳達他們的所要表達的意識,歡迎有閒有興趣的人去參觀一下!

Web Desktop - eyeOS

Official Webiste:eyeOS

現今Web Desktop已經不是什麼新科技,但他卻是一個很好的Cloud Computing技術下的實例,目前網路上有不少Web Desktop如eyeOS, Glide OS, G.ho.st等等,這裡我就推崇eyeOS啦!(因為他是用PHP+AJAX開發出來的,並且以AGPL3授權下的開放性原始碼程式),eyeOS提供了一些基本的文件處理,影音應用等功能,下面就是使用eyeOS裡的 Browser瀏覽youtube影片。


或許有一天Web Desktop會改變我們以往使用電腦的習慣。

Friday, July 02, 2010

Qt and Qt Creator

Official Website: qt.nokia.com


在先前已經發布過關於Qt的文章,這裡將以往的資料重新校正更新。
Qt是一個跨平台(Windows, Linux, Mac, Symbian, Maemo)的圖形介面Framework並由知名的手機公司Nokia所支撐,在linux系統底下有不少軟體是以Qt作為圖形介面的開發,例如VLC,SMPlayer等等。透過QtCreator的圖形介面開發工具,可以有效降低開發所需時間,以提昇整體工作效益。

Qt Installation

如果沒有開發上的需要,建議直接下載官方所提供的bin安裝檔,不然手動編譯Qt可是要花上不少時間。

下載位置:http://qt.nokia.com/downloads
非商業用途時選擇LGPL版本下載即可。目前最新的版本為4.6.3,以Linux為例,安裝方式如下:

對下載好的安裝檔增加執行的權限
chmod u+x qt-sdk-linux-x86-opensource-2010.04.bin
執行qt安裝程式
./qt-sdk-linux-x86-opensource-2010.04.bin
安裝完後Qt的主程式會被放置在/opt/qtsdk-2010.04裡,這時你可以視情況將qt的bin連結到.profile裡。
sudo gedit .profile
將Path的變數修改成如下:
PATH="$HOME/bin:$PATH:/opt/qtsdk-2010.04/qt/bin"
重新登入系統即生效。

對於其他語言與Qt搭配的開發環境(如PyQt)在編譯過程可能會需要一些Library,而這邊Library必須是手動編譯Qt才會安裝至系統,但如前面所提,手動編譯Qt實在是太花時間,這些缺少的Library我們就額外安裝吧!
sudo apt-get install libfreetype6-dev libfontconfig-dev libxrender-dev libsm-dev libglib2.0-dev libxext-dev libxext6-dbg x11proto-xext-dev
以往使用qmake的編譯方式筆者我就不推薦再使用,除非你只是單純想簡單測試一下Qt的小程式,否則還是以QtCreator會比較完善一點。這裡還是提供了一個使用傳統qmake來編譯的範例。
Download : QPasswordCheck.zip
下載回去解壓縮後,輸入以下指令來編譯:
qmake -project
qmake
make

執行編譯好的程式畫面如下:


Qt Resource

取得Qt的相關資料除了透過官方的http://qt.nokia.com/developer開發頁面之外,你還可以透過參考Qt的範例來了解如何使用Qt寫圖形介面程式,相關範例可以在/opt/qtsdk-2010.04/qt/demos底下搜尋到。

JDownloader

Official website:JDownloader


一套由Java所撰寫出來的跨平台(正確來說你的系統只要有相符的JRE就可以執行)的檔案下載軟體,他跟一般的Http下載不同的是JDownloader支援眾多的檔案下載空間,例如知名的Rapidshare, Hotfile等空間,透過JDownloader可以有效管理所要下載的檔案,省去一些繁複的下載手續。從官方下載好JDownloader.zip之後(不要使用jd.sh來下載,不然會很慢,不然你可以試著使用PPA來更新),將JDownloader.zip解開,並執行下列設定。

如果你沒有JRE的環境,請安裝sun-java6-jre
sudo apt-get install sun-jave6-jre
接著設定JDownloader資料夾底下的JDownloader.jar,新增執行的權限
sudo chmod +x JDownloader.jar
設定完成,你可以右鍵點選JDownloader.jar使用Java runtime machine來開啟JDownloader了。

[Ubuntu] Squid Proxy

Official Website: Squid
在Linux底下想要建立一個Proxy時可以使用Squid這個套件,一個簡單的設定方式如下:

安裝Squid,Ubuntu 10.04所自帶的版本為2.7,如果需要更新版本可以到官方網站上下載自行編譯
sudo apt-get install squid

至/etc/squid,備份squid.conf設定檔
sudo cp squid.conf squid.conf.bak
編輯squid.conf設定檔
sudo gedit squid.conf
預設port為3128,如果要修改port(約略在1110行左右),請更改http_port的值(假設要更改為8080)
http_port 8080
設定存取控制清單(約略600行的位置),增加網路ip的存取權(假設網段為192.168.1.0/24)
acl myhost src 192.168.1.0/24
設定http存取控制(約略在670行左右)
http_access allow myhost
重新啟動squid,開始使用Squid Proxy Server。
service squid restart
#關於squid的mac address過濾的功能僅限於區域網路,對於廣域網路是無效的。Squid所提供的功能不單單是如此而已,詳細設定部份可以參考Squid 2.7 Online Manuals

Thursday, July 01, 2010

[Ubuntu] Ramdisk for Linux

雖著記憶體的大小不斷上升,在加上平常一般作業階段是不需要用上2GB甚至4GB之多的實體記憶體,有鑑於此,我們可以將這些剩餘的記憶體空間做個妥善的運用,透過linux底下tmpfs檔案系統,我們可以把記憶體掛載成硬碟使用,對於要做大量的I/O讀寫或者模擬運算時,Ramdisk可以大大提昇整體效能,以提昇運作速度。

首先在/media底下先建立一個名為ramdisk的資料
sudo mkdir /media/ramdisk
接著將此資料夾的權限設定為完全存取
sudo chmod 777 /media/ramdisk
最後設定所要掛載的記憶體空間,存取模式,並掛載至/media/ramdisk即可使用
sudo mount -t tmpfs -o size=512M,mode=0777 tmpfs /media/ramdisk
如果你想要每次開機都自動掛載,可以在/etc/fstab裡這樣設定。
tmpfs /media/ramdisk tmpfs size=512M,mode=777 0 0

[Ubuntu] Blueman

Official Website:Blueman

Ubuntu本身有自帶bluetooth的工具,但是在功能上略為簡略,這裡推薦一個不錯的bluetooth的控制軟體-Blueman,安裝方式可以透過增加PPA的路徑會比較方便點。

sudo add-apt-repository ppa:blueman/ppa
接著輸入以下指令就可以安裝Blueman了
sudo apt-get update
sudo apt-get install blueman