PROJET AUTOBLOG


Planet-Libre

source: Planet-Libre

⇐ retour index

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

dimanche 20 décembre 2020 à 01:07

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 rôle de ces derniers est triple: iconifier, déiconifier ou bien fermer la fenêtre qui lui est associée. 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 commence par exécuter wmctrl -l (voir capture d’écran ci-dessus) et stocke les informations obtenues dans une variable que nous allons baptiser old_running_applications. Cette variable doit être hors de la boucle.

La boucle exécute une nouvelle fois wmctrl -l et stocke les informations dans une variable baptisée new_running_applications. Cette variable se trouve dans la boucle.

Le contenu des deux variables est comparé (en excluant les lignes contenant N/A). Le programme détermine quelles sont les fenêtres ouvertes, 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 demande au script taskbar.py de créer le bouton correspondant. En revanche, si une fenêtre a été fermée, elle lui demande de détruire le bouton corespondant. 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 de la variable old_running_applications, qui est hors de la boucle.

old_running_applications = new_running_applications

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 fichiers .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. Seule 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 était possible que le bouton affiche seulement le texte et pas l’icône. Afin de remédier à ce problème et être ceinture-bretelles, j’ai modifié le code pour que le processus soit de nouveau lancé à chaque début de boucle. Les boutons qui n’ont que le texte, tentent de récupérer leur icône. Si vraiment, l’icône n’a pas éte convertie au format png, alors le bouton s’affiche quand-même. Tout ce processus est invisible pour l’utilisateur. Il n’y a aucun retard. C’est instantané.

Les fichiers générés par xprop et portant l’extension *.pam, ainsi que les fichiers au format *.png sont conservés provisoirement dans le répertoire courant. Ils sont détruits à l’extinction, au redémarrage ou à la déconnexion, 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 donner l’illusion d’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('', 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('', 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

Gravatar de miamondo
Original post of miamondo.Votez pour ce billet sur Planet Libre.