Scripts no Desktop do Linux, Parte 2: Scripts no Nautilus

Usando o Python para expandir e estender o Nautilus

Esta série de artigos explora como usar Python para criar scripts para o desktop GNOME, a estrutura de screenlets e Nautilus para fornecer um ambiente altamente produtivo. Scripts no desktop ativam a funcionalidade de arrastar e soltar e o acesso rápido às informações e aos serviços comumente usados. Nesta parte, aprenda a usar o Python para incluir funcionalidades a fim de estender o Nautilus no seu desktop.

Paul Ferrill, CTO, ATAC

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.



16/Mar/2011

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.

Scripts no Nautilus

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 ambienteDescrição
NAUTILUS_SCRIPT_SELECTED_FILE_PATHSCaminhos delimitados pelo caractere de nova para os arquivos selecionados (somente se são locais)
NAUTILUS_SCRIPT_SELECTED_URISURIs delimitados pelo caractere de nova linha para arquivos selecionados
NAUTILUS_SCRIPT_CURRENT_URIO local atual
NAUTILUS_SCRIPT_WINDOW_GEOMETRYPosiçã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
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
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()

Extensões do Nautilus

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 provedorDescrição
nautilus.ColumnReferência ao objeto column do Nautilus
nautilus.FileInfoReferência ao objeto fileinfo do Nautilus
nautilus.MenuReferência ao objeto menu do Nautilus
nautilus.MenuItemReferência ao objeto menuitem do Nautilus
nautilus.PropertyPageReferência ao objeto propertypage do Nautilus
nautilus.ColumnProviderPermite que a saída seja exibida em uma coluna do Nautilus
nautilus.InfoProviderFornece informações sobre o arquivo
nautilus.LocationWidgetProviderExibe o local
nautilus.MenuProviderInclui uma funcionalidade nova no menu de clique com o botão direito
nautilus.PropertyPageProviderInclui 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
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
A coluna Line Count em uma janela do Nautilus

Concluindo

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.

Recursos

Aprender

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.

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Linux
ArticleID=632761
ArticleTitle=Scripts no Desktop do Linux, Parte 2: Scripts no Nautilus
publish-date=03162011