¿Cómo puedo escribir una aplicación / indicador de panel actualizado dinámicamente?

12

Estoy tratando de escribir algunas aplicaciones de panel para ubuntu Mate. Sé C / C ++ y SDL razonablemente bien. He visto la página de github de las aplicaciones del panel Mate-University, pero no puedo hacer que funcione correctamente / Estoy pasando un tiempo con ella.

Me pregunto si hay alguna vía fácil para escribir aplicaciones de panel. No estoy hablando de usar el iniciador de aplicaciones personalizado, me gustaría agregar nuevas funciones al panel, pero no estoy seguro de cómo hacerlo. Un tutorial o una descripción sobre la escritura de aplicaciones de panel podría ser realmente útil.

j0h
fuente

Respuestas:

16

Dado que lo que parece ser la ocasión para hacer esta pregunta ya tiene una respuesta , estoy respondiendo esta pregunta como una explicación extendida sobre cómo se hizo (en python)

Indicador estático básico

Dado que Ubuntu Mate, de 15,10, admite indicadores, no hay mucha diferencia entre escribir un indicador y una aplicación de panel para Mate. Por lo tanto, este enlace es un buen punto de partida para un indicador básico en el pythonuso de la AppIndicator3API. El enlace es un buen comienzo, pero no proporciona ninguna información sobre cómo mostrar el texto en el indicador, y mucho menos cómo actualizar el texto (o icono). Sin embargo, con algunas adiciones, esto lleva a un "marco" básico de un indicador como se muestra a continuación. Mostrará un icono, una etiqueta de texto y un menú:

ingrese la descripción de la imagen aquí

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

En la línea AppIndicator3.IndicatorCategory.OTHER, se define la categoría, como se explica en este enlace (parcialmente desactualizado) . Es importante establecer la categoría correcta para poner el indicador en una posición adecuada en el panel.

El principal desafío; Cómo actualizar el texto indicador y / o el ícono

El verdadero desafío no es cómo escribir un indicador básico, sino cómo actualizar periódicamente el texto y / o el icono de su indicador, ya que desea que muestre el tiempo (textual). Para que el indicador funcione correctamente, no podemos simplemente usar threadingpara iniciar un segundo proceso para actualizar periódicamente la interfaz. Bueno, en realidad podemos, pero a largo plazo, conducirá a conflictos, como descubrí.

Aquí es donde GObjectentra, a, como se coloca en este enlace (también desactualizado) :

llame gobject.threads_init()a la solicitud de inicialización. Luego inicia sus hilos normalmente, pero asegúrese de que los hilos nunca realicen ninguna tarea de GUI directamente. En su lugar, se utiliza gobject.idle_addpara programar la tarea GUI para que se ejecute en el hilo principal

Cuando se reemplaza gobject.threads_init() por GObject.threads_init()y gobject.idle_addpor GObject.idle_add(), prácticamente sólo tenemos la versión actualizada de cómo ejecutar subprocesos en una Gtkaplicación. Un ejemplo simplificado, que muestra un número creciente de monos:

ingrese la descripción de la imagen aquí

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

Ese es el principio. En el indicador real de esta respuesta , tanto el tiempo de bucle como el texto del indicador fueron determinados por un módulo secundario, importado en el script, pero la idea principal es la misma.

Jacob Vlijm
fuente