Labortablo, une interface de bureau codée en Python sur une base Archlinux/Openbox (2ème partie)

La barre de tâches

Sommaire

1. La boucle

2. Récupérer et convertir les icônes avec xprop

3. Iconifier et déiconifier les fenêtres


Voici le deuxième article sur mon interface de bureau moulé à la louche. Il traite de la barre des tâches. Le processus fonctionne de manière tout à fait satisfaisante mais il m’a vraiment donné du fil à retordre. Cette zone qui se trouve en bas de l’écran, affiche:

  • Le bouton d’ouverture ou de fermeture du menu principal,
  • quatre boutons qui permettent d’éteindre, de redémarrer, de mettre en veille ou de se déconnecter,
  • et bien sûr, autant de boutons qu’il y a de fenêtres ouvertes.

Le rôle de ces derniers est triple: iconifier, déiconifier ou bien fermer les fenêtres qui leur sont associées. Les boutons sont des valeurs stockées dans un dictionnaire. Les clés de ce dernier sont les identifiants obtenus avec la commande wmctrl -l.

Dans la capture d’écran ci-dessous, on peut constater que trois applications sont ouvertes, mon agenda, chromium et Libre-Office Calc.

  1. La boucle

Toute la difficulté de cette barre de tâches est qu’il faut créer une boucle qui récupère les informations sur chaque fenêtre d’application ouverte et surtout qui affiche l’icône qui lui est associée.

Voici donc le déroulé des opérations:

La boucle est incluse dans le script taskbar.py. Elle exécute pour la première fois la commande wmctrl -l (voir capture d’écran ci-dessus) et stocke les informations obtenues dans un dictionnaire que nous allons baptiser running_applications. Ce dictionnaire doit être hors de la boucle. À chaque tour, la boucle exécute wmctrl -l et compare les informations obtenues avec celles stockées dans le dictionnaire running_applications. Les lignes contenant N/A) sont exclues du processus. Le programme sait désormais ce qu’il doit faire. Il splitte chaque ligne, c’est-à dire qu’il les transforme en liste, pour en extraire le numéro d’identification (par exemple 0x008000007), ainsi que le titre de la fenêtre (par exemple Agenda). Le titre de la fenêtre va s’afficher sur le bouton qui lui correspond.

Si une nouvelle fenêtre a été ouverte, la boucle se charge elle-même de créer le bouton correspondant. Et si une fenêtre a été fermée, elle se charge de détruire le bouton associé. Bien sûr, le script taskbar.py ne s’amuse pas à détruire et à recréer une nouvelle barre à chaque changement. Il ne rajoute ou ne détruit que les boutons impactés par une modification. Puis, en fin de boucle, après avoir effectué les modifications et actualisé la barre des tâches, le script met à jour le contenu du dictionnaire running_applications, qui est hors de la boucle.

Je vous invite à aller voir le code sur mon dépôt git. Je pense qu’il est assez concis.

2. Récupérer les icônes avec xprop, puis les convertir au format *.png pour les afficher à la volée

Il s’agit sans aucun doute de la partie la plus délicate. Je dois vous avouer que le titre de l’article n’est pas correct. Je prétends que l’interface de bureau est codée tout en Python, mais il y a tout de même un fichier *.sh. J’ai déniché ce script à cette adresse.

 #!/bin/bash

path=$1
id=$2
xprop -id "$id" -notype 32c _NET_WM_ICON |
    perl -0777 -pe '@_=/\d+/g;
    printf "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 255\nTUPLTYPE RGB_ALPHA\nENDHDR\n", splice@_,0,2;
    $_=pack "N*", @_;
    s/(.)(...)/$2$1/gs' > "$path"/"$id".pam

convert "$path"/"$id".pam "$path"/"$id".png

Il est manifestement l’oeuvre d’un dénommé Stéphane Chazelas. Bien sûr, je l’ai adapté à ma sauce. Il prend deux paramètres en arguments (le répertoire courant et le numéro d’identification). La mission de ce script est d’exécuter xprop et de générer des fichiers portant l’extension .pam. Ceux-ci sont immédiatement convertis en icônes .png pour être affichés à la volée sur les boutons respectifs. Si l’identifiant de la fenêtre est 0x00800007, alors l’icône qui lui est associé, s’appelle 0x00800007.png.

Ça marche presque à tous les coups. Seules quelques applications résistent et refusent de convertir le fichier *.pam. C’est le cas de Shotwell par exemple. Lorsque cela se produit, elles sont capturées dans une exception.

Le processus xprop prend un peu de temps, si bien qu’il est possible que le bouton affiche seulement le texte et pas l’icône. Afin de remédier à ce problème et être ceinture-bretelles, je vais modifier le code pour que le processus soit lancé une deuxième fois en début de boucle. Si vraiment, la conversion au format png s’avère impossible, je peux encore utiliser une liste d’icônes de secours. Après tout, cela ne concerne que quelques applications. En tout cas, le processus actuel est lisse pour l’utilisateur. Il n’y a aucun retard. C’est instantané. Le texte de l’icône s’actualise dès qu’on change de page ou qu’on ouvre un nouvel onglet dans le navigateur. Il n’y a aucun sursaut.

Les fichiers générés par xprop et portant l’extension *.pam, ainsi que les icônes au format *.png sont conservés vingt secondes dans le répertoire courant, avant d’être détruits. Si l’ordinateur est éteint pendant ce laps de temps, le programme se charge de détruire les éventuels survivants juste avant le processus d’extinction, d’une part pour ne pas encombrer le répertoire, et d’autre part pour être certain qu’à la prochaine session, ils ne seront pas réattribués à une mauvaise application. Le cas s’est déjà produit. Le bouton de Thunar affichait l’icône de Chromium!

3. Iconifier et déiconifier les fenêtres en utilisant un simple clic de souris

Cette opération est bien moins évidente qu’il n’y parait. Il ne s’agit pas seulement d’alterner la commande d’un bouton pour qu’il réduise sa fenêtre puis qu’il l’agrandisse. En se contentant de faire ça, une fenêtre ouverte mais totalement cachée va d’abord se réduire puis s’agrandir au-dessus des autres, ce qui va contraindre l’utilisateur à faire un double-clic un peu énervant.

Il faut donc faire en sorte de créer une condition pour que le bouton qui iconifie une fenêtre puisse aussi être celui qui va la passer au premier plan si cette dernière n’a pas le focus. Pour ce qui est de la déiconification, c’est beaucoup plus simple puisqu’on demande juste au bouton d’agrandir et de passer sa fenêtre au premier plan.

C’est la commande xdotool getwindowfocus getwindowname qui se charge de déterminer quelle fenêtre a le focus. Ensuite, selon le cas, c’est xdotool windowminimize qui va iconifier la fenêtre ou bien wmctrl -i -a qui va la placer au-dessus de toutes les autres, et par conséquent la rendre visible.

def iconify_window(self, ID, tb):
    """iconifies the window"""

    self.ID = ID
    self.taskbar_button = tb

    process = subprocess.Popen(['xdotool', 'getwindowfocus', 'getwindowname'], text=True, stdout=subprocess.PIPE)
    for line in process.stdout.readlines():
        if line.strip()==self.taskbar_button['text'].strip():
            subprocess.Popen(['xdotool', 'windowminimize', self.ID], text=True, stdout=subprocess.PIPE)
            self.taskbar_button.bind('<Button-1>', lambda event, ID=self.ID,
                                                  tb=self.taskbar_button: self.deiconify_window(ID, tb))
        else:
            subprocess.Popen(['wmctrl', '-i', '-a', self.ID], text=True, stdout=subprocess.PIPE)
            self.taskbar_button.bind('<Button-1>', lambda event, ID=self.ID,
                                                  tb=self.taskbar_button: self.iconify_window(ID, tb))

Conclusion

Voilà pour ce qui concerne la barre des tâches. Elle fonctionne très bien mais il reste tout de même quelques points d’amélioration.

Ajouter de la transparence et opacifier la barre des tâches au survol de la souris. Malgré la méthode root.attributes(‘-alpha’, 0.5), je n’y parviens pas.

Je souhaiterais également que mes deux écrans affichent une barre de tâches qui corresponde aux fenêtres ouvertes sur chacun d’entre eux. Pour l’instant, les deux barres sont des clônes. Mais j’ai peut-être déjà la solution…

Et pour celles et ceux que ça intéresse, le code est ici:

https://gitlab.com/miamondo/labortablo

Votre commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l’aide de votre compte WordPress.com. Déconnexion /  Changer )

Photo Google

Vous commentez à l’aide de votre compte Google. Déconnexion /  Changer )

Image Twitter

Vous commentez à l’aide de votre compte Twitter. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l’aide de votre compte Facebook. Déconnexion /  Changer )

Connexion à %s