RunApplet

Ça faisait quelques temps que je cherchais un applet pour pouvoir lancer des commandes sans avoir à taper Alt+F2, juste à partir du tableau de bord, le truc étant disponible à tous moments. Il y'avait autrefois un paquet dans Gnome qui faisait ça, MiniCommander, mais il n'est plus integré par défaut maintenant, et reste quasiment introuvable sur la toile.

L'esprit du Libre étant de faire toi-même le logiciel dont tu as besoin, et bien je l'ai fait. En python puisque je connais ça un peu, et qu'il existe quelques vagues tutos sur le sujet, enfin suffisament pour commencer. RunApplet.png

Vous pouvez télécharger l'applet RunApplet.tar.gz, et les consignes d'installation sont données dans l'archive.

Tutorial Applet Gnome en python

Comme je le dis précédemment, il n'est pas forcément facile de débuter dans la création d'applet pour Gnome. Je m'inspire principalement de ces 2 liens : ce thread sur le forum ubuntu anglophone, et dans la doc PyGTK.

L'idée est la suivante : les applets sont gérés par le service Bonobo de Gnome. Votre applet sera donc composé de 2 parties :

  1. la première est le composant qui va dire à Bonobo où trouver le programme à lancer, et c'est aussi lui qui va lister votre applet dans le sélecteur d'applets à ajouter au tableau de bord (clique droit sur une partie vide du tableau de bord, Ajouter).
  2. Le programme en lui-même qui créer un objet GTK appelé "applet" et avec lequel vous faites ce que vous voulez.

Commençons donc par le fichier pour bonobo. Il doit être placé dans /usr/lib/bonobo/servers/ et porter un nom en .server, par exemple Hello_World.server. Typiquement, ça ressemble à ça :

<oaf_info>
    <oaf_server iid="OAFIID:GNOME_HelloWorld_Applet_Factory"  type="exe" location="/usr/bin/helloworld_applet.py">
        <oaf_attribute name="repo_ids" type="stringv">
            <item value="IDL:Bonobo/GenericFactory:1.0"/>
            <item value="IDL:Bonobo/Unknown:1.0"/>
        </oaf_attribute>
        <oaf_attribute name="name" type="string" value="Hello World Applet"/>
        <oaf_attribute name="description" type="string" value="Applet Hello World en python"/>
    </oaf_server>

    <oaf_server iid="OAFIID:GNOME_HelloWorld_Apple" type="factory" location="OAFIID:GNOME_HelloWorld_Applet_Factory">
        <oaf_attribute name="repo_ids" type="stringv">
            <item value="IDL:GNOME/Vertigo/PanelAppletShell:1.0"/>
            <item value="IDL:Bonobo/Control:1.0"/>
            <item value="IDL:Bonobo/Unknown:1.0"/>
        </oaf_attribute>
        <oaf_attribute name="name" type="string" value="Hello World Applet"/>
        <oaf_attribute name="description" type="string" value="Applet Hello World en python"/>
        <oaf_attribute name="panel:category" type="string" value="Utility"/>
        <oaf_attribute name="panel:icon" type="string" value="system-run"/>
    </oaf_server>
</oaf_info>

Il y'a plusieurs choses à noter dans ce fichier. D'abord, on definit 2 "OAFIID" qui vont permettre de reconnaître notre applet, et qui doivent être uniques pour Gnome. La premiere ID définie appelle le programme en python contenant l'applet, et on doit lui donner le chemin exact et entier de notre appli : location="/usr/bin/helloword.py". On peut ensuite rentrer le nom de l'applet, et une description, puis on arrive à la partie "factory". Ici il faut faire référence à l'ID qu'on vient de définir : location="OAFIID:GNOME_HelloWorld_Applet_Factory". Si vous mettez autre chose que l'ID du premier server, ça ne marchera pas. Le reste est assez parlant. Passons donc au programme python en lui-même.

Voici mon exemple de helloworld_applet.py, qui affiche juste "Hello World !" dans le tableau de bord :

#!/usr/bin/env python

import pygtk
pygtk.require('2.0')
import gnomeapplet
import gtk
import gobject
import sys

class Applet_Hello(gnomeapplet.Applet):

    def __init__(self, applet, iid):
        label=gtk.Label("Hello World !") 
        label.connect("button_press_event", self.showMenu)
        applet.add(label)
        applet.show_all()
        
    def showMenu(self, widget, event):
        self.applet.request_focus(long(event.time))
        if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3:
            widget.emit_stop_by_name("button_press_event")
            self.create_menu()

    def create_menu(self):
        propxml="""
            <popup name="button3">
            <menuitem name="Item 3" verb="About" label="_About" pixtype="stock" pixname="gtk-about"/>
            <menuitem name="Item 4" verb="Preferences" label="_Preferences" pixtype="stock" pixname="gtk-preferences"/>
            </popup>"""
        verbs = [("About", self.showAboutDialog), ("Preferences", self.showPreferencesDialog)]
        self.applet.setup_menu(propxml, verbs, None)
        
            
if __name__ == '__main__':
    gobject.type_register(Applet)
    
    #for debugging purpose, create its own window
    if len(sys.argv) > 1 and sys.argv[1] == "run-in-window":
        #create the main window 
        mainWindow = gtk.Window(gtk.WINDOW_TOPLEVEL)
        mainWindow.set_title("Applet Test")
        mainWindow.connect("destroy", gtk.main_quit)
        #create the applet and run the window
        applet = gnomeapplet.Applet()
        Applet_Hello(applet, None)
        applet.reparent(mainWindow)
        mainWindow.show_all()
        gtk.main()
    else:
        #create as an applet
        print "Starting factory"
        gnomeapplet.bonobo_factory("OAFIID:GNOME_HelloWorld_Applet_Factory", 
                                    gnomeapplet.Applet.__gtype__, 
                                    "Applet Hello World en python", 
                                    "1.0", 
                                    Applet_Hello)

D'abord, on importe les modules nécessaires : gtk, pygtk, gobject, sys et gnomeapplet.
Ensuite, j'utilise une classe Applet qui sera ma classe principale. C'est elle qui affichera le gtk.Label avec écris "Hello World!" dedans. Ceci est géré dans la méthode init qui ne comprends rien de bien compliqué pour qui connaît un peu de PyGTK. On passe donc aux 2 méthodes suivantes qui créent un menu lorsqu'on clique droit sur l'applet. La méthode showMenu a été connectée au signal button_press_event (bouton de souris pressé) lors de l'initialisation. Après avoir vérifié qu'il s'agit d'un clic droit, la méthode create_menu est lancée, et c'est elle qui va véritablement créé ce menu. C'est un bout de XML, et la syntaxe est assez claire pour ajouter l'entrée que l'on souhaite à ce menu, puis la connecter à une fonction.
La 3ème partie est la plus nouvelle : il s'agit ici de lancer l'applet bien comme un applet et pas comme un programme en lui-même. Cependant, pour le déboggage, il est intéressant de pouvoir lancer l'applet dans sa propre fenêtre et obtenir les erreurs sur la sortie standard. C'est ce que vérifie la première condition : si l'argument passé à notre applet est run-in-window, alors on créé une fenêtre, puis un applet, et enfin on appelle la classe principale. S'il n'y a pas d'argument, alors on utilise la méthode bonobo_factory du module gnomeapplet pour lancer l'applet. Cette méthode prend 5 arguments :

  • L'ID qu'on a défini dans le fichier server, ici OAFIID:GNOME_HelloWorld_Applet_Factory,
  • Le type gnomeapplet.Applet.gtype,
  • Une description de l'applet en question,
  • Le numéro de version,
  • Et la classe ou fonction à appeler.

Une fois tout ça rentré (et sans incohérences), vous devez disposer d'une entrée "Hello World" dans la fenêtre de sélection des applets, et en l'ajoutant, vous aurez un joli "Hello World !" dans votre barre des tâches ! Après, à vous de jouer pour créer vos applets !

Références

Seule référence complète, malheureusement pas à jour...
Un post sur le forum d'ubuntu, avec un exemple à jour lui, mais peu d'explications.
Et puis la doc de PyGTK, pour la suite !