# 3. Psaní nových zásuvných modulů

Jak Accerciser rozšířit o svoji požadovanou funkcionalitu.
Eitan Isaacson
eitan@ascender.com
Peter Parente
pparent@us.ibm.com
Aline Bessa
alibezz@gmail.com

Creative Commons Share Alike 3.0

Lucas Lommer
llommer@svn.gnome.org 2008, 2010.
Marek Černocký
marek@manet.cz 2009, 2015.

# Psaní zásuvných modulů pro Accerciser

Rozšíření funkcí aplikace Accerciser pomocí nových zásuvných modulů je teď mnohem snažší díky tomuto průvodci (odkazovaný text je v angličitně). Vzhledem k tomu, že poskytuje velmi podrobné vysvětlení tématu, vychází tato část v podstatě z jeho původního obsahu – byly provedeny jen drobné úpravy a aktualizace.

Accerciser podporuje tři hlavní typy zásuvných modulů:

Základní zásuvné moduly – Tyto zásuvné moduly jsou odvozeny od základní třídy Plugin. Neposkytují viditelné uživatelské rozhraní, ale mohou v aplikaci Accerciser poskytnout dodatečnou funkcionalitu.

Konzolové zásuvné moduly – Tyto zásuvné moduly poskytují jednoduchý konzolový výstup do textové oblasti na kartě zásuvného modulu. Neplést si s balíčkem Konzolový zásuvný modul IPython.

Okenní zásuvné moduly – Většina výchozích zásuvných modulů aplikace Accerciser. Poskytují vlastní grafické rozhraní v kartě.

# Vytvoření základního zásuvného modulu

Vytvoříme zjednodušenou verzi zásuvného modulu Rychlý výběr. Tento zásuvný modul bude vybírat poslední zaměřený zpřístupněný objekt pomocí zmáčknutí Ctrl Alt E.

Nejprve řádky s importy, které budeme používat:

from accerciser.plugin import Plugin import gtk import pyatspi

Následně odvodíme novou třídu ze základní třídy Plugin a přiřadíme některé její povinné atributy:

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

Nyní přepíšeme metodu init, ve které budeme nastavovat akci globání klávesové zkratky pro výběr naposledy zaměřené zpřístupněné položky, registrovat naslouchání události pro událost „focus“ a nastavovat instanci proměnné last_focused na None.

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

Všimněte si, že instance proměnné global_hotkeys je seznam n-tic. Každou n-ticí je akce globální klávesové zkratky složená z popisu akce, požadovaného volání metody, symbol mačkané klávesy a maska s klávesovými modifikátory.

Ve zpětném volání události „focus“ přiřadíme instanci proměnné last_focused zpřístupněnu položku, která událost „focus“ vyslala.

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

Ve zpětném volání akce klávesové zkratky aktualizujeme uzel aplikace s poslední zaměřenou zpřístupněnou položkou (v případě, že ji máme poznačenou):

def inspectLastFocused(self): if self.last_focused: self.node.update(self.last_focused)

# Vytvoření konzolového zásuvného modulu

Vytvoříme konzolový zásuvný modul, který bude zobrazovat změny zaměření vyslané zpřístupněnou položkou pomocí role „push button“. Pamatujte, že zjistit u položky o jakou se jedná roli je pomoci aplikace Accerciser snadné. Můžete to ověřit například ve stromovém zobrazení aplikací.

Řádky s potřebnými importy jsou:

from accerciser.plugin import ConsolePlugin import pyatspi

Přidáme definici třídy s názvem a popisem zásuvného modulu:

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

Přepíšeme metodu init přidáním registrovaného naslouchání:

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

V metodě zpětného volání se vypíší všechny události zmáčknutí tlačítka.

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

# Vytvoření okenního zásuvného modulu

Vytvoříme okenní zásuvný modul, který umožní rychle testovat akci „click“ na zpřístupněné položce, která podporuje rozhraní AT-SPI „Action“ a má akci pojmenovanou „click“. Bude se jedna o prosté tlačítko, na které když se klikne, provede se akce „click“ na zpřístupněné položce.

Ze všecho nejdřív uvedeme řádky s povinným importem:

import gtk from accerciser.plugin import ViewportPlugin

Následně definujeme třídu s názvem a popisem:

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

Přepíšeme metodu init kódem pro sestavení uživatelského rozhraní a pro přípojení zpětného volání k signálu pro tlačítko. Použijeme kontejner pro zarovnávní, aby bylo tlačítko na kartě zásuvného modulu umístěno na střed a nezabíralo monstrózně celý prostor zásuvného modulu. Všimněte si, že instance proměnné plugin_area obsahuje gtk.Frame, který by měl vystupovat ve všech widgetech zásuvného modulu.

def init(self): alignment = gtk.Alignment(0.5,0.5,0,0) self.click_button = gtk.Button('Click me!') alignment.add(self.click_button) self.plugin_area.add(alignment)

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

self.show_all()

Rovněž vytvoříme vhodnou metodu, která vrací seznam podporovaných akcí právě vybrané přístupné položky. V případě, že nepodporuje rozhraní Action, vrátí se prázdný seznam:

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

Základní třída zásuvných modulů má metodu nazývanou onAccChanged, které je volána pokaždé, když se vybraná přístupná položka v aplikaci změní. Přepíšeme ji tak, aby se nastavilo tlačítko jako citlivé jen když má aktuální přístupná položka činnost „click“:

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

Metoda zpětného volání „clicked“ pro tlačítko provádí akci při kliknutí na zpřístupněnou položku. Protože toto zpětné volání může být zavoláno jen když je tlačítko citlivé, nemusíme se starat o kontrolu, jestli má aktuální zpřístupněná položka akci „click“?

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'))