Para usuários do desktop GNOME, o programa Nautilus é, provavelmente, um dos aplicativos usados mais frequentemente. Ele trata todas as tarefas de copiar, mover, renomear e procurar arquivos com uma interface gráfica simples. À primeira vista, parece que não existem muitas coisas relacionadas a arquivos que o Nautilus não pode fazer — a não ser que você comece a pensar em tarefas que normalmente seriam feitas com um shell script.
Os desenvolvedores de Nautilus forneceram várias formas de incluir uma funcionalidade nova sem abrir a base de código principal. O método mais simples é usar um Bash ou shell script que executa uma série de comandos que você geralmente executaria a partir de um prompt de terminal. Esse método permite experimentar os comandos para se certificar de que eles façam o que você quer que eles façam primeiramente. Também é possível usar outras linguagens, como linguagem de script C, GnomeBasic, Perl e Python. Este artículo mostra como incluir novos recursos no Nautilus usando a linguagem Python. Supõe-se que o leitor tem um entendimento básico da linguagem Python e da Python Standard Library.
O primeiro método para estender o Nautilus é por meio de um diretório especial localizado em /home chamado .gnome2/nautilus-scripts. Todos os arquivos executáveis colocados nesse diretório aparecem quando você clica com o botão direito em um arquivo ou pasta sob o menu Scripts . Também é possível selecionar diversos arquivos ou pastas e passar uma lista de arquivos para um script com o mesmo método de clicar com o botão direito.
O Nautilus oferece diversas variáveis de ambiente que contêm coisas como o diretório atual e os arquivos selecionados quando um script é chamado. A Tabela 1 mostra essas variáveis de ambiente.
Tabela 1. Variáveis de ambiente do Nautilus
| Variável de ambiente | Descrição |
|---|---|
| NAUTILUS_SCRIPT_SELECTED_FILE_PATHS | Caminhos delimitados pelo caractere de nova para os arquivos selecionados (somente se são locais) |
| NAUTILUS_SCRIPT_SELECTED_URIS | URIs delimitados pelo caractere de nova linha para arquivos selecionados |
| NAUTILUS_SCRIPT_CURRENT_URI | O local atual |
| NAUTILUS_SCRIPT_WINDOW_GEOMETRY | Posição e tamanho da janela atual |
No Python, você obtém o valor dessas variáveis com uma única chamada à função os.environ.get , da seguinte forma:
selected = os.environ.get('NAUTILUS_SCRIPT_SELECTED_FILE_PATHS,'')
|
Essa chamada retorna uma cadeia de caracteres com caminhos para todos os arquivos selecionados, delimitados pelo caractere de nova linha. O Python facilita a conversão dessa cadeia de caracteres para uma lista iterável com a seguinte linha:
targets = selected.splitlines() |
Nesse ponto, talvez seja conveniente parar e falar sobre a interação com o usuário. Depois que o controle é passado do Nautilus para o script, não há restrições em relação ao que o script faz. Dependendo do que o script faz, pode ser que o feedback do usuário não seja necessário, com a exceção de algum tipo de mensagem de erro ou de conclusão, que pode ser resolvida com uma simples caixa de mensagem. Já que o Nautilus é foi escrito com o kit de ferramentas de janelas gtk, a opção de usá-lo também parece lógica, embora não seja obrigatória. Também é possível usar o TkInter ou o wxPython.
Para os fins deste artigo, você usará o gtk. A produção de uma caixa de mensagens simples para comunicar o status de conclusão requer apenas algumas linhas de código. Por uma questão de capacidade de leitura, esse código será mais adequado se você criar uma função simples para gerar a mensagem. Para fazer isso, são necessárias quatro linhas de código no total:
def alert(msg):
dialog = gtk.MessageDialog()
dialog.set_markup(msg)
dialog.run()
|
Exemplo: criar um script simples para retornar o número de arquivos selecionados
O primeiro programa de exemplo combina esses fragmentos em um script simples que retorna o número de arquivos selecionados no momento. Esse script funcionará com arquivos ou diretórios individuais. Você usará outra função de biblioteca do Python,
os.walk, para desenvolver recursivamente uma lista de arquivos em cada diretório. Um total de 38 linhas de código, mostradas na Listagem 1, são suficientes para esse pequeno utilitário — incluindo as linhas em branco.
Listagem 1. Código do Python referente ao script Filecount
#!/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()
|
A Figura 1 mostra o que você deve ver no Nautilus quando clica com o botão direito em um arquivo ou seleciona um grupo de arquivos. A opção de menu Scripts exibe todos os arquivos executáveis em .gnome2/nautilus-scripts e também oferece a opção de abrir essa pasta. Quando um dos arquivos é selecionado, o script é executado.
Figura 1. Selecionando arquivos no Nautilus
A Figura 2 mostra a saída da execução do script Filecount.py.
Figura 2. Saída de Filecount.py
Algumas coisas podem ser úteis na depuração dos scripts do Nautilus. A primeira coisa é fechar todas as instâncias do Nautilus, para permitir que ele recarregue totalmente e localize os seus novos scripts ou extensões. É possível fazer isso com o comando:
nautilus -q |
O próximo comando prático permite executar o Nautilus sem abrir a preferência ou os dados do perfil. Isso pode eliminar algumas etapas posteriormente, caso o seu script ou extensão corrompa algo inadvertidamente. Este é o comando:
nautilus -no-desktop |
A única etapa que falta para deixar o seu utilitário filecount acessível a partir do Nautilus é copiá-lo para o diretório ~/.gnome2/nautilus-scripts
e mude o modo de arquivo para permitir a execução. Você faz isso com o seguinte comando:
chmod +x Filecount.py |
Exemplo: criar um utilitário de limpeza
Como segundo exemplo, crie um utilitário de limpeza de arquivos para procurar arquivos que podem ser considerados temporários, gerados por editores como Vim ou EMACS.
Os mesmos conceitos podem ser usados para limpar um diretório de arquivos específicos simplesmente modificando a função check . Esse código se enquadra na categoria operação silenciosa , ou seja, ele executa e não fornece feedback ao usuário.
A função principal desse script é praticamente igual ao exemplo anterior, com algumas pequenas exceções. Esse código usa o conceito de recursão para chamar a função principal diversas vezes até que o ultimo diretório tenha sido percorrido. Você poderia usar a função os.walk para realizar a mesma tarefa sem usar a recursão. A verificação dos arquivos ocorre na função
check e simplesmente procura arquivos que terminam com um til (~) ou sinal de sustenido (#),
começam com o sinal de sustenido ou terminam com a extensão .pyc.
Esse exemplo mostra o grande número de funções que o módulo da Python Standard
Library chamado os fornece. Também é um bom exemplo de uma forma independente do sistema operacional de manipular nomes de caminhos e diretórios e realizar operações em arquivos.
A Listagem 2 mostra o código referente a esse script.
Listagem 2. Código do Python referente ao script de limpeza
#!/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()
|
O segundo método para aprimorar o Nautilus é por meio da criação de extensões. Esse método é um pouco mais complexo que o primeiro, mas proporciona mais benefícios. As extensões do Nautilus podem ser integradas à janela de exibição de arquivos, para que você possa escrever uma extensão que preencherá uma coluna com informações anteriormente indisponíveis. Uma das primeiras coisas que talvez você precise fazer é instalar todas as extensões python-nautilus com o seguinte comando:
sudo apt-get install python-nautilus |
Esse comando faz o download e instala os arquivos necessários, inclusive a documentação e os exemplos. Você encontra o código de amostra no diretório /usr/share/doc/python-nautilus/examples. Após a instalação, você tem acesso a um conjunto de classes e provedores do Nautilus para programar. A Tabela 2 mostra a lista.
Tabela 2. Classes e provedores do Nautilus
| Classe ou provedor | Descrição |
|---|---|
nautilus.Column | Referência ao objeto column do Nautilus |
nautilus.FileInfo | Referência ao objeto fileinfo do Nautilus |
nautilus.Menu | Referência ao objeto menu do Nautilus |
nautilus.MenuItem | Referência ao objeto menuitem do Nautilus |
nautilus.PropertyPage | Referência ao objeto propertypage do Nautilus |
nautilus.ColumnProvider | Permite que a saída seja exibida em uma coluna do Nautilus |
nautilus.InfoProvider | Fornece informações sobre o arquivo |
nautilus.LocationWidgetProvider | Exibe o local |
nautilus.MenuProvider | Inclui uma funcionalidade nova no menu de clique com o botão direito |
nautilus.PropertyPageProvider | Inclui informações na página de propriedade |
Os exemplos fornecidos no site gnome.org demonstram o uso de
MenuProvider (background-image.py e
open-terminal.py), ColumnProvider e
InfoProvider (block-size-column.py) e
PropertyPageProvider (md5sum-property-page.py). O ColumnProvider usa 13 linhas de código executável do Python para introduzir uma coluna nova no Nautilus. Depois que esse código é colocado no diretório adequado (~/.nautilus/python-extensions) e o Nautilus é reiniciado, você deve ver uma nova opção ao clicar em View > Visible Columns.
A opção Visible Columns só aparece quando você configura o tipo de visualização como get(). A ativação da coluna Block size
ao marcar a caixa de seleção exibe os resultados da seguinte chamada à biblioteca do Python:
str(os.stat(filename).st_blksize)) |
O padrão básico de qualquer extensão do Python é transformar em subclasse a classe base do provedor do Nautilus e, em seguida, executar uma série de instruções que, no final, retornará o objeto apropriado do Nautilus. No exemplo block-size-column.py, o objeto retornado é nautilus.Column. É necessário passar quatro parâmetros de volta para o Nautilus, incluindo name,
attribute, label e
description. O código do Python referente a esse exemplo é:
return nautilus.Column("NautilusPython::block_size_column",
"block_size",
"Block size",
"Get the block size")
|
A codificação de uma nova extensão envolve o ato de herdar as informações necessárias das classes base especificadas. No exemplo block-size-column.py,
nautilus.ColumnProvider e
nautilus.InfoProvider são enumerados na definição da classe — portanto, a nova classe herda de ambos. A próxima coisa que deve ser feita é substituir todos os métodos da(s) classe(s) base para preencher a coluna. Você faz isso no exemplo block-size-column.py substituindo os métodos get_columns e
update_file_info
.
A passagem de informações para uma extensão do Nautilus funciona de forma diferente do script.
O Nautilus ativa, de fato, um novo processo para executar um script e configura diversas variáveis de ambiente para passar informações. As extensões executam no mesmo processo que o Nautilus e, portanto, tem acesso a objetos, métodos e atributos.
As informações sobre um arquivo são passadas por meio do objeto nautilus.FileInfo
, inclusive coisas como file_type,
location, name,
uri e mime_type. Para incluir informações no objeto FileInfo , é necessário chamar o método add_string_attribute . Você usará essa abordagem no exemplo a seguir para incluir novos atributos no objeto FileInfo .
Exemplo: mostrar o número de linhas de um arquivo
O primeiro exemplo usa o método PropertyPageProvider
para mostrar o número de linhas e caracteres quando você clica com o botão direito em um arquivo (ou mais) e depois clica em Properties. A ideia básica por trás dessa extensão é contar o número de linhas e caracteres de um arquivo e relatar os resultados em uma nova guia da página de propriedades do arquivo.
As extensões possuem acesso direto às estruturas de dados do Nautilus, inclusive o objeto file . A única coisa que você precisa fazer é desempacotar o nome usando a função de biblioteca urllib.unquote da seguinte forma:
filename = urllib.unquote(file.get_uri()[7:] |
Algumas linhas de código do Python fazem o trabalho principal de contar as linhas e os caracteres. Nesse exemplo, você cria uma função count
para ler o arquivo inteiro em uma cadeia de caracteres grande e, em seguida, contar o número total de caracteres e o número de caracteres de nova linha. Já que é possível exibir a página de propriedade de vários arquivos e diretórios selecionados, é necessário fazer provisões para contar diversos arquivos. Neste ponto, só falta incluir os resultados em uma nova página na página de propriedade. Este exemplo cria um gtk.Hbox simples e, em seguida, preenche várias etiquetas com as informações coletadas, como mostra a Listagem 3.
Listagem 3. O arquivo 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),
|
A Figura 3 mostra o resultado de clicar com o botão direito em um arquivo e clicar na guia Linecount . Neste ponto, é importante ressaltar que esse recurso funciona em arquivos individuais ou em qualquer grupo de arquivos e diretórios selecionados. O número relatado representará todas as linhas de todos os arquivos.
Figura 3. Clicando na guia Linecount para ver o número de linhas de um arquivo
Finalmente, altere o seu utilitário de extensão para preencher uma coluna em vez de uma página de propriedade. As modificações no código são razoavelmente pequenas, mas é realmente necessário herdar de nautilus.ColumnProvider e
nautilus.InfoProvider. Também é necessário implementar get_columns e update_file_info.
O método get_columns simplesmente retorna as informações coletadas pelo método count .
O método count usa uma técnica diferente para a extensão de provedor de coluna. A rotina readlines do Python é usada para ler todas as linhas de um arquivo e colocá-las em uma lista de cadeias de caracteres. A contagem do número total de linhas é simplesmente o número de elementos da lista retornada com a instrução len(s) . Isso não quer dizer que a verificação de tipo de arquivo ocorre nos dois exemplos — só faz sentido contar arquivos de texto que realmente possuem linhas a serem contadas. Você cria uma lista de extensões de arquivo aceitáveis com a linha:
types = ['.py','.js','.html','.css','.txt','.rst','.cgi'] |
Uma segunda lista contém exceções que não serão contadas. Para esse exemplo, exclua um único arquivo com a linha:
exceptions = ['MochiKit.js'] |
Em seguida, essas duas listas são usadas para incluir ou excluir arquivos com as duas linhas de código a seguir:
if ext not in types or basename in exceptions:
return 0
|
A extensão inteira requer um total de 26 linhas de código executável. É conveniente modificar as listas de exceções e tipos para incluir ou excluir os arquivos do seu interesse. A Listagem 4 mostra a extensão concluída.
Listagem 4. Código do Python referente à extensão Linecountcolumn
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))
|
A Figura 4 mostra uma janela do Nautilus com a coluna Line Count habilitada. Cada arquivo individual terá o número total de linhas exibido. Você terá que fazer algumas contas ao usar esse método caso precise obter um total de diversos arquivos.
Figura 4. A coluna Line Count em uma janela do Nautilus
Estender o Nautilus com o Python é realmente um processo objetivo. A beleza e elegância do Python e da Python Standard Library possibilitam um código eficiente e legível. A navegação na documentação e nos exemplos do site gnome.org pode ser desafiadora, mas não é impossível. Algumas procuras no Google também proporcionarão exemplos adicionais. Espera-se que os exemplos deste artigo deem ideias de como estender o Nautilus para suprir necessidades específicas. Se você está familiarizado com a codificação em Python, não terá dificuldades.
Aprender
-
Saiba mais sobre o Nautilus.
-
Saiba mais sobre as extensões Nautilus-python.
-
Você encontra mais recursos sobre o Python em Python.org.
- Na zona Linux do developerWorks, encontre vários artigos de instruções e tutoriais, bem como downloads, fóruns de discussão e muitos outros recursos para desenvolvedores e administradores Linux.
- Fique por dentro dos eventos técnicos e webcasts do developerWorks com foco em uma variedade de produtos da IBM e tópicos do segmento de mercado de TI.
- Participe de um briefing ao vivo e gratuito do developerWorks para se atualizar rapidamente sobre produtos e ferramentas da
IBM, bem como tendências do segmento de mercado de TI.
- Assista a demos on-demand do developerWorks , que abrangem desde demos de instalação e configuração de produtos para iniciantes até funcionalidades avançadas para desenvolvedores experientes.
- Siga o developerWorks no
Twitter, ou inscreva-se em um feed de Linux tweets no developerWorks.
Obter produtos e tecnologias
-
Avalie produtos
IBM da maneira que for melhor para você: Faça o download da avaliação de um produto, experimente um produto
on-line, use um produto em um ambiente de nuvem ou passe algumas horas no
SOA Sandbox aprendendo como implementar Arquitetura Orientada
a Serviços de forma eficiente.
Discutir
- Participe da comunidade do My developerWorks
. Conecte-se a outros usuários do developerWorks, ao mesmo tempo que explora os blogs, fóruns, grupos e wikis direcionados a desenvolvedores.
Paul Ferrill escreve nos meios de comunicação de comércio de computadores há mais de 20 anos. Começou escrevendo revisões de rede para o PC Magazine sobre produtos como LANtastic e versões anteriores do Novell Netware. Paul é graduado e possui mestrado em engenharia elétrica e escreveu softwares para mais arquiteturas e plataformas de computador do que consegue se lembrar.