Como posso escrever um aplicativo / indicador de painel atualizado dinamicamente?

8

Estou tentando escrever alguns aplicativos de painel para o ubuntu Mate. Eu conheço C / C ++ e SDL razoavelmente bem. Eu vi a página do github dos aplicativos do painel Mate-University, mas não consigo que ela funcione corretamente / Eu estou tendo um tempo com ela.

Estou apenas imaginando, se há algum caminho fácil para escrever aplicativos de painel? Eu não estou falando sobre o uso do lançador de aplicativos personalizados, gostaria de adicionar novas funcionalidades, para o painel, mas não sei como. Um tutorial ou descrição sobre a criação de aplicativos do painel pode ser muito útil.

    
por j0h 29.03.2016 / 21:21

1 resposta

13

Desde que parece ser a ocasião para fazer esta pergunta já tem uma resposta , eu estou respondendo esta questão como explicação detalhada sobre como foi feito (em python )

Indicador básico estático

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, este link é um bom ponto de partida para um indicador básico em python , usando a API AppIndicator3 . O link é um bom começo, mas não fornece nenhuma informação sobre como mostrar o texto no indicador, muito menos como atualizar o texto (ou ícone). No entanto, com algumas adições, isso leva a um "quadro" básico de um indicador, conforme abaixo. Ele mostrará um ícone, um rótulo de texto e um menu:

#!/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 em este link (parcialmente desatualizado) . Definir a categoria correta é importante, a.o. para colocar o indicador em uma posição apropriada no painel.

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

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

Aqui é onde entra GObject , como é colocado neste link (também desatualizado) :

chama gobject.threads_init() na inicialização do aplicativo. Em seguida, você inicia seus encadeamentos normalmente, mas certifique-se de que os encadeamentos nunca realizem nenhuma tarefa da GUI diretamente. Em vez disso, você usa gobject.idle_add para agendar a tarefa da GUI para ser executada no thread principal

Quando substituímos gobject.threads_init() por GObject.threads_init() e gobject.idle_add por GObject.idle_add() , praticamente temos a versão atualizada de como executar encadeamentos em um aplicativo Gtk . Um exemplo simplificado, mostrando um número crescente de Monkeys:

#!/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 em esta resposta , tanto o tempo de loop quanto o texto do indicador foram determinados por um módulo secundário, importado no roteiro, mas a idéia principal é a mesma.

    
por Jacob Vlijm 12.04.2016 / 11:03