Jak napisać dynamicznie aktualizowaną aplikację / wskaźnik panelu?

12

Próbuję napisać niektóre aplikacje panelowe dla Ubuntu Mate. Znam C / C ++ i SDL dość dobrze. Widziałem stronę github aplikacji Mate-University, ale nie mogę sprawić, by działała poprawnie / mam z tym trochę czasu.

Zastanawiam się tylko, czy jest jakaś prosta droga do pisania aplikacji panelowych? Nie mówię o używaniu niestandardowego programu uruchamiającego aplikacje, chciałbym dodać nową funkcjonalność do panelu, ale nie jestem pewien, jak to zrobić. Samouczek lub opis pisania aplikacji panelowych może być bardzo pomocny.

j0h
źródło

Odpowiedzi:

16

Ponieważ wydaje się, że jest to okazja, aby zadać to pytanie, ma już odpowiedź , odpowiadam na to pytanie jako rozszerzone wyjaśnienie, w jaki sposób zostało to zrobione (w python)

Podstawowy wskaźnik statyczny

Ponieważ Ubuntu Mate od 15,10 obsługuje wskaźniki, nie ma dużej różnicy między pisaniem wskaźnika a aplikacją panelową dla Mate. Dlatego ten link jest dobrym punktem wyjścia dla podstawowego wskaźnika pythonprzy użyciu AppIndicator3interfejsu API. Link jest dobrym początkiem, ale nie zawiera żadnych informacji na temat sposobu wyświetlania tekstu na wskaźniku, ani tym bardziej na temat aktualizacji tekstu (lub ikony). Niemniej jednak, z kilkoma dodatkami, prowadzi to do podstawowej „ramki” wskaźnika, jak poniżej. Wyświetli ikonę, etykietę tekstową i menu:

wprowadź opis zdjęcia tutaj

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

W linii AppIndicator3.IndicatorCategory.OTHERkategoria jest zdefiniowana, jak wyjaśniono w tym (częściowo nieaktualnym) linku . Ustawienie właściwej kategorii jest ważne, między innymi, aby ustawić wskaźnik w odpowiedniej pozycji w panelu.

Główne wyzwanie; jak zaktualizować tekst wskaźnika i / lub ikonę

Prawdziwe wyzwanie nie polega na tym, jak napisać podstawowy wskaźnik, ale jak okresowo aktualizować tekst i / lub ikonę wskaźnika, ponieważ chcesz, aby pokazywał (tekstowy) czas. Aby wskaźnik działał poprawnie, nie możemy po prostu threadingrozpocząć drugiego procesu okresowej aktualizacji interfejsu. Cóż, właściwie możemy, ale na dłuższą metę doprowadzi to do konfliktów, jak się dowiedziałem.

Oto, gdzie GObjectprzychodzi, do, jak to jest umieszczone w tym (również nieaktualnym) linku :

wywołanie gobject.threads_init()przy inicjalizacji aplikacji. Następnie uruchamiasz wątki normalnie, ale upewnij się, że wątki nigdy nie wykonują bezpośrednio żadnych zadań GUI. Zamiast tego używasz gobject.idle_addplanowania zadania GUI do wykonania w głównym wątku

Kiedy możemy zastąpić gobject.threads_init() przez GObject.threads_init()i gobject.idle_addprzez GObject.idle_add(), dość dużo mają zaktualizowaną wersję, jak uruchomić wątki w Gtkaplikacji. Uproszczony przykład pokazujący rosnącą liczbę małp:

wprowadź opis zdjęcia tutaj

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

To jest zasada. W rzeczywistym wskaźniku w tej odpowiedzi zarówno czas pętli, jak i tekst wskaźnika zostały określone przez dodatkowy moduł zaimportowany do skryptu, ale główna idea jest taka sama.

Jacob Vlijm
źródło