Wie kann ich eine dynamisch aktualisierte Panel-App / Anzeige erstellen?

12

Ich versuche einige Panel-Apps für Ubuntu Mate zu schreiben. Ich kenne C / C ++ und SDL ziemlich gut. Ich habe die Github-Seite der Mate-University-Panel-Apps gesehen, aber ich kann sie nicht richtig zum Laufen bringen / Ich habe eine Weile Zeit damit.

Ich frage mich nur, ob es eine einfache Möglichkeit gibt, Panel-Apps zu schreiben. Ich spreche nicht über die Verwendung des benutzerdefinierten Anwendungsstarters. Ich möchte dem Panel neue Funktionen hinzufügen, bin mir aber nicht sicher, wie. Ein Tutorial oder eine Beschreibung zum Schreiben von Panel-Apps kann sehr hilfreich sein.

j0h
quelle

Antworten:

16

Da die scheinbare Gelegenheit, diese Frage zu stellen, bereits eine Antwort hat , beantworte ich diese Frage als ausführliche Erklärung, wie es gemacht wurde (in python)

Grundlegende statische Anzeige

Da Ubuntu Mate ab 15,10 Indikatoren unterstützt, gibt es keinen großen Unterschied zwischen dem Schreiben eines Indikators und einer Panel-App für Mate. Daher ist dieser Link ein guter Ausgangspunkt für einen Basisindikator in pythonder AppIndicator3API. Der Link ist ein guter Anfang, enthält jedoch keine Informationen zum Anzeigen von Text auf dem Indikator, geschweige denn zum Aktualisieren des Texts (oder Symbols). Mit ein paar Ergänzungen führt dies jedoch zu einem grundlegenden "Rahmen" eines Indikators wie unten. Es werden ein Symbol, eine Textbeschriftung und ein Menü angezeigt:

Geben Sie hier die Bildbeschreibung ein

#!/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()

In der Zeile AppIndicator3.IndicatorCategory.OTHERwird die Kategorie definiert, wie in diesem (teilweise veralteten) Link erläutert . Es ist wichtig, die richtige Kategorie einzustellen, um den Indikator an einer geeigneten Stelle im Bedienfeld zu platzieren.

Die Hauptherausforderung; wie man den Anzeigetext und / oder das Symbol aktualisiert

Die eigentliche Herausforderung besteht nicht darin, einen Basisindikator zu schreiben, sondern den Text und / oder das Symbol Ihres Indikators regelmäßig zu aktualisieren , da die (Text-) Zeit angezeigt werden soll. Damit der Indikator ordnungsgemäß funktioniert, können wir nicht einfach threadingeinen zweiten Prozess starten, um die Schnittstelle regelmäßig zu aktualisieren. Nun, eigentlich können wir das, aber auf längere Sicht wird es zu Konflikten führen, wie ich herausgefunden habe.

Hier GObjectkommt, wie es in diesem (auch veralteten) Link steht :

Aufruf gobject.threads_init()bei der Anwendungsinitialisierung. Dann starten Sie Ihre Threads normal, stellen jedoch sicher, dass die Threads niemals direkt GUI-Aufgaben ausführen. Stattdessen gobject.idle_addplanen Sie die Ausführung der GUI-Aufgabe im Hauptthread

Wenn wir gobject.threads_init() nach GObject.threads_init()und gobject.idle_addnach ersetzen GObject.idle_add(), haben wir so ziemlich die aktualisierte Version, wie Threads in einer GtkAnwendung ausgeführt werden. Ein vereinfachtes Beispiel, das eine zunehmende Anzahl von Affen zeigt:

Geben Sie hier die Bildbeschreibung ein

#!/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()

Das ist das Prinzip. Im tatsächlichen Indikator in dieser Antwort wurden sowohl die Schleifenzeit als auch der Indikatortext von einem sekundären Modul bestimmt, das in das Skript importiert wurde, aber die Hauptidee ist dieselbe.

Jacob Vlijm
quelle