怎样编写linux桌面,编写 Linux 桌面脚本: 编写 Nautilus 脚本

对于 GNOME 桌面用户来说,Nautilus

编程可能是更比较常用的应用程序之一。它能够通过简单的图形界面,来处理所有的文件复制、移动、重命名、以及搜索的问题。从表面上看,似乎不存在

Nautilus 不能处理的文件相关事务 — 除非考虑执行具有 shell 脚本的任务。

Nautilus

开发工具提供了多个不必打开主代码库而增加新功能的方法。最简单的方法是使用能执行那些通常在终端提示符上执行的命令的 bash 或者

bash 脚本。该方法使得尝试使用这一命令来确保他们完成想要首先完成的任务。还可以采用其他语言,包括 C

脚本语言、GnomeBasic、Perl、以及 Python。本文介绍如何利用 Python 语言来为 Nautilus

增加新功能。假定读者已对 Python 语言及 Python 标准库有所了解。

扩展 Nautilus 的第一个方法是通过在 /home 中发现的名为

.gnome2/nautilus-scripts 的特定目录。当在 Scripts

菜单下的文件或者文件夹上点击鼠标右键时,该目录下所有可执行文件将会出现。还可以选择多个文件或者文件夹,并采用相同的右击方法,将文件清单传递给脚本。

当调用脚本时,Nautilus 支持多个包含当前目录以及所选文件等内容的环境变量。表 1 展示了这些环境变量。

表 1. Nautilus

环境变量

环境变量

描述

NAUTILUS_SCRIPT_SELECTED_FILE_PATHS

所选文件的新行分割路径(仅针对本地)

NAUTILUS_SCRIPT_SELECTED_URIS

所选文件的新行分割 URIs

NAUTILUS_SCRIPT_CURRENT_URI

当前位置

NAUTILUS_SCRIPT_WINDOW_GEOMETRY

当前窗口的位置和大小

在 Python 中,通过对 os.environ.get

函数的一个调用来获取这些变量的值,具体如下:

selected = os.environ.get('NAUTILUS_SCRIPT_SELECTED_FILE_PATHS,'')

此调用返回一个字符串,其包含到达由换行符分隔的全部所选文件。Python

利用下列代码,简化了将这一字符串返回到可迭代列表中的操作:

targets = selected.splitlines()

此时,也许应该停下来探讨一下用户交互。当控制从 Nautilus

传送到脚本后,在该点上确实不存在对脚本的限制。根据脚本作用的不同,甚至不需要任何用户反馈,除了一些类型的完成或错误消息,这样通过一些简单的消息框

就可处理好。由于在编写 Nautilus 时采用了 gtk windowing

工具包,所以尽管这不是必须的,但是采用相同的做法很合乎逻辑。您可以很方便地使用 TkInter 或者 wxPython。

鉴于本文的目的,您将采用

gtk。生成一个用于通信完成状态的简单消息框,仅需几行代码,出于方便阅读的目的,如果想创建简单的函数来生成消息,这个代码将最为合适。总共需要

4 行代码:

def alert(msg):

dialog = gtk.MessageDialog()

dialog.set_markup(msg)

dialog.run()

第一个示例程序将多个程序段合并成一个简单脚本,来返回当前所选文件的数量。这一脚本可用于文件或者目录。可利用另一个 Python

库函数,os.walk,递归地构建每个目录中文件的清单。总共有 38 行代码,如清单 1

所示,这就是这一小工具所需的全部内容,其中还包括了空行。

清单 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 展示了当在文件上右击鼠标或者选择一组文件时所看到的内容。Scripts 菜单选项展示

.gnome2/nautilus-scripts 中所有的可执行文件,并给出了打开文件夹的选项。选择一个文件来执行该脚本。

图 1.在 Nautilus

中选择文件

a4c26d1e5885305701be709a3d33442f.png

图 2 展示了 Filecount.py 脚本的运行结果。

图 2. Filecount.py

输出

a4c26d1e5885305701be709a3d33442f.png

在调试 Nautilus 脚本时,有几件事需要注意。第一件事是关闭 Nautilus

的所有实例,来使它完全重新加载,并找到新脚本或者扩展。可采用如下命令:

nautilus -q

下一个常用命令可实现不必打开首选或者配置数据,而直接运行

Nautilus。这在解决脚本或者扩展在无意间造成破坏之类的问题时,会节省很多步骤。命令如下:

nautilus -no-desktop

确保 filecount 工具可被 Nautilus 访问所剩的最后一步是将其复制到

~/.gnome2/nautilus-scripts 目录,并改变文件代码来允许执行,相关命令是:

chmod +x Filecount.py

第二个例子是,创建文件 cleanup 工具,来查找任何可能由 Vim 或者 EMACS

之类的编辑器临时生成的文件。仅通过简单地修改 check

函数,就可利用相同的概念来清除任何特定文件的目录。这一代码属于静默操作,这意味着它执行后不向用户提供任何反馈。

该脚本的主函数看上去基本与前面具的示例相同,除了几个微不足道的异常。此代码会利用递归概念多次调用主函数,直至处理完最后一个目录为止。您可以采用

os.walk 函数,而不必采用递归来完成相同的任务。文件检查发生在 check

函数中,仅简单地检查以波浪号(~)或者井号(#)结束的文件,以井号后开始或扩展名

.pyc 结束的文件。该示例展示了 Python 标准库 os

模块所提供的数量众多的函数。它还提供了独立于操作系统方式来操作路径名和目录,以及执行文件操作的示例。清单 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()

增强 Nautilus 的第二个方法是通过创建扩展。此方法比第一个复杂,但有很多优势。Nautilus

扩展可被内嵌到文件展示窗口中,那么就可以编写利用以前没有的信息来填充列的扩展。首先要做的就是利用如下命令安装

python-nautilus 扩展:

sudo apt-get install python-nautilus

此命令下载并安装所需的文件,包括文档和示例。可在目录

/usr/share/doc/python-nautilus/examples 中到找到示例代码。安装完成后,就可以访问一组

Nautilus 类和提供程序来再次对其进行编码。表 2 展示了该清单。

表 2. Nautilus

类与供应商

类或者供应商

描述

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

提供关于文件的信息

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 可执行代码来向 Nautilus

引入新的列。一旦该代码被放置到合适的目录中(~/.nautilus/python-extensions)并且 Nautilus

已重启,在单击 View > Visible Columns

时将会看到新的选项。当将查看类型设置为 List 时,才会出现 Visible

Columns 选项。通过选择展示以下 Python 库调用结果的复选框,来启用 Block

size 列:

str(os.stat(filename).st_blksize))

任何 Python 扩展的基本模式都是对现有 Nautilus 提供程序基本类进行子类划分,然后执行一系列指令,并最终返回合适的

Nautilus 对象。在 block-size-column.py 例子中,返回的对象是

nautilus.Column。必须向 Nautilus 传递 4 个参数,包括

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

实际上是启动新的流程来执行脚本,并设置多个环境变量来传递信息。在与 Nautilus

相同的流程中执行的扩展,能够访问对象、方法、和属性。通过 nautilus.FileInfo

传递的文件信息,包括

file_type、location、name、uri、以及

mime_type。想要向 FileInfo 对象增加信息,必须调用

add_string_attribute 方法。下面的例子是采用这一方法,来向

FileInfo 对象增加新的属性。

第一个例子使用 PropertyPageProvider

方法在文件(或多个文件)上单击右键显示行数和参数,然后单击

Properties。这一扩展背后的基本思想是计算文件中的行数和参数个数,并在文件属性页的新选项卡中报告结果。扩展可以直接访问了

Nautilus 数据结构,包括 file 对象。惟一要做的是利用

urllib.unquote 库函数来打开名字,操作如下:

filename = urllib.unquote(file.get_uri()[7:]

Python 中的一些行完成了对行及参数计数的主要工作。对于本例来说,创建 count

函数来将整个文件读取到一个大字符串中,然后计算参数数量及新添参数数量。因为属性页面可被显示为很多选中的文件及目录,所以必须预先计算多个文件。此时,惟一要做的就是将结果添加到属性页上的新页中。本例创建了示例

gtk.Hbox,然后利用获取的信息来填充大量标签,如清单 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

选项卡的结果。此时,需要注意,这一特性可用于文件或者任何一组选定的文件和目录。所报告的数字将代表所有文件中的所有行。

图 3. 单击 Linecount

选项卡来查看文件的行数

a4c26d1e5885305701be709a3d33442f.png

最后,修改扩展函数来填充一列而不是整个属性页。因而代码的修改相当少,尽管需要同时从

nautilus.ColumnProvider 和

nautilus.InfoProvider 继承。还必须执行

get_columns 和 update_file_info。方法

get_columns 仅返回由方法 count 获取的信息。

方法 count 为列提供程序扩展采用不同的技术。Python 的

readlines 例程用于将一个文件的所有行读取到一列字符串中。计算行的总数就是在

len(s)

语句中返回的清单元素的数量。在两个例子中都要进行文件类型检查:这是要确保仅对包含需要计数行的文本文件进行计数。可利用如下行来创建一列可接受的文件扩展:

types = ['.py','.js','.html','.css','.txt','.rst','.cgi']

第二个清单包含了不会被计数的异常,对于本例来说,包含具有如下行的文件:

exceptions = ['MochiKit.js']

这两个清单用于包括或者排除具有如下两行代码的文件:

if ext not in types or basename in exceptions:

return 0

整个扩展需要 26 行可执行代码。您可能想要修改扩展,并输入清单来包含或者排除感兴趣的文件,清单 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

窗口。每个单独的文件显示全部行数。您需要利用该方法进行一次计算就可以知道您总共需要多少个文件。

图 4. Nautilus 窗口中的 Line Count

a4c26d1e5885305701be709a3d33442f.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值