用Python擴展和延伸Nautilus
如何使用 Python 為 GNOME 桌面、screenlets 架構(gòu)以及 Nautilus 創(chuàng)建腳本,來交付高生產(chǎn)能力的環(huán)境?桌面腳本實現(xiàn)拖放功能,以及快速訪問常用信息與服務(wù)的功能。本文將介紹如何使用 Python 來增加功能,從而實現(xiàn)對桌面 Nautilus 的擴展。
對于 GNOME 桌面用戶來說,Nautilus 編程可能是更比較常用的應(yīng)用程序之一。它能夠通過簡單的圖形界面,來處理所有的文件復(fù)制、移動、重命名、以及搜索的問題。從表面上看,似乎不存在 Nautilus 不能處理的文件相關(guān)事務(wù) — 除非考慮執(zhí)行具有 shell 腳本的任務(wù)。
Nautilus 開發(fā)工具提供了多個不必打開主代碼庫而增加新功能的方法。最簡單的方法是使用能執(zhí)行那些通常在終端提示符上執(zhí)行的命令的 bash 或者 bash 腳本。該方法使得嘗試使用這一命令來確保他們完成想要首先完成的任務(wù)。還可以采用其他語言,包括 C 腳本語言、GnomeBasic、Perl、以及 Python。本文介紹如何利用 Python 語言來為 Nautilus 增加新功能。假定讀者已對 Python 語言及 Python 標(biāo)準(zhǔn)庫有所了解。
Nautilus 腳本
擴展 Nautilus 的第一個方法是通過在 /home 中發(fā)現(xiàn)的名為 .gnome2/nautilus-scripts 的特定目錄。當(dāng)在 Scripts 菜單下的文件或者文件夾上點擊鼠標(biāo)右鍵時,該目錄下所有可執(zhí)行文件將會出現(xiàn)。還可以選擇多個文件或者文件夾,并采用相同的右擊方法,將文件清單傳遞給腳本。
當(dāng)調(diào)用腳本時,Nautilus 支持多個包含當(dāng)前目錄以及所選文件等內(nèi)容的環(huán)境變量。表 1 展示了這些環(huán)境變量。
表 1. Nautilus 環(huán)境變量
| 環(huán)境變量 | 描述 | 
| NAUTILUS_SCRIPT_SELECTED_FILE_PATHS | 所選文件的新行分割路徑(僅針對本地) | 
| NAUTILUS_SCRIPT_SELECTED_URIS | 所選文件的新行分割 URIs | 
| NAUTILUS_SCRIPT_CURRENT_URI | 當(dāng)前位置 | 
| NAUTILUS_SCRIPT_WINDOW_GEOMETRY | 當(dāng)前窗口的位置和大小 | 
在 Python 中,通過對 os.environ.get 函數(shù)的一個調(diào)用來獲取這些變量的值,具體如下:
selected = os.environ.get('NAUTILUS_SCRIPT_SELECTED_FILE_PATHS,'')
此調(diào)用返回一個字符串,其包含到達由換行符分隔的全部所選文件。Python 利用下列代碼,簡化了將這一字符串返回到可迭代列表中的操作:
targets = selected.splitlines()
此時,也許應(yīng)該停下來探討一下用戶交互。當(dāng)控制從 Nautilus 傳送到腳本后,在該點上確實不存在對腳本的限制。根據(jù)腳本作用的不同,甚至不需要任何用戶反饋,除了一些類型的完成或錯誤消息,這樣通過一些簡單的消息框就可處理好。由于在編寫 Nautilus 時采用了 gtk windowing 工具包,所以盡管這不是必須的,但是采用相同的做法很合乎邏輯。您可以很方便地使用 TkInter 或者 wxPython。
鑒于本文的目的,您將采用 gtk。生成一個用于通信完成狀態(tài)的簡單消息框,僅需幾行代碼,出于方便閱讀的目的,如果想創(chuàng)建簡單的函數(shù)來生成消息,這個代碼將最為合適??偣残枰?4 行代碼:
def alert(msg): dialog = gtk.MessageDialog() dialog.set_markup(msg) dialog.run()
#p#
示例:創(chuàng)建簡單腳本來返回所選文件的數(shù)量
第一個示例程序?qū)⒍鄠€程序段合并成一個簡單腳本,來返回當(dāng)前所選文件的數(shù)量。這一腳本可用于文件或者目錄??衫昧硪粋€ Python 庫函數(shù),os.walk,遞歸地構(gòu)建每個目錄中文件的清單??偣灿?38 行代碼,如清單 1 所示,這就是這一小工具所需的全部內(nèi)容,其中還包括了空行。
清單 1. 用于 Filecount 腳本的 代碼 Python
#!/usr/bin/env python
import pygtk
pygtk.require('2.0')
import gtk
import os
def alert(msg):
"""Show a dialog with a simple message."""
dialog = gtk.MessageDialog()
dialog.set_markup(msg)
dialog.run()
def main():
selected = os.environ.get('NAUTILUS_SCRIPT_SELECTED_URIS', '')
curdir = os.environ.get('NAUTILUS_SCRIPT_CURRENT_URI', os.curdir)
if selected:
targets = selected.splitlines()
else:
targets = [curdir]
files = []
directories = []
for target in targets:
if target.startswith('file:///'):
target = target[7:]
for dirname, dirnames, filenames in os.walk(target):
for dirname in dirnames:
directories.append(dirname)
for filename in filenames:
files.append(filename)
alert('%s directories and %s files' %
(len(directories),len(files)))
if __name__ == "__main__":
main()
圖 1 展示了當(dāng)在文件上右擊鼠標(biāo)或者選擇一組文件時所看到的內(nèi)容。Scripts 菜單選項展示 .gnome2/nautilus-scripts 中所有的可執(zhí)行文件,并給出了打開文件夾的選項。選擇一個文件來執(zhí)行該腳本。
圖 1.在 Nautilus 中選擇文件
圖 2 展示了 Filecount.py 腳本的運行結(jié)果。
圖 2. Filecount.py 輸出
在調(diào)試 Nautilus 腳本時,有幾件事需要注意。第一件事是關(guān)閉 Nautilus 的所有實例,來使它完全重新加載,并找到新腳本或者擴展。可采用如下命令:
nautilus -q
下一個常用命令可實現(xiàn)不必打開首選或者配置數(shù)據(jù),而直接運行 Nautilus。這在解決腳本或者擴展在無意間造成破壞之類的問題時,會節(jié)省很多步驟。命令如下:
nautilus -no-desktop
確保 filecount 工具可被 Nautilus 訪問所剩的最后一步是將其復(fù)制到 ~/.gnome2/nautilus-scripts 目錄,并改變文件代碼來允許執(zhí)行,相關(guān)命令是:
chmod +x Filecount.py
#p#
示例:創(chuàng)建文件 cleanup 工具
第二個例子是,創(chuàng)建文件 cleanup 工具,來查找任何可能由 Vim 或者 EMACS 之類的編輯器臨時生成的文件。僅通過簡單地修改 check 函數(shù),就可利用相同的概念來清除任何特定文件的目錄。這一代碼屬于靜默操作,這意味著它執(zhí)行后不向用戶提供任何反饋。
該腳本的主函數(shù)看上去基本與前面具的示例相同,除了幾個微不足道的異常。此代碼會利用遞歸概念多次調(diào)用主函數(shù),直至處理完最后一個目錄為止。您可以采用 os.walk 函數(shù),而不必采用遞歸來完成相同的任務(wù)。文件檢查發(fā)生在 check 函數(shù)中,僅簡單地檢查以波浪號(~)或者井號(#)結(jié)束的文件,以井號后開始或擴展名 .pyc 結(jié)束的文件。該示例展示了 Python 標(biāo)準(zhǔn)庫 os 模塊所提供的數(shù)量眾多的函數(shù)。它還提供了獨立于操作系統(tǒng)方式來操作路徑名和目錄,以及執(zhí)行文件操作的示例。清單 2 展示了該腳本的代碼。
清單 2. 用于 cleanup 腳本的 Python 代碼
#!/usr/bin/env python
import pygtk
pygtk.require('2.0')
import gtk
import os
def check(path):
"""Returns true to indicate a file should be removed."""
if path.endswith('~'):
return True
if path.startswith('#') and basename.endswith('#'):
return True
if path.endswith('.pyc'):
return True
return False
def walk(dirname=None):
selected = os.environ.get('NAUTILUS_SCRIPT_SELECTED_FILE_PATHS', '')
curdir = os.environ.get('NAUTILUS_SCRIPT_CURRENT_URI', os.curdir)
if dirname is not None:
targets = [dirname]
elif selected:
targets = selected.splitlines()
else:
targets = [curdir]
for target in targets:
if target.startswith('file:///'):
target = target[7:]
if not os.path.isdir(target): continue
for dirname, dirnames, files in os.walk(target):
for dir in dirnames:
dir = os.path.join(dirname, dir)
walk(dir)
for file in files:
file = os.path.join(dirname, file)
if check(file):
os.remove(file)
if __name__ == '__main__':
walk()
#p#
Nautilus 擴展
增強 Nautilus 的第二個方法是通過創(chuàng)建擴展。此方法比第一個復(fù)雜,但有很多優(yōu)勢。Nautilus 擴展可被內(nèi)嵌到文件展示窗口中,那么就可以編寫利用以前沒有的信息來填充列的擴展。首先要做的就是利用如下命令安裝 python-nautilus 擴展:
sudo apt-get install python-nautilus
此命令下載并安裝所需的文件,包括文檔和示例。可在目錄 /usr/share/doc/python-nautilus/examples 中到找到示例代碼。安裝完成后,就可以訪問一組 Nautilus 類和提供程序來再次對其進行編碼。表 2 展示了該清單。
表 2. Nautilus 類與供應(yīng)商
| 類或者供應(yīng)商 | 描述 | 
| nautilus.Column | 引用 Nautilus column 對象 | 
| nautilus.FileInfo | 引用 Nautilus fileinfo 對象 | 
| nautilus.Menu | 引用 Nautilus menu 對象 | 
| nautilus.MenuItem | 引用 Nautilus menuitem 對象 | 
| nautilus.PropertyPage | 引用 Nautilus propertypage 對象 | 
| nautilus.ColumnProvider | 允許在 Nautilus 列中展示輸出 | 
| nautilus.InfoProvider | 提供關(guān)于文件的信息 | 
| nautilus.LocationWidgetProvider | 展示位置 | 
| nautilus.MenuProvider | 為右擊菜單增加新功能 | 
| nautilus.PropertyPageProvider | 為屬性頁面增加信息 | 
gnome.org 站點上提供的示例展示了 MenuProvider(background-image.py 和 open-terminal.py)、ColumnProvider 以及 InfoProvider(block-size-column.py)、和 PropertyPageProvider(md5sum-property-page.py)的使用。ColumnProvider 采用 13 行 Python 可執(zhí)行代碼來向 Nautilus 引入新的列。一旦該代碼被放置到合適的目錄中(~/.nautilus/python-extensions)并且 Nautilus 已重啟,在單擊 View > Visible Columns 時將會看到新的選項。當(dāng)將查看類型設(shè)置為 List 時,才會出現(xiàn) Visible Columns 選項。通過選擇展示以下 Python 庫調(diào)用結(jié)果的復(fù)選框,來啟用 Block size 列:
str(os.stat(filename).st_blksize))
任何 Python 擴展的基本模式都是對現(xiàn)有 Nautilus 提供程序基本類進行子類劃分,然后執(zhí)行一系列指令,并最終返回合適的 Nautilus 對象。在 block-size-column.py 例子中,返回的對象是 nautilus.Column。必須向 Nautilus 傳遞 4 個參數(shù),包括 name、attribute、label、以及 description。本例子的 Python 代碼是:
return nautilus.Column("NautilusPython::block_size_column",
"block_size",
"Block size",
"Get the block size")
編寫新擴展的代碼涉及繼承來自特定基本類的信息。 在 block-size-column.py 的例子中,nautilus.ColumnProvider 與 nautilus.InfoProvider 在類定義中有舉例,因此新類要從這兩處繼承。接下來需要覆蓋來自基類或者類的任何方法來填充列。在 block-size-column.py 例子中,可通過覆蓋 get_columns 和 update_file_info 方法來完成。
向 Nautilus 擴展傳遞信息的方法與腳本示例不同。Nautilus 實際上是啟動新的流程來執(zhí)行腳本,并設(shè)置多個環(huán)境變量來傳遞信息。在與 Nautilus 相同的流程中執(zhí)行的擴展,能夠訪問對象、方法、和屬性。通過 nautilus.FileInfo 傳遞的文件信息,包括 file_type、location、name、uri、以及 mime_type。想要向 FileInfo 對象增加信息,必須調(diào)用 add_string_attribute 方法。下面的例子是采用這一方法,來向 FileInfo 對象增加新的屬性。#p#
示例:列出了文件中的行數(shù)
第一個例子使用 PropertyPageProvider 方法在文件(或多個文件)上單擊右鍵顯示行數(shù)和參數(shù),然后單擊 Properties。這一擴展背后的基本思想是計算文件中的行數(shù)和參數(shù)個數(shù),并在文件屬性頁的新選項卡中報告結(jié)果。擴展可以直接訪問了 Nautilus 數(shù)據(jù)結(jié)構(gòu),包括 file 對象。惟一要做的是利用 urllib.unquote 庫函數(shù)來打開名字,操作如下:
filename = urllib.unquote(file.get_uri()[7:]
Python 中的一些行完成了對行及參數(shù)計數(shù)的主要工作。對于本例來說,創(chuàng)建 count 函數(shù)來將整個文件讀取到一個大字符串中,然后計算參數(shù)數(shù)量及新添參數(shù)數(shù)量。因為屬性頁面可被顯示為很多選中的文件及目錄,所以必須預(yù)先計算多個文件。此時,惟一要做的就是將結(jié)果添加到屬性頁上的新頁中。本例創(chuàng)建了示例 gtk.Hbox,然后利用獲取的信息來填充大量標(biāo)簽,如清單 3 所示。
清單 3. Linecountextension.py 文件
import nautilus
import urllib
import gtk
import os
types = ['.py','.js','.html','.css','.txt','.rst','.cgi']
exceptions = ('MochiKit.js',)
class LineCountPropertyPage(nautilus.PropertyPageProvider):
def __init__(self):
pass
def count(self, filename):
s = open(filename).read()
return s.count('\n'), len(s)
def get_property_pages(self, files):
if not len(files):
return
lines = 0
chars = 0
for file in files:
if not file.is_directory():
result = self.count(urllib.unquote(file.get_uri()[7:]))
lines += result[0]
chars += result[1]
self.property_label = gtk.Label('Linecount')
self.property_label.show()
self.hbox = gtk.HBox(0, False)
self.hbox.show()
label = gtk.Label('Lines:')
label.show()
self.hbox.pack_start(label)
self.value_label = gtk.Label()
self.hbox.pack_start(self.value_label)
self.value_label.set_text(str(lines))
self.value_label.show()
self.chars_label = gtk.Label('Characters:')
self.chars_label.show()
self.hbox.pack_start(self.chars_label)
self.chars_value = gtk.Label()
self.hbox.pack_start(self.chars_value)
self.chars_value.set_text(str(chars))
self.chars_value.show()
return nautilus.PropertyPage("NautilusPython::linecount",
self.property_label, self.hbox),
圖 3 展示了在文件上單擊右鍵并單擊 Linecount 選項卡的結(jié)果。此時,需要注意,這一特性可用于文件或者任何一組選定的文件和目錄。所報告的數(shù)字將代表所有文件中的所有行。
圖 3. 單擊 Linecount 選項卡來查看文件的行數(shù)
最后,修改擴展函數(shù)來填充一列而不是整個屬性頁。因而代碼的修改相當(dāng)少,盡管需要同時從 nautilus.ColumnProvider 和 nautilus.InfoProvider 繼承。還必須執(zhí)行 get_columns 和 update_file_info。方法 get_columns 僅返回由方法 count 獲取的信息。
方法 count 為列提供程序擴展采用不同的技術(shù)。Python 的 readlines 例程用于將一個文件的所有行讀取到一列字符串中。計算行的總數(shù)就是在 len(s) 語句中返回的清單元素的數(shù)量。在兩個例子中都要進行文件類型檢查:這是要確保僅對包含需要計數(shù)行的文本文件進行計數(shù)??衫萌缦滦衼韯?chuàng)建一列可接受的文件擴展:
types = ['.py','.js','.html','.css','.txt','.rst','.cgi']
第二個清單包含了不會被計數(shù)的異常,對于本例來說,包含具有如下行的文件:
exceptions = ['MochiKit.js']
這兩個清單用于包括或者排除具有如下兩行代碼的文件:
if ext not in types or basename in exceptions: return 0
整個擴展需要 26 行可執(zhí)行代碼。您可能想要修改擴展,并輸入清單來包含或者排除感興趣的文件,清單 4 展示了完整的擴展。
清單 4. 用于 Linecountcolumn 擴展的 Python 代碼
import nautilus
import urllib
import os
types = ['.py','.js','.html','.css','.txt','.rst','.cgi']
exceptions = ['MochiKit.js']
class LineCountExtension(nautilus.ColumnProvider, nautilus.InfoProvider):
def __init__(self):
pass
def count(self, filename):
ext = os.path.splitext(filename)[1]
basename = os.path.basename(filename)
if ext not in types or basename in exceptions:
return 0
s = open(filename).readlines()
return len(s)
def get_columns(self):
return nautilus.Column("NautilusPython::linecount",
"linecount",
"Line Count",
"The number of lines of code"),
def update_file_info(self, file):
if file.is_directory():
lines = 'n/a'
else:
lines = self.count(urllib.unquote(file.get_uri()[7:]))
file.add_string_attribute('linecount', str(lines))
圖 4 顯示了啟用 Line Count 列的 Nautilus 窗口。每個單獨的文件顯示全部行數(shù)。您需要利用該方法進行一次計算就可以知道您總共需要多少個文件。
圖 4. Nautilus 窗口中的 Line Count 列
結(jié)束語
利用 Python 來擴展 Nautilus 的確是個簡單的過程。Python 與 Python 標(biāo)準(zhǔn)庫非常的精巧,可用于編寫高效而易讀的代碼。理解 gnome.org 站點中的文檔與示例是很有挑戰(zhàn)性的,但也不是不可能。Google 中的一些搜索結(jié)果也能提供一些例子。此處的例子可用于幫助您掌握如何擴展 Nautilus 來滿足特定需求。如果您對 Python 編程很熟悉,那將不會有什么問題。
原文:http://www.ibm.com/developerworks/cn/linux/l-script-linux-desktop-2/index.html?ca=drs
【編輯推薦】



















 
 
 



 
 
 
 