Comment puis-je écrire une application / un indicateur de panneau mis à jour dynamiquement?

12

J'essaie d'écrire des applications de panneau pour ubuntu Mate. Je connais assez bien C / C ++ et SDL. J'ai vu la page github des applications du panneau Mate-University, mais je ne peux pas le faire fonctionner correctement / Je passe un bon moment avec.

Je me demande simplement s'il existe un moyen facile d'écrire des applications de panneau? Je ne parle pas d'utiliser le lanceur d'applications personnalisé, je voudrais ajouter de nouvelles fonctionnalités au panneau, mais je ne sais pas comment. Un didacticiel ou une description de l'écriture d'applications de panneau pourrait être très utile.

j0h
la source

Réponses:

16

Étant donné que ce qui semble être l'occasion de poser cette question a déjà une réponse , je réponds à cette question comme une explication détaillée de la façon dont cela a été fait (en python)

Indicateur statique de base

Comme Ubuntu Mate, à partir de 15,10, prend en charge les indicateurs, il n'y a pas beaucoup de différence entre l'écriture d'un indicateur et une application de panneau pour Mate. Par conséquent, ce lien est un bon point de départ pour un indicateur de base dans python, en utilisant l' AppIndicator3API. Le lien est un bon début, mais ne fournit aucune information sur la façon d'afficher du texte sur l'indicateur, et encore moins sur la façon de mettre à jour le texte (ou l'icône). Néanmoins, avec quelques ajouts, cela conduit à un "cadre" de base d'un indicateur comme ci-dessous. Il affichera une icône, une étiquette de texte et un menu:

entrez la description de l'image ici

#!/usr/bin/env python3
import signal
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('AppIndicator3', '0.1')
from gi.repository import Gtk, AppIndicator3

class Indicator():
    def __init__(self):
        self.app = 'test123'
        iconpath = "/opt/abouttime/icon/indicator_icon.png"
        self.indicator = AppIndicator3.Indicator.new(
            self.app, iconpath,
            AppIndicator3.IndicatorCategory.OTHER)
        self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE)       
        self.indicator.set_menu(self.create_menu())
        self.indicator.set_label("1 Monkey", self.app)

    def create_menu(self):
        menu = Gtk.Menu()
        # menu item 1
        item_1 = Gtk.MenuItem('Menu item')
        # item_about.connect('activate', self.about)
        menu.append(item_1)
        # separator
        menu_sep = Gtk.SeparatorMenuItem()
        menu.append(menu_sep)
        # quit
        item_quit = Gtk.MenuItem('Quit')
        item_quit.connect('activate', self.stop)
        menu.append(item_quit)

        menu.show_all()
        return menu

    def stop(self, source):
        Gtk.main_quit()

Indicator()
signal.signal(signal.SIGINT, signal.SIG_DFL)
Gtk.main()

Dans la ligne AppIndicator3.IndicatorCategory.OTHER, la catégorie est définie, comme expliqué dans ce lien (partiellement obsolète) . Il est important de définir la bonne catégorie, notamment pour placer l'indicateur dans une position appropriée dans le panneau.

Le principal défi; comment mettre à jour le texte et / ou l'icône de l'indicateur

Le vrai défi n'est pas de savoir comment écrire un indicateur de base, mais comment mettre à jour périodiquement le texte et / ou l'icône de votre indicateur, car vous voulez qu'il affiche l'heure (textuelle). Pour que l'indicateur fonctionne correctement, nous ne pouvons pas simplement utiliser threadingpour démarrer un deuxième processus de mise à jour périodique de l'interface. Eh bien, en fait, nous le pouvons, mais à plus long terme, cela entraînera des conflits, comme je l'ai découvert.

Voici où GObjectentre en jeu, comme il est mis dans ce lien (également obsolète) :

appeler gobject.threads_init()à l'initialisation de l'applicaiton. Ensuite, vous lancez vos threads normalement, mais assurez-vous que les threads n'effectuent jamais aucune tâche GUI directement. Au lieu de cela, vous utilisez gobject.idle_addpour planifier l'exécution de la tâche GUI dans le thread principal

Lorsque nous remplaçons gobject.threads_init() par GObject.threads_init()et gobject.idle_addpar GObject.idle_add(), nous avons à peu près la version mise à jour de la façon d'exécuter les threads dans une Gtkapplication. Un exemple simplifié, montrant un nombre croissant de singes:

entrez la description de l'image ici

#!/usr/bin/env python3
import signal
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('AppIndicator3', '0.1')
from gi.repository import Gtk, AppIndicator3, GObject
import time
from threading import Thread

class Indicator():
    def __init__(self):
        self.app = 'test123'
        iconpath = "/opt/abouttime/icon/indicator_icon.png"
        self.indicator = AppIndicator3.Indicator.new(
            self.app, iconpath,
            AppIndicator3.IndicatorCategory.OTHER)
        self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE)       
        self.indicator.set_menu(self.create_menu())
        self.indicator.set_label("1 Monkey", self.app)
        # the thread:
        self.update = Thread(target=self.show_seconds)
        # daemonize the thread to make the indicator stopable
        self.update.setDaemon(True)
        self.update.start()

    def create_menu(self):
        menu = Gtk.Menu()
        # menu item 1
        item_1 = Gtk.MenuItem('Menu item')
        # item_about.connect('activate', self.about)
        menu.append(item_1)
        # separator
        menu_sep = Gtk.SeparatorMenuItem()
        menu.append(menu_sep)
        # quit
        item_quit = Gtk.MenuItem('Quit')
        item_quit.connect('activate', self.stop)
        menu.append(item_quit)

        menu.show_all()
        return menu

    def show_seconds(self):
        t = 2
        while True:
            time.sleep(1)
            mention = str(t)+" Monkeys"
            # apply the interface update using  GObject.idle_add()
            GObject.idle_add(
                self.indicator.set_label,
                mention, self.app,
                priority=GObject.PRIORITY_DEFAULT
                )
            t += 1

    def stop(self, source):
        Gtk.main_quit()

Indicator()
# this is where we call GObject.threads_init()
GObject.threads_init()
signal.signal(signal.SIGINT, signal.SIG_DFL)
Gtk.main()

Voilà le principe. Dans l'indicateur réel de cette réponse , le temps de boucle et le texte de l'indicateur ont été déterminés par un module secondaire, importé dans le script, mais l'idée principale est la même.

Jacob Vlijm
la source