Como escrever um aplicativo / indicador de painel atualizado dinamicamente?

12

Estou tentando escrever alguns aplicativos de painel para o ubuntu Mate. Eu sei C / C ++ e SDL razoavelmente bem. Eu vi a página do github de aplicativos do painel Mate-University, mas não consigo fazê-la funcionar corretamente.

Estou apenas pensando, se existe algum caminho fácil para escrever aplicativos de painel? Não estou falando sobre o uso do iniciador de aplicativos personalizado, gostaria de adicionar novas funcionalidades ao painel, mas não sei ao certo como. Um tutorial ou descrição sobre como escrever aplicativos do painel pode ser realmente útil.

j0h
fonte

Respostas:

16

Como o que parece ser a ocasião para fazer esta pergunta já tem uma resposta , eu estou respondendo a essa pergunta como uma explicação detalhada de como foi feita (in python)

Indicador estático básico

Como o Ubuntu Mate, de 15,10, suporta indicadores, não há muita diferença entre escrever um indicador e um aplicativo de painel para o Mate. Portanto, esse link é um bom ponto de partida para um indicador básico python, usando a AppIndicator3API. O link é um bom começo, mas não fornece nenhuma informação sobre como mostrar texto no indicador, muito menos em como atualizar o texto (ou ícone). No entanto, com algumas adições, isso leva a um "quadro" básico de um indicador como abaixo. Ele mostrará um ícone, um rótulo de texto e um menu:

insira a descrição da imagem aqui

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

Na linha AppIndicator3.IndicatorCategory.OTHER, a categoria é definida, conforme explicado neste link (parcialmente desatualizado) . É importante definir a categoria correta, ao colocar o indicador em uma posição apropriada no painel.

O principal desafio; como atualizar o texto e / ou ícone do indicador

O verdadeiro desafio não é como escrever um indicador básico, mas como atualizar periodicamente o texto e / ou o ícone do seu indicador, pois você deseja que ele mostre o tempo (textual). Para fazer o indicador funcionar corretamente, não podemos simplesmente usar threadingpara iniciar um segundo processo para atualizar periodicamente a interface. Bem, na verdade podemos, mas, a longo prazo, isso levará a conflitos, como descobri.

Aqui é onde GObjectentra, para, como é colocado neste link (também desatualizado) :

chamar gobject.threads_init()na inicialização do aplicativo. Em seguida, você inicia seus encadeamentos normalmente, mas verifique se os encadeamentos nunca realizam nenhuma tarefa da GUI diretamente. Em vez disso, você usa gobject.idle_addpara agendar tarefas da GUI executadas no encadeamento principal

Quando substituímos gobject.threads_init() por GObject.threads_init()e gobject.idle_addpor GObject.idle_add(), nós praticamente temos a versão atualizada de como executar threads em um Gtkaplicativo. Um exemplo simplificado, mostrando um número crescente de macacos:

insira a descrição da imagem aqui

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

Esse é o princípio. No indicador real nesta resposta , o tempo do loop e o texto do indicador foram determinados por um módulo secundário, importado no script, mas a idéia principal é a mesma.

Jacob Vlijm
fonte