Accerciser Plugin Tutorial

Accerciser supports three basic types of plugins.

  1. Base plugins - These plugins are derived from the Plugin base class. They do not provide a visible interface, but could provide additional functionality to Accerciser.

  2. Console plugins - These plugins provide simple console output in to a text area in a plugin tab. Not to be confused with the packaged IPython console.

  3. Viewport plugins - The majority of Accerciser plugins. They provide a custom graphical interface in a tab.

Plugin python source files will automatically be loaded by Accerciser from two locations, either in prefix/share/accerciser/plugins, or in ~/.accerciser/plugins.

In the following sections we will demonstrate how to use each plugin type.

Base plugin

We will create a simplified version of the Quick Select plugin. This plugin will select the last focused accessible when pressing control+alt+e. The entire plugin source could be downloaded here: selector_plugin.py.

First off, here are the import lines we will use:

   1 from accerciser.plugin import Plugin
   2 import gtk
   3 import pyatspi

Next we will derive a new class from the Plugin base class, and assign some mandatory class attributes:

   1 class FocusSelect(Plugin):
   2   plugin_name = 'Focus Select'
   3   plugin_description = 'Allows selecting last focused accessible.'

We will now override the init method, in it we will set a global key action for selecting the last focused accessible, register an event listener for the "focus" event, and set the last_focused instance variable to None.

   1   def init(self):    
   2     pyatspi.Registry.registerEventListener(self.accEventFocusChanged, 
   3                                            'focus')
   4 
   5     self.global_hotkeys = [('Inspect last focused accessible',
   6                             self.inspectLastFocused, 
   7                             gtk.keysyms.e,
   8                             gtk.gdk.CONTROL_MASK | gtk.gdk.MOD1_MASK)]
   9 
  10     self.last_focused = None

Notice that the global_hotkeys instance variable is a list of tuples, each tuple is one global hotkey action, and it is a list of an action description, desired method to call, key symbol of keypress, and a key modifier mask.

In the "focus" event callback we assign the last_focused instance variable with the accessible that just just emitted the "focus" event.

   1   def accEventFocusChanged(self, event):
   2     if not self.isMyApp(event.source):
   3       self.last_focused = event.source

In the hotkey action callback we update the application wide node with the last focused accessible, if we have recorded it:

   1   def inspectLastFocused(self):
   2     if self.last_focused:
   3       self.node.update(self.last_focused)

Console plugin

The console plugin type allows the display of output on any kind of event, an Accerciser selection change, a gobject timeout, or an AT-SPI event. In this example we will use this plugin type to display focus changes that are emitted by an accessible with a "push button" role. The complete plugin source could be downloaded here: pushbutton_event.py.

The needed import lines:

   1 from accerciser.plugin import ConsolePlugin
   2 import pyatspi

The class definition, with a plugin name and description:

   1 class PushButtonFocus(ConsolePlugin):
   2   plugin_name = 'Push Button Focus'
   3   plugin_description = 'Print event when pushbutton get\'s focus.'

The init method override, here we connect any signals, or register listeners:

   1   def init(self):
   2     pyatspi.Registry.registerEventListener(self.accEventFocusChanged, 
   3                                            'focus')

The event callback, if the event source is a push button, print the event.

   1   def accEventFocusChanged(self, event):
   2     if event.source.getRole() == pyatspi.ROLE_PUSH_BUTTON:
   3       self.appendText(str(event)+'\n')

Viewport plugin

We will create a plugin that allows quick testing of the "click" action in accessibles that support the Action interface, and have an action named "click". It will be a simple button that could be clicked, and guess what happens when you click it? That's right, it does the "click" action in the accessible. The full source of this plugin could be found here: clicker plugin.py.

First off, some mandatory import lines:

   1 import gtk
   2 from accerciser.plugin import ViewportPlugin

Next, a class definition, with a name and description:

   1 class Clicker(ViewportPlugin):
   2   plugin_name = 'Clicker'
   3   plugin_description = 'Test the "click" action in relevant accessibles.'

We override the init method with some UI building, and connecting a callback to a signal for the button. We use the alignment container to allow the button to be centered in the plugin tab, and not monstrously take up the entire plugin space. Notice the plugin_area instance variable, this contains a gtk.Frame that could be populated with all the plugin's widgets.

   1   def init(self):
   2     alignment = gtk.Alignment(0.5,0.5,0,0)
   3     self.click_button = gtk.Button('Click me!')
   4     alignment.add(self.click_button)
   5     self.plugin_area.add(alignment)
   6 
   7     self.click_button.connect('clicked', self.onClick)
   8 
   9     self.show_all()

We also created a convenience method that returns a list of supported actions of the currently selected accessible, of course if the accessible does not support the Action interface, it returns an empty list:

   1   def accSupportedActions(self):
   2     try:
   3       ai = self.node.acc.queryAction()
   4     except NotImplementedError:
   5       action_names = []
   6     else:
   7       action_names = [ai.getName(i) for i in xrange(ai.nActions)]
   8     return action_names

The base plugin class has a method call onAccChanged that is called every time the main application's selected accessible changes, we could override it for our own needs, in this case we need to set the button to sensitive only when the current accessible has the "click" action.

   1   def onAccChanged(self, acc):
   2     has_click = 'click' in self.accSupportedActions()
   3     self.click_button.set_sensitive(has_click)

The button "clicked" callback performs the "click" action on the accessible. Since this callback could only be called when the button is sensitive, we don't need to worry about checking if the current accessible has the "click" action.

   1   def onClick(self, button):
   2     ai = self.node.acc.queryAction()
   3     action_names = [ai.getName(i) for i in xrange(ai.nActions)]
   4     ai.doAction(action_names.index('click'))

Apps/Accerciser/PluginTutorial (last edited 2013-11-18 18:28:11 by WilliamJonMcCann)