Next: Extension d'adesklets, Previous: Utiliser adesklets, Up: Top
Comme expliqué ci-dessus (See A propos d'adesklets.), adesklets est principalement une console interactive d'Imlib2, avec une ou deux fonctions supplémentaires:
Commençons une session interactive typique pour voir ce qui se passe. Sous X, ouvrez un terminal et tapez:
adesklets :
Comme dans la section
précédente (See 'Utilisation d'adesklets comme un
éditeur graphique en ligne de commande'.), vous
obtenez une invite. Maintenant entrez la commande
'images_info
'. Vous devriez obtenir sur la sortie
standard :
2 images id 0 width 1 height 1 alpha 1 filename (null) id 1 width 1 height 1 alpha 1 filename (null) command 0 ok: images_info
Cela signifie que vous avez deux images : l'image 0 et l'image 1, et qu'elles ont une taille de 1x1 pixel. Ce sont les seules images que vous ne pourrez jamais détruire ou modifier (redimensionner indépendamment, retourner diagonalement, etc.) comme vous le souhaitez. Qu'est-ce que c'est que cela?
Mais où est cette fenêtre? Eh
bien, pour l'instant, ce sont seulement des images de 1x1 pixel,
et elles ne sont pas affichées1 à
l'écran. Ainsi, redimensionnons les à 100x100
pixels: tapez 'window_resize 100 100
'. Maintenant,
si vous retapez la commande 'images_info
', vous
obtiendrez:
2 images id 0 width 100 height 100 alpha 1 filename (null) id 1 width 100 height 100 alpha 1 filename (null) command 2 ok: images_info
Vous voyez? Les images d'avant-plan et
d'arrière-plan ont été redimensionées
à 100x100 pixels: de plus l'arrière-plan a
été mis à jour pour contenir la nouvelle
image de fond. Maintenant, rendons cette fenêtre visible.
Tapez les deux commandes: 'window_reset managed
' et
enfin 'window_show
'.
La première commande demande à adesklets de laisser votre gestionnaire de fenêtres "gérer" votre fenêtre (ce qui n'est pas fait par défaut): cela signifie que la fenêtre va être décorée, et que vous pourrez changer sa visibilité2 de façon interactive. La seconde montre votre fenêtre de 100x100. Rien d'impressionnant: un carré noir de 100x100 avec une barre de titre et des bordures fixes.
Comme dit plus haut, seule l'image 0
(l'avant-plan) est affichée par défaut. Maintenant,
tapons : 'window_set_transparency 1
'. Wouah! Nous
voyons la fenêtre racine du dessous! Avec "window
transparency" activé, l'image 1 (arrière-plan) est
d'abord copiée dans la fenêtre, puis l'image 0
(avant-plan) qui est entièrement noire et transparente,
est mise par-dessus3.
Il est temps de parler des couleurs:
souvenez-vous, adesklets est une console d'Imlib2. Par
conséquent, comme Imlib24, les couleurs sont
toujours de véritables couleurs 32 bits codées en
RVBA - huit bits par canal (de 0 à 255), incluant un canal
alpha pour la transparence. La conversion de palette se fera
automatiquement si la définition de votre écran est
inférieure, vous n'avez pas à vous en occuper. Si
vous tapez: 'context_get_color
', vous
obtiendrez:
command 0 ok: context color 255 255 255 255
Imlib2 est une sorte de machine par
états; en tant que tel, il utilise un "contexte" qui garde
en mémoire une série d'attributs qui seront
utilisés dans ses opérations futures. Par exemple,
nous savons maintenant que la couleur qui sera utilisée,
aussi longtemps que nous ne la modifions pas, sera un blanc
opaque (rouge=255, bleu=255, vert=255, alpha=255). Si nous
tapons: 'context_get_image
', il retourne:
command 0 ok: context image 0
Cela signifie que toute opération que
nous effectuons sur une fenêtre sera faite directement sur
l'image d'avant-plan. Dessinons un rectangle plein
semi-transparent de couleur magenta sur l'avant-plan. Les
commandes seront: 'context_set_color 255 0 0 200
' et
'image_fill_rectangle 25 25 50 50
'.
Élégant, n'est-ce pas?
Si vous avez l'habitude de programmer avec
Imlib2, vous avez du remarquer que ces dernières commandes
ont l'air familières. C'est assez normal; la plupart des
fonctions C d'Imlib2 sont présentées de la
même façon dans les 'commandes' d'adesklets. Ainsi,
la commande d'adesklets 'image_fill_rectangle
' suit
la même sémantique que la fonction
'imlib_image_fill_rectangle()
' d'Imlib2, la commande
'context_set_color
' suit celle de la fonction
'imlib_context_set_color()
', etc. Les deux seules
différences significatives (et systématiques) sont
premièrement que partout où vous utiliserez un
objet 'Imlib_Quelquechose
' en C, vous utiliserez un
ID entier avec adesklets, et deuxièmement dans la
sémantique des fonctions
'imlib_free_Quelquechose()
' où on doit
préciser l'ID de l'objet auquel s'applique la fonction au
lieu de libérer l'objet de ce type
sélectionné dans le contexte.
Tout ceci est facile à illustrer. Chargeons la police "Vera" incluse dans adesklets à la taille 20pt, mettons la en police par défaut et écrivons 'Hello' en blanc opaque en diagonale sur l'image d'avant-plan en partant du coin en haut à gauche, avant de décharger la police. Cela devrait ressembler à:
6 >>> load_font Vera/20 command 6 ok: new font 0 7 >>> context_set_font 0 command 7 ok: context_set_font 0 8 >>> context_set_direction text_to_angle command 8 ok: context_set_direction text_to_angle 9 >>> context_set_angle 45 command 9 ok: context_set_angle 45 10 >>> context_set_color 255 255 255 255 command 10 ok: context_set_color 255 255 255 255 11 >>> text_draw 0 0 Hello command 11 ok: text_draw 0 0 Hello 12 >>> free_font 0 command 12 ok: free_font 0
Si vous regardez votre documentation
d'Imlib2, vous remarquerez immédiatement les deux
différences que l'on vient d'évoquer: toutes les
références à la police Vera/20 sont faites
par un ID entier (0 ici), et la commande
'free_font
', contrairement à la fonction
correspondante 'imlib_free_font()
', est
appliquée à la police dont l'ID est donné en
argument plutôt qu'à la police du contexte.
A partir de là, imaginons que vous
voulez sauvegarder l'image résultante; aucun
problème! Tapez: 'save_image out.png
', et
l'avant-plan sera sauvé dans un fichier "out.png" dans le
répertoire courant5. Maintenant, juste pour le
plaisir, quittons adesklets, et lançons une autre session
interactive pour voir si nous pouvons recharger cette image.
Tapez juste la commande "quit
" ou pressez ^D
(Control D: fin de fichier), votre nouvelle session devrait
ressembler à ceci :
0 >>> window_resize 100 100 command 0 ok: window_resize 100 100 1 >>> window_reset managed command 1 ok: window_reset managed 2 >>> window_set_transparency 1 command 2 ok: window_set_transparency 1 3 >>> load_image out.png command 3 ok: new image 2 4 >>> blend_image_onto_image 2 1 0 0 100 100 0 0 100 100 command 4 ok: blend_image_onto_image 2 1 0 0 100 100 0 0 100 100 5 >>> window_show command 5 ok: window_show 6 >>> free_image 2 command 6 ok: free_image 2
Bien sur, nous voulions visualiser le
résultat - c'est pourquoi nous avons utilisé les
commandes 'window_reset
',
'window_set_transparency
' et
'window_show
': elles n'ont pas d'autre
utilité. La commande 'blend_image_onto_image
'
vient elle aussi tout droit d'Imlib2; elle prend la nouvelle
image 2 que l'on vient de créer avec la troisième
commande, et la met dans l'image du contexte (image 0 -
avant-plan par défaut), à partir de ses
coordonnées (0,0) et d'une taille de 100x100 pixels, et la
met dans un rectangle partant de (0,0) et de taille 100x100
pixels dans l'image 0. Il est finalement bon de noter que, avec
adesklets, il y a toujours une image dans le contexte d'Imlib2:
dès que vous essayez de libérer l'image courante,
le contexte d'image est réinitialisé à
l'image d'avant-plan.
Voila, c'est presque la fin de cette introduction... A partir de là vous devriez pouvoir jouer un peu avec adesklets. Si vous êtes déjà familier avec Imlib2 vous vous y ferez très rapidement. Sinon vous devriez garder la documentation d'Imlib2 sous la main. :-)
Quelques dernières astuces:
help
'.history
' pour
enregistrer votre session courante dans un fichier6.pause
' à
la fin du script pour geler l'interpréteur.Voici un exemple:
#!/usr/bin/env adesklets -f # Rendre la fenêtre 'gérée' # window_reset managed # Redimmensioner la fenêtre à 100x100 pixels # window_resize 100 100 # Montrer la fenêtre, puis la geler pendant 10 secondes avant de sortir # window_show pause 10
Vous aurez juste besoin de rendre ce script exécutable et de le lancer. Comme d'habitude, les résultats seront affichées sur la sortie standard.
Lorsque vous serez prêt pour un vrai travail7, vous voudrez surement utiliser un véritable langage de script plutôt que l'interpréteur rudimentaire d'adesklets que nous avons utilisé dans l'introduction; ne serait-ce que pour pouvoir utiliser des variables.
Au moment où j'écris ces lignes (31 mars 2006), le support pour Python et Perl 5 est inclus dans le paquetage d'adesklets8.
Une fonction très utile de l'interpréteur
d'adesklets est sa
capacité à produire un trace complète d'une
de ses sessions (commandes, évènements et messages
spéciaux), que ce soit sur stderr
ou dans un
fichier journal, et ce indépendemment de ce qui le
contrôle. Pour se servir de cette fonctionalité, il
faut:
--enable-debug
de
configure
.ADESKLETS_LOG
dans
l'environnement si la sortie de la session doit être
enregistrée dans un fichier plutôt
qu'affichée sur stderr
9. Ce doit
être un chemin absolu qui servira de préfixe
à l'interpréteur qui, l'ayant
hérité de l'environnement, s'en servira pour
créer les noms complets des fichiers dans lesquels il
écrira.echo
pour placer des marqueurs faciles à
trouver10.
Avec Python, les choses ne sont pas très
différentes de l'interpréteur. Les commandes ont
été encapsulées dans des fonctions Python,
et une classe publique Events_handler
a
été construite pour manipuler les retours
asynchrones de l'interpréteur. Jetons un coup d'oeil sur
le script test/test.py de
l'archive source de la version 0.6.1:
""" test_fr.py - S.Fourmanoit <syfou@users.sourceforge.net>, Guillaume Boitel <g.boitel@wanadoo.fr> Petit script de test non-exhaustif pour adesklets : - Redimensionne la fenêtre adesklets à 100x100 pixels - La met sous le contrôle du gestionnaire de fenêtre - La rend pseudo-transparente - L'affiche à l'écran - Attends ensuite que l'utilisateur la quitte, génère une alarme toutes les 10 secondes et attrape les événements motion_notify dès qu'ils apparaissent. Pour l'essayer : - Installer adesklets avec le support python activé (par défaut) - Lancer python test.py à partir de ce répertoire. """ import adesklets class My_Events(adesklets.Events_handler): def __init__(self): adesklets.Events_handler.__init__(self) def __del__(self): adesklets.Events_handler.__del__(self) def ready(self): adesklets.window_resize(100,100) adesklets.window_reset(adesklets.WINDOW_MANAGED) adesklets.window_set_transparency(True) adesklets.window_show() def quit(self): print 'Quitting...' def alarm(self): print 'Alarm. Next in 10 seconds.' return 10 def motion_notify(self, delayed, x, y): print 'Motion notify:', x, y, delayed My_Events().pause()Voilà! 26 lignes de code Python, et nous avons un desklet parfaitement fonctionnel. Je pense qu'il s'explique de lui-même; jetez un coup d'oeil à l'aide intégrée de python à propos de
adesklets
, adesklets.functions
et
adesklets.Events_handler
pour de bonnes et
complètes explications (See
Documentation du paquetage Python.) . Tout ce que nous avons
fait ici c'est:
adesklets
: cela
instancie automatiquement un processus fils adesklets et configure toutes les
communications. Si l'initialisation du paquetage termine sans
provoquer d'exception, cela signifie que l'interpréteur
tourne et qu'il attend des commandes.adesklets.Events_handler
et redéfinir les
méthodes invoquées pour les
événements qui nous intéressent (tous les
autres événements ne sont pas simplement
ignorés, ils ne sont pas
générés).Tous les objets d'adesklets (polices, images, palettes de couleurs, polygones, etc), sont stockés dans des piles (une pile par type d'objet). Les ID de Python ne sont rien d'autre que la position initale d'objets donnés dans la pile correspondante, et deviendront donc invalides si un objet du même type situé en-dessous est libéré.
Par exemple, partant d'un état vierge, il n'y a d'autre dans la pile des images que l'avant-plan (ID O) et l'arrière-plan (ID 1) de la fenêtre. Lorsque quelqu'un créé deux autres images depuis Python comme ceci:
im_1 = adesklets.create_image(10,10) im_2 = adesklets.create_image(10,10)
on a im_1==2
, et
im_2==3
. Dès qu'on fera:
adesklets.free_image(im_1)
La pile commence à s'écraser,
et ce qui était l'image 3 (im_2
) est
maintenant l'image 2, et im_2
est une
réference invalide. Cela signifie clairement
qu'invoquer:
adesklets.free_image(im2)
génerera le message d'erreur
image out of range
.
adesklets inclut maintenant aussi bien un
mode d'exécution indirect que les variables textuelles
(pour être précis, le remplacement textuel
non-récursif). Cela signifie que les auteurs de desklets
ont ce qu'il faut pour créer des animations
précisément temporisées. Vous trouverez un
exemple simple à suivre dans test/fading.py. Une utilisation plus
poussée de ces fonctionalités est faite dans le
desklet yab
. Pour réaliser un animation en
Python, il faut:
adesklets.start_recording()
, émettre un
nombre indéterminé de commandes
adesklets, puis
repasser en mode normal (execution directe) avec
adesklets.stop_recording()
. Bien sur, aucune
commande n'est évaluée quand
l'interpréteur est en mode indirect; les commandes sont
seulement conservées dans l'historique pour être
rejouées plus tard.adesklets.play_set_abort_on_events()
. La
méthode de haut niveau
adesklets.Events_handler::set_events()
peut aussi
être utilisée pour choisir dynamiquement quels
évènements seront rattrapés. Pour un
exemple, voyez test/test_events.py.adesklets.set()
, qui est très similaire
à son homonyme du shell Bourne.adesklets.play()
.Quelques remarques supplémentaires:
time_gate
. Elle
fonctionne très simplement: à chaque fois qu'une
macro est exécutée, adesklets enregistre à
quel moment elle à commencé. Dès qu'une
commande time_gate
apparait lors de
l'exécution, le programme attend que le nombre de
secondes données en argument à
time_gate
soient écoulées. Par
exemple, écrire time_gate 0.1
dans une
macro empêchera l'execution de toutes les commandes
suivantes de la macro tant que celle-ci n'aura pas
fonctionné pendant au moins un dixième de
seconde.set
).
Si aucune variable ne correspond, la séquence est
simplement ignorée.play
sont, du point de vue du desklet qui les
contrôle, élémentaires, comme toutes les
autres commandes. En un mot, cela signifie qu'aucun
évènement ne sera jamais géneré
pendant qu'une macro est exécutée; ils
apparaitront tous juste après que la commande
play
ait terminé.set_abort_on_events
ou
les variables à chaque fois que vous appelez une macro,
sauf si quelquechose doit être changé depuis
l'appel précedent.adesklets.Events_handler::set_events()
sert
principalement à arrêter la prise en charge de
certains évenements pendant l'exécution de la
macro, ainsi la macro ne peut être interrompue que dans
des circonstances bien précises. Bien sur, tous les
évènements génerés avant l'appel
à cette méthode ne seront pas perdus mais
placés en file d'attente, et les méthodes
d'évènements appropriées seront
appelées plus tard, pourvu que les trappes soient en
place lorsque l'exécution de la macro est
terminée.adesklets.Events_handler::block()
et
adesklets.Events_handler::unblock()
autour de tout
code relatif à une animation si vous avez
instancié un objet d'une classe fille de
adesklets.Events_handler
.Mentionnons jute ici que depuis adesklets 0.4.0, le module
adesklets.utils
inclut maintenant une classe
optionelle ConfigFile
qui peut être facilement
réutilisée par les auteurs de desklets pour ajouter
un système de configuration facilement extensible à
leur code12 (See
Documentation du paquetage Python.) . Par défaut, la
classe prend aussi en charge l'internationalisation
automatiquement, en sélectionnant les jeux de
caractères selon les besoins de l'utilisateur.
L'utilisation des jeux peut bien sûr être
déterminée ou changée à tout moment
en utilisant les fonctions adesklets.get_charset()
et adesklets.set_charset()
. La classe
adesklets.utils.ConfigFile
a aussi un attribut
charset
qu'on peut examiner pour connaitre les
préferences de l'utilisateur. En utilisant cette classe on
notera que le jeu de caractères 'ASCII' est
considéré comme choix par défaut, et qu'il
ne désactivera pas une éventuelle conversion.
Ainsi, les utilisateurs de plate-formes ne supportant pas iconv
pourront toujours utiliser adesklets sans le support de
l'internationalisation.
Enfin, signalons que vous pourriez avoir envie de jeter un oeil au code source du desklet weather disponible sur le site web du projet SourceForge pour voir un plus gros morceau de code Python utilisant adesklets. Il y a aussi quelques autres desklets de test sous le répertoire test/ qui pourraient vous donner des idées. Vous pourriez aussi jeter un oeil au paquetage d'aide autogéneré d'adesklets par pydoc, inclus dans ce document (See Documentation du paquetage Python.). Dès que vous êtes suffisement content de votre desklet, n'hésitez pas à le partager avec le reste d'entre nous (ce qui serait très apprécié). Faites-en un paquetage (See GNU Makefile pour empaqueter les desklets.), puis soumettez-le (See Soumettre un desklet.).
Depuis adesklets 0.6.0, adesklets peut par défaut être commandé par Perl, grâce au travail de Lucas Brutschy . Bien sur, les liens avec Perl sont complètement perpendiculaires à Python, et n'ont besoin que de l'environnement Perl 5 en plus de l'interpréteur de base d'adesklets, écrit en C.
Comme avec le paquetage Python, les commandes sont
encapsulées dans des routines Perl. Une boucle de gestion
des évènements peut alors être lancée,
enregistrant et appelant automatiquement les routines
données (BackgroundGrab
,
ButtonRelease
, LeaveNotify
,
MotionNotify
, ButtonPress
,
EnterNotify
, MenuFire
) selon le cas
approprié. adesklets::event_loop
ne termine
que lors de la fin de l'interpréteur adesklets
commandé. Voici le script test/test.pl du paquetage source de la
version 0.6.1:
# test.pl - Lucas Brutschy <lbrutschy@users.sourceforge.net>, 2006 # # Small, non-exhaustive adesklets test script: # - Resize adesklets window to 100x100 pixels # - Set it to be pseudo-transparent # - Draw a translucent light grey rectangle on it # - Map it on screen # - Mark with a red dot the pointer position every time # a user click # # To try it: # - Install adesklets with perl support enabled (default) # - Run perl test.py from this directory use strict; use adesklets; adesklets::open_streams(); # these are just normal adesklet commands adesklets::window_resize(100,100); adesklets::window_reset(adesklets::WINDOW_UNMANAGED); adesklets::window_set_transparency(1); adesklets::context_set_color(255,255,255,64); adesklets::image_fill_rectangle(5,5,90,90); adesklets::window_show(); adesklets::event_loop(ButtonPress=>\&onbutton); # supply a hash of callbacks adesklets::close_streams(); sub onbutton { my($x,$y) = @_; adesklets::context_set_color(255,0,0,255); adesklets::image_fill_rectangle($x,$y,3,3); }Simplement vingt lignes de code, et comme précedemment, nous avons déjà un desklet fonctionnel. Il doit être assez simple à comprendre, mais voici quelques indications génerales sur ce qu'il fait:
use
adesklets
), et instancie l'interpréteur
d'adesklets en appelant adesklets::open_stream()
.
Lorsque la sous-routine termine, la ligne de commande est
déjà traitée et l'interpréteur est
prêt à accepter des commandes.adesklets::close_streams()
est
appelée.Lorsqu'une commande adesklets retourne une erreur, le script Perl meurt. Vous pouvez récuperer le message d'erreur d'origine comme ceci:
eval { my $image = adesklets::load_image("aaarrhg"); }; if($@) { print $@; }
See Quelques astuces pour les programmeurs, pour une alternative.
Vous pouvez très bien conserver de multiples instances
du même desklet en Perl configurées differemment; il
s'agit simplement d'utiliser la valeur de retour de
adesklets::get_id()
pour identifier de
manière unique l'instance en cours d'exécution; la
manière dont vous utilisez cet unique identifiant entier
pour sélectionner les paramètres de configuration
dépends entièrement de vous.
Vous désirez certainement jeter un oeil aux sections précedentes concernant les ID d'objets (See Attention.) et les animations (See Un sujet spécial: les animations.), qui sont toujours valables pour Perl.
Vous pouvez aussi voir la vieille documentation (plain old documentation – POD) du paquetage Perl, incluse dans ce document (See Documentation du paquetage Perl.). Comme signalé precedemment, dès que vous êtes suffisemment satisfait de votre desklet, n'hésitez pas à le partage avec le reste d'entre nous (ce qui serait très apprecié) – faites-en un paquetage (See GNU Makefile pour empaqueter les desklets.), puis soumettez-le (See Soumettre un desklet.).
[1] "Mappé" dans le jargon d'X Window
[2] Une fenêtre "non-gérée" est très utile pour les desklets: c'est comme si c'était une partie de la fenêtre racine puisqu'elle peut être faite pour ne jamais être en avant-plan et pour rester affichée (mapped) lorsque l'on change d'espace de travail.
[3] Enfin... Pas tout à fait : il y a un buffer qui intervient pour la performance, mais l'opération est logiquement équivalente à cette description.
[4] Gardez votre documentation d'Imlib2 à proximité: c'est très utile! See Documentation d'Imlib2.
[5] Pour que ça marche, il faut que votre installation d'Imlib2 soit correctement configurée pour utiliser libpng, sinon vous risquez de recevoir l'erreur "no loader for file format".
[6] Pour que ça marche, GNU history doit être installé lors de la compilation d'adesklets.
[7] :-)
[8] adesklets a été écrit pour être facilement utilisé depuis toutes sortes de langages interprétés; n'hésitez pas à proposer votre aide si vous voulez que votre langage favori soit supporté!
[9] L'afficher sur stderr
risque
fortement de perturber les liens avec les langages externes, il
est donc préferable de ne le faire que lors d'une
invocation de l'interpréteur directement sur la
console.
[10] Certains systèmes de gestion des
paquetages, comme les ebuilds de Portage avec le drapeau USE
debug
, utilisent cette fonctionalité
automatiquement.
[11] Cette dernière étape
n'est pas obligatoire; le script peut très bien
continuer, mais devra alors avoir été
écrit pour supporter les interruptions des signaux.
Lisez `help(adesklets.Events_handler)
' pour de
plus amples informations.
[12] Pour en voir un exemple voyez n'importe quel desklet non issu de contributions.