<?xml version="1.0" encoding="utf-8"?>
<page xmlns="http://projectmallard.org/1.0/" type="topic" id="howto_write_a_plugin" xml:lang="pt_BR">
  <info>
    <link type="guide" xref="index#advanced"/>
    <title type="sort">3. Escrevendo novos plug-ins</title>
    <link type="next" xref="preferences"/>
    <desc>Estendendo <app>Accerciser</app> com suas funcionalidades desejadas</desc>
    <credit type="author">
      <name>Eitan Isaacson</name>
      <email>eitan@ascender.com</email>
    </credit>
    <credit type="author">
      <name>Peter Parente</name>
      <email>pparent@us.ibm.com</email>
    </credit>
    <credit type="author">
      <name>Aline Bessa</name>
      <email>alibezz@gmail.com</email>
    </credit>
    <license>
      <p>Atribuição Compartilhada Igual 3.0 - Creative Commons</p>
    </license>
  
    <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
      <mal:name>Enrico Nicoletto</mal:name>
      <mal:email>liverig@gmail.com</mal:email>
      <mal:years>2008.</mal:years>
    </mal:credit>
  
    <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
      <mal:name>Fabrício Godoy</mal:name>
      <mal:email>skarllot@gmail.com</mal:email>
      <mal:years>2008.</mal:years>
    </mal:credit>
  
    <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
      <mal:name>Leonardo Ferreira Fontenelle</mal:name>
      <mal:email>leonardof@gnome.org</mal:email>
      <mal:years>2008.</mal:years>
    </mal:credit>
  
    <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
      <mal:name>Rafael Fontenelle</mal:name>
      <mal:email>rafaelff@gnome.org</mal:email>
      <mal:years>2025.</mal:years>
    </mal:credit>
  </info>
  <title>Escrevendo plug-ins para o <app>Accerciser</app></title>
  <p>Estender o <app>Accerciser</app> com novos plug-ins ficou muito mais simples desde que <link href="https://wiki.gnome.org/Apps/Accerciser/PluginTutorial">este tutorial</link> foi escrito. Dado que ele fornece uma explicação muito detalhada sobre o tópico, esta seção consiste basicamente em seu conteúdo original - apenas edições e atualizações simples foram feitas.</p>
  <p><app>Accerciser</app> oferece suporte a três tipos básicos de plug-ins:</p>
  <list>
    <item>
      <p>Plug-ins base - Esses plug-ins são derivados da classe base Plugin. Eles não fornecem uma interface visível, mas podem fornecer funcionalidade adicional ao <app>Accerciser</app>.</p>
    </item>
    <item>
      <p>Plug-ins de console - Esses plug-ins fornecem saída de console simples em uma área de texto em uma aba de plug-in. Não deve ser confundido com o <link xref="ipython_plugin">plug-in Console IPython</link> empacotado.</p>
    </item>
    <item>
      <p>Plug-ins de viewport - A maioria dos plug-ins padrão do <app>Accerciser</app>. Eles fornecem uma interface gráfica personalizada em uma aba.</p>
    </item>
  </list>

  <section id="base_plugin">
    <title>Criando um plug-in base</title>
    <p>Criaremos uma versão simplificada do <link xref="quick_select_plugin">plug-in Seleção rápida</link>. Este plug-in selecionará o último foco acessível ao pressionar <keyseq><key>ctrl</key><key>alt</key><key>e</key></keyseq>.</p>
    <p>Primeiramente, as linhas de importação que usaremos são:</p>
    <code>
      from accerciser.plugin import Plugin
      import gtk
      import pyatspi
    </code>
    <p>Em seguida, derivaremos uma nova classe da classe base Plugin e atribuiremos alguns atributos de classe obrigatórios:</p>
    <code>
      class FocusSelect(Plugin):
        plugin_name = 'Focus Select'
        plugin_description = 'Allows selecting last focused accessible.'
    </code>
    <p>Agora, substituiremos o método init, no qual definiremos uma ação de tecla global para selecionar o último item acessível em foco, registraremos um ouvinte de evento para o evento "focus" e definiremos a variável de instância <cmd>last_focused</cmd> como <cmd>None</cmd>.</p>
    <code>
      def init(self):
        pyatspi.Registry.registerEventListener(self.accEventFocusChanged, 'focus')
        self.global_hotkeys = [('Inspect last focused accessible',
                                self.inspectLastFocused,
                                gtk.keysyms.e,
                                gtk.gdk.CONTROL_MASK | gtk.gdk.MOD1_MASK)]
        self.last_focused = None
    </code>
    <p>Observe que a variável de instância <cmd>global_hotkeys</cmd> é uma lista de tuplas. Cada tupla é uma ação de tecla de atalho global, composta por uma descrição de ação, um método desejado para chamar, um símbolo de tecla de pressionamento de tecla e uma máscara de modificador de tecla.</p>
    <p>No retorno de chamada do evento "focus", atribuímos à variável de instância <cmd>last_focused</cmd> o item acessível que acabou de emitir o evento "focus".</p>
    <code>
      def accEventFocusChanged(self, event):
        if not self.isMyApp(event.source):
          self.last_focused = event.source
    </code>
    <p>No retorno de chamada da ação de tecla de atalho, atualizamos todo o nó do aplicativo com o último item acessível em foco, se o tivermos registrado:</p>
    <code>
      def inspectLastFocused(self):
        if self.last_focused:
          self.node.update(self.last_focused)
    </code>
  </section>
  <section id="console_plugin">
    <title>Criando um plug-in de console</title>
    <p>Vamos criar um plug-in de console para exibir as alterações de foco emitidas por um item acessível com uma função de "botão de pressionamento" - lembre-se de que é fácil verificar qual é a função de qualquer item com o <app>Accerciser</app>; você pode verificar isso na <link xref="desktop_tree_view">Visão em Árvore de Aplicativos</link>, por exemplo.</p>
    <p>As linhas de importação necessárias são:</p>
    <code>
      from accerciser.plugin import ConsolePlugin
      import pyatspi
    </code>
    <p>Em seguida, adicionamos uma definição de classe, com um nome de plug-in e uma descrição:</p> 
    <code>
      class PushButtonFocus(ConsolePlugin):
        plugin_name = 'Push Button Focus'
        plugin_description = 'Print event when pushbutton get\'s focus.'
    </code>
    <p>Substituímos o método init adicionando um ouvinte de registro:</p>
    <code>
       def init(self):
         pyatspi.Registry.registerEventListener(self.accEventFocusChanged, 'focus')
    </code>
    <p>No método de retorno de chamada, todos os eventos de botão de pressionamento são impressos.</p>
    <code>
      def accEventFocusChanged(self, event):
        if event.source.getRole() == pyatspi.ROLE_PUSH_BUTTON:
          self.appendText(str(event)+'\n')
    </code>
  </section>
  <section id="viewport_plugin">
    <title>Criando um plug-in de viewport</title>
    <p>Vamos criar um plug-in de viewport que permite testes rápidos da ação "click" em itens acessíveis que suportam a interface Action do AT-SPI e têm uma ação chamada "clique". Será um botão simples que, uma vez clicado, faz a ação "click" no acessível.</p>
    <p>Primeiramente, algumas linhas de importação obrigatórias:</p>
    <code>
      import gtk
      from accerciser.plugin import ViewportPlugin
    </code>
    <p>A seguir, uma definição de classe, com um nome e uma descrição:</p>
    <code>
      class Clicker(ViewportPlugin):
        plugin_name = 'Clicker'
        plugin_description = 'Test the "click" action in relevant accessibles.'
    </code>
    <p>Substituímos o método init com alguma construção de UI e conectamos um retorno de chamada a um sinal para o botão. Definimos o alinhamento para permitir que o botão seja centralizado na aba do plug-in e não ocupe monstruosamente todo o espaço do plug-in. Observe que a variável de instância <cmd>plugin_area</cmd> contém um gtk.Frame que pode ser preenchido com todos os widgets do plug-in.</p>
    <code>
       def init(self):
         self.click_button = gtk.Button('Click me!')
         self.click_button.set_halign(gtk.Align.CENTER)
         self.click_button.set_valign(gtk.Align.CENTER)
         self.plugin_area.add(self.click_button)

         self.click_button.connect('clicked', self.onClick)

         self.show_all()
    </code>
    <p>Também criamos um método de conveniência que retorna uma lista de ações suportadas pelo item acessível selecionado no momento. Se ele não suportar a interface Action, ele retornará uma lista vazia:</p> 
    <code>
       def accSupportedActions(self):
       try:
         ai = self.node.acc.queryAction()
       except NotImplementedError:
         action_names = []
       else:
         action_names = [ai.getName(i) for i in xrange(ai.nActions)]
       return action_names
    </code>
    <p>A classe base do plug-in tem uma chamada de método onAccChanged que é chamada toda vez que o item acessível selecionado do aplicativo alvo muda. Nós a substituiremos definindo o botão para ser sensível somente quando o item acessível atual tiver a ação "click":</p>
    <code>
       def onAccChanged(self, acc):
         has_click = 'click' in self.accSupportedActions()
         self.click_button.set_sensitive(has_click)
    </code>
    <p>O método de retorno de chamada para o botão "clicked" executa a ação "click" no item acessível. Como esse retorno de chamada só pode ser chamado quando o botão é sensível, não precisamos nos preocupar em verificar se o acessível atual tem a ação "click"?</p>
    <code>
      def onClick(self, button):
        ai = self.node.acc.queryAction()
        action_names = [ai.getName(i) for i in xrange(ai.nActions)]
        ai.doAction(action_names.index('click'))
    </code>
   </section>
</page>
