Philippe Blayo
Le paysage des logiciels libres de dessin vectoriel diffère beaucoup de celui du bitmap. Pour le bitmap, un logiciel, Gimp, demeure sans rival, alors que pour le vectoriel, plusieurs logiciels cohabitent avec chacun des atouts et des faiblesses. Parmi eux, Skencil (anciennement appelé Sketch) est le seul qui soit scriptable.
Python est le langage dans lequel de tels scripts peuvent être conçus. D'autres logiciels libres qui gravitent dans le monde de l'image sont également scriptables en Python : Gimp (depuis sa version 2), Blender et Scribus. Dans le cas de Skencil, il s'agit également du langage dans lequel le logiciel lui-même est écrit (majoritairement), ce qui permet d'envisager un accès très naturel à l'ensemble des objets qu'il manipule.
De Sketch à SkencilEn langue anglaise, sketch signifie esquisse. Comme il s'agit d'un terme très commun, la décision a été prise à l'automne 2003 de le remplacer par Skencil. Ce dernier avait notamment l'avantage de commencer par les mêmes lettres, ce qui permettait de préserver le .sk comme extension des fichiers sources. La version 0.6.16 est la première publiée sous le nom Skencil. C'est donc ce dernier qui sera employé dans l'article. Par contre, en interne, le terme employé dans les sources reste Sketch (c'est le package principal). C'est pourquoi il apparaîtra dans les portions de code. |
Cet article aborde le scripting de Skencil sous l'angle des raccourcis clavier. Leur définition ne passe en effet pas par des fichiers de configuration classiques, mais par du code Python. De tels raccourcis peuvent s'avérer bien utile, mais cette possibilité n'est malheureusement pas documentée (si ce n'est par un bout de code qui traîne sur une liste de diffusion). Aussi m'a-t-il paru intéressant de détailler cet aspect pour introduire le scripting.
Une grande partie de la personnalisation de Skencil passe par le fichier userhooks.py situé dans le répertoire .sketch/ à la racine de chaque utilisateur. Comme son extension l'indique, il renferme du code Python que Skencil exécute chaque fois qu'il est lancé.
Ce fichier est utilisé notamment pour signifier l'ajout de scripts et de greffons (dont la réalisation fera l'objet d'un prochain article). Il l'est également pour définir (ou redéfinir) des raccourcis claviers. Chaque utilisateur peut ainsi choisir les siens. Si l'on souhaite par exemple associer
CreateRectangle
)
SelectAll
)
on peut insérer le code suivant dans userhooks.py :
import Sketch.UI.mainwindow import Sketch.UI.canvas def find_command(name): """Renvoie l'objet commande""" for cmd in (Sketch.UI.mainwindow.command_list + Sketch.UI.canvas.command_list): if cmd.name == name: return cmd return None # raccourcis clavier find_command("CreateRectangle").key_stroke = "r" find_command("SelectAll").key_stroke = "C-a"
On définit une fonction find_command()
qui prend un nom en paramètre
et renvoie la commande que désigne ce nom. Leur attribut key_stroke
est
ensuite positionné à la séquence de touches souhaitée.
C'est Bernhard Herzog (l'auteur de Skencil) qui a proposé cette manière de procéder. Elle présente l'avantage d'ajouter le rappel du raccourci à l'entrée des menus qui lui correspond.
A l'examen du parcours de sa boucle for
, on se rend compte que
la fonction find_command()
ne permet d'accéder qu'aux commandes définies
dans l'interface utilisateur (UI.mainwindow
et UI.canvas
). C'est là que se
trouvent la plus grande partie des commandes (70 dans mainwindow
et 31
dans canvas
), mais toutes n'y figurent pas. Celles d'édition des noeuds
d'une courbe, par exemple, se trouvent ailleurs.
[Note : les autres commandes se répartissent dans différents fichiers des objets graphiques de Sketch/Graphics/ (bezier.py, ellipse.py, image.py, maskgroup.py, text.py). Celles des noeuds d'une courbe sont dans bezier.py.] |
Heureusement, ces autres commandes sont accessibles par l'intermédiaire
d'une simple liste (command_classes
) qui réside dans le package
Sketch
lui-même :
commands = (Sketch.UI.mainwindow.command_list + Sketch.UI.canvas.command_list) # ajout des autres commandes for cmd_class in Sketch.command_classes: commands += cmd_class.commands
# nouvelle fonction find_command() def find_command(name): for cmd in commands: if cmd.name == name: return cmd return None
La définition des raccourcis ne change pas :
find_command("CreateRectangle").key_stroke = "r" find_command("SelectAll").key_stroke = "C-a"
On voit cependant poindre une difficulté : chaque raccourci provoque un parcours de la liste des commandes. Cela convient pour deux raccourcis, mais conduirait à un ralentissement pour un nombre beaucoup plus élevé. Pour faire face à cette possibilité, il est préférable de passer par un dictionnaire des commandes qui associe les noms des commandes aux objets commandes eux-mêmes :
catalog = {} for cmd in commands: catalog[cmd.name] = cmd catalog["CreateRectangle"].key_stroke = "r" catalog["SelectAll"].key_stroke = "C-a"
Ainsi, la liste des commandes n'est parcourue qu'une seule fois quelque soit le nombre de raccourcis.
Muni de ces informations, essayons de créer un raccourci (disons [e]) pour
fusionner (fermer) deux noeuds d'une courbe (une opération très utile pour
laquelle aucun raccourci n'est définit par défaut).
Il faut d'abord trouver comment se nomme cette commande.
Pour cela, il existe plusieurs possibilités. La première qui vient à
l'esprit est d'afficher tous les noms (cmd.name
). Comme il en
existe 150, les passer en revue peut s'avérer fastidieux
(d'autant qu'on ignore quel terme Skencil emploie en interne).
Mieux vaut donc afficher également l'entrée du menu qui invoque la
commande (cmd.menu_name
) :
for cmd in commands: print cmd.menu_name,' : ', cmd.name
Le lancement de skencil provoque l'affichage de 150 lignes parmi lesquelles
... Couper la courbe : OpenNodes Fermer les noeuds : CloseNodes ...
On reconnaît l'entrée Fermer les noeuds
qui nous est familière
(la traduction est préservée).
Il ne reste plus qu'à positionner le raccourci :
catalog["CloseNodes"].key_stroke = "e"
Une autre manière de trouver un nom consiste à explorer les sources de
Skencil (langage interprété oblige, elles sont déjà présentes).
L'examen des fichiers mainwindow.py et canvas.py
indique que les noms des commandes y sont passés en
paramètre d'une fonction AddCmd()
(ou de ses variantes
AddModeCmd()
, AddSelCmd()
et AddDocCmd()
). Un grep Add.*Cmd
permettrait donc de sélectionner les lignes contenant des noms de commandes.
Mais comme les fichiers à explorer se répartissent dans plusieurs
répertoires (Sketch/Graphics/ et Sketch/UI/), mieux vaut se placer
à la racine de Skencil (cd /usr/lib/ske*/Sketch
) et lancer la
recherche depuis cet endroit :
$ grep "^[^#d]*Add.*Cmd(" Graphics/* UI/*
ou plus simplement :
$ grep "^[^#d]*Add.*Cmd(" */*
Ce qui abouti à l'affichage de 136 lignes parmi lesquelles
... Graphics/bezier.py: AddCmd(commands, OpenNodes, _("Cut Curve"),... Graphics/bezier.py: AddCmd(commands, CloseNodes, _("Close Nodes"), ...
L'entrée du menu ("Close Nodes"
) permet à nouveau d'identifier la bonne
commande, même s'il s'agit cette fois de la version originale (non traduite).
La présence du nom des fichiers permet en plus de restreindre assez vite
la recherche.
[Note : l'expression régulière |
Peut-être avez-vous remarqué que la première solution affichait 150 commandes et la deuxième 136. Il y a donc des doublons. Le nombre exact de commandes est accessible par le nombre d'entrées dans le dictionnaire (il n'y a qu'une entrée par nom).
print len(catalog)
affiche 132.
Pour rassembler tous les éléments abordés, voici le code complet à placer dans userhooks.py pour définir les trois raccourcis évoqués :
import Sketch.UI.mainwindow import Sketch.UI.canvas
# liste des commandes commands = (Sketch.UI.mainwindow.command_list + Sketch.UI.canvas.command_list) for cmd_class in Sketch.command_classes: commands += cmd_class.commands # catalogue des commandes catalog={} for cmd in commands: catalog[cmd.name] = cmd catalog["CloseNodes"].key_stroke = "e" catalog["SelectAll"].key_stroke = "C-a" catalog["CreateRectangle"].key_stroke = "r"
La définition de raccourcis nous a permit d'entrevoir la facilité avec laquelle il est possible d'accéder à des objets au coeur de Skencil. Il s'agissait d'un prélude à un prochain article où on verra comment utiliser cet accès pour l'écrire de scripts et de greffons.