Cairo-Dock, Docky, Avant-window-navigator, Screenlets, gDesklets

Cairo-Dock, Docky, Avant-window-navigator, Screenlets, gDesklets 這些桌面應用程式除了改善原有的操作外觀之外,我想最重要的是將資訊整合起來方便快速查詢。基本上這些套件在ubuntu底下都可以透過apt-get安裝,如下:

Cairo-Dock
Official Website:http://www.glx-dock.org/
sudo apt-get install cairo-dock
Docky
Official Website:http://do.davebsd.com/wiki/Docky
sudo apt-get install docky
Avant-window-navigator
Official Website:https://launchpad.net/awn
sudo apt-get install avant-window-navigator
Screenlets
Official Website:http://www.screenlets.org/index.php/Home
sudo apt-get install screenlets
gDesklets
Official Website:http://www.gdesklets.de/
sudo apt-get install gdesklets
基本上Cairo-Dock, Docky, Avant-window-navigator這三個是屬於同一類的,擇一安裝就好;而Screenlets, gDesklets這兩個性質也很像,所以也是選擇一個安裝。筆者我習慣使用Cairo-Dock+Screenlets,但由於Screenlets的外掛套件是由廣大網路上的開發者撰寫出來的,自然穩定度與完善度就會有落差,如果你考量到程式的穩定,那我建議你先裝Avant-window-navigator就好。


更多關於gnome桌面應用的資訊可以到下面這個網站取得
Source:http://gnome-look.org/

[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的功能給整合進去吧!

[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只有實做到連線上下傳的部份,進一步的功能如續傳,進階檔案變更,往後有這個需要再來撰寫吧!

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

gedit color scheme

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


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

Orange - data analysis tool

Installation pip install orange3 Run orange python -m Orange.canvas