1<?xml version="1.0" encoding="utf-8"?> 2<page xmlns="http://projectmallard.org/1.0/" type="topic" id="howto_write_a_plugin" xml:lang="fr"> 3 <info> 4 <link type="guide" xref="index#advanced"/> 5 <title type="sort">3. Écriture de nouveaux greffons</title> 6 <link type="next" xref="preferences"/> 7 <desc>Extension d'<app>Accerciser</app> avec vos propres fonctions</desc> 8 <credit type="author"> 9 <name>Eitan Isaacson</name> 10 <email>eitan@ascender.com</email> 11 </credit> 12 <credit type="author"> 13 <name>Peter Parente</name> 14 <email>pparent@us.ibm.com</email> 15 </credit> 16 <credit type="author"> 17 <name>Aline Bessa</name> 18 <email>alibezz@gmail.com</email> 19 </credit> 20 <license> 21 <p>Creative Commons Partage des Conditions Initiales à l'Identique 3.0</p> 22 </license> 23 24 <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright"> 25 <mal:name>Robert-André Mauchin</mal:name> 26 <mal:email>zebob.m@pengzone.org</mal:email> 27 <mal:years>2007</mal:years> 28 </mal:credit> 29 30 <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright"> 31 <mal:name>Claude Paroz</mal:name> 32 <mal:email>claude@2xlibre.net</mal:email> 33 <mal:years>2008</mal:years> 34 </mal:credit> 35 36 <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright"> 37 <mal:name>Julien Hardelin</mal:name> 38 <mal:email>jhardlin@orange.fr</mal:email> 39 <mal:years>2011</mal:years> 40 </mal:credit> 41 42 <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright"> 43 <mal:name>Bruno Brouard</mal:name> 44 <mal:email>annoa.b@gmail.com</mal:email> 45 <mal:years>2012</mal:years> 46 </mal:credit> 47 </info> 48 <title>Écriture de greffons pour <app>Accerciser</app></title> 49 <p>L'extension d'<app>Accerciser</app> par de nouveaux greffons est devenue beaucoup plus simple depuis que <link href="http://wiki.gnome.org/Apps/Accerciser/PluginTutorial">ce tutoriel</link> a été écrit. Étant donné qu'il fournit des explications détaillées sur le sujet, la section présente reprend son contenu d'origine, avec seulement quelques petites modifications et mises à jour.</p> 50 <p><app>Accerciser</app> prend en charge trois types de greffons :</p> 51 <list> 52 <item> 53 <p>Les greffons de base : ces greffons dérivent de la classe de base des greffons. Ils ne fournissent pas d'interface visible mais peuvent apporter des fonctionnalités supplémentaires à <app>Accerciser</app>.</p> 54 </item> 55 <item> 56 <p>Les greffons de console : ces greffons offrent une simple sortie console dans une zone de texte d'un onglet du greffon. Ne pas confondre avec le <link xref="ipython_plugin">Greffon console IPython</link> fourni avec l'application.</p> 57 </item> 58 <item> 59 <p>Les greffons avec fenêtre d'affichage : c'est la majorité des greffons par défaut d'<app>Accerciser</app>. Ils offrent une interface graphique personnalisée dans un onglet.</p> 60 </item> 61 </list> 62 63 <section id="base_plugin"> 64 <title>Création d'un greffon de base</title> 65 <p> 66 We will create a simplified version of the <link xref="quick_select_plugin">Quick Select Plugin</link>. 67 This plugin will select the last focused accessible when pressing <keyseq><key>ctrl</key><key>alt</key><key>e</key></keyseq>. 68 </p> 69 <p>Pour commencer, les lignes d'importation que nous utiliserons sont :</p> 70 <code> 71 from accerciser.plugin import Plugin 72 import gtk 73 import pyatspi 74 </code> 75 <p>Ensuite, nous dériverons une nouvelle classe depuis la classe de base Plugin et assignerons quelques attributs de classe obligatoires :</p> 76 <code> 77 class FocusSelect(Plugin): 78 plugin_name = 'Focus Select' 79 plugin_description = 'Allows selecting last focused accessible.' 80 </code> 81 <p>Nous allons maintenant redéfinir la méthode init, dans laquelle nous paramétrerons une action de touche générale pour sélectionner le dernier élément accessible ayant été sélectionné, enregistrer un listener d'événement pour l'événement « focus », et fixer la variable d'instance <cmd>last_focused</cmd> à <cmd>None</cmd>.</p> 82 <code> 83 def init(self): 84 pyatspi.Registry.registerEventListener(self.accEventFocusChanged, 'focus') 85 self.global_hotkeys = [('Inspect last focused accessible', 86 self.inspectLastFocused, 87 gtk.keysyms.e, 88 gtk.gdk.CONTROL_MASK | gtk.gdk.MOD1_MASK)] 89 self.last_focused = None 90 </code> 91 <p>Notez que la variable d'instance <cmd>global_hotkeys</cmd> est une liste de tuples. Chaque tuple est une action raccourci général, composée d'une description de l'action, de la méthode à appeler, d'un symbole de touche d'appui-touche, et d'un masque de modification de touche.</p> 92 <p>Dans le rappel de l'événement « focus », nous attribuons la variable d'instance <cmd>last_focused</cmd> à l'élément accessible qui vient juste d'émettre l'événement « focus ».</p> 93 <code> 94 def accEventFocusChanged(self, event): 95 if not self.isMyApp(event.source): 96 self.last_focused = event.source 97 </code> 98 <p>Dans l'appel en retour de l'action raccourci, nous mettons à jour le nœud global de l'application avec le dernier élément accessible sélectionné, si nous l'avons enregistré :</p> 99 <code> 100 def inspectLastFocused(self): 101 if self.last_focused: 102 self.node.update(self.last_focused) 103 </code> 104 </section> 105 <section id="console_plugin"> 106 <title>Création d'un greffon Console</title> 107 <p>Nous allons créer un greffon console pour afficher les changements de focus émis par un élément accessible avec un rôle « bouton-poussoir ». Souvenez-vous qu'il est facile de vérifier le rôle d'un quelconque élément avec <app>Accerciser</app> ; vous pouvez le faire dans le <link xref="desktop_tree_view">Panneau de l'arborescence des applications</link>, par exemple.</p> 108 <p>Les lignes d'importation nécessaires sont :</p> 109 <code> 110 from accerciser.plugin import ConsolePlugin 111 import pyatspi 112 </code> 113 <p>Puis nous ajoutons une définition de classe, avec un nom de greffon et une description :</p> 114 <code> 115 class PushButtonFocus(ConsolePlugin): 116 plugin_name = 'Push Button Focus' 117 plugin_description = 'Print event when pushbutton get\'s focus.' 118 </code> 119 <p>Nous redéfinissons la méthode init en ajoutant un listener de registre :</p> 120 <code> 121 def init(self): 122 pyatspi.Registry.registerEventListener(self.accEventFocusChanged, 'focus') 123 </code> 124 <p>Dans la méthode de rappel, tous les événements des boutons poussoirs sont affichés.</p> 125 <code> 126 def accEventFocusChanged(self, event): 127 if event.source.getRole() == pyatspi.ROLE_PUSH_BUTTON: 128 self.appendText(str(event)+'\n') 129 </code> 130 </section> 131 <section id="viewport_plugin"> 132 <title>Création d'un greffon avec fenêtre d'affichage</title> 133 <p>Nous allons créer un greffon avec fenêtre d'affichage qui permet de tester rapidement l'action « click » dans les éléments accessibles qui prennent en charge l'interface AT-SPI Action et qui possèdent une action appelée « click ». Ce sera un simple bouton qui, une fois cliqué, effectue l'action « click » dans l'accessible.</p> 134 <p>Tout d'abord, quelques lignes d'importation obligatoires :</p> 135 <code> 136 import gtk 137 from accerciser.plugin import ViewportPlugin 138 </code> 139 <p>Ensuite, une définition de classe, avec un nom et une description :</p> 140 <code> 141 class Clicker(ViewportPlugin): 142 plugin_name = 'Clicker' 143 plugin_description = 'Test the "click" action in relevant accessibles.' 144 </code> 145 <p>Nous redéfinissons la méthode init en ajoutant quelques termes de construction d'UI et en connectant un rappel à un signal pour le bouton. Nous utilisons un conteneur d'alignement pour permettre le centrage du bouton dans l'onglet greffon et ne pas occuper tout l'espace du greffon. Notez que la variable d'instance <cmd>plugin_area</cmd> contient un gtk.Frame qui peut être rempli de tous les composants graphiques du greffon.</p> 146 <code> 147 def init(self): 148 alignment = gtk.Alignment(0.5,0.5,0,0) 149 self.click_button = gtk.Button('Click me!') 150 alignment.add(self.click_button) 151 self.plugin_area.add(alignment) 152 153 self.click_button.connect('clicked', self.onClick) 154 155 self.show_all() 156 </code> 157 <p>Nous avons aussi créé une méthode pratique qui renvoie une liste des actions gérées par l'élément accessible actuellement sélectionné. S'il ne gère pas l'interface Action, cela renvoie une liste vide :</p> 158 <code> 159 def accSupportedActions(self): 160 try: 161 ai = self.node.acc.queryAction() 162 except NotImplementedError: 163 action_names = [] 164 else: 165 action_names = [ai.getName(i) for i in xrange(ai.nActions)] 166 return action_names 167 </code> 168 <p>La classe greffon de base possède une méthode appelée onAccChanged qui est appelée à chaque fois que l'élément accessible sélectionné dans l'application cible change. Nous allons la redéfinir pour que le bouton ne soit réactif que lorsque l'élément accessible actuel possède l'action « click » :</p> 169 <code> 170 def onAccChanged(self, acc): 171 has_click = 'click' in self.accSupportedActions() 172 self.click_button.set_sensitive(has_click) 173 </code> 174 <p>La méthode de rappel pour le bouton cliqué réalise l'action « click » sur l'élément accessible. Puisque ce rappel ne peut être effectué que lorsque le bouton est réactif, nous ne devons pas nous préoccuper de savoir si l'accessible actuel possède l'action « click » :</p> 175 <code> 176 def onClick(self, button): 177 ai = self.node.acc.queryAction() 178 action_names = [ai.getName(i) for i in xrange(ai.nActions)] 179 ai.doAction(action_names.index('click')) 180 </code> 181 </section> 182</page> 183