Ubuntu – AppIndicator3: Set indicator icon from file name or GdkPixbuf

iconsindicatorpython

I'm writing a small Python 3 app which uses AppIndicator3 to place an icon in the top bar, and then change that icon in response to user actions. Simple, right? Because the app is small, it needs to run from its source directory without any kind of installation.

The problem is that AppIndicator3.set_icon() requires a str with an icon name, not a path to said icon.

How can I persuade AppIndicator3 to let me give it either a filename or a Pixbuf? Alternatively, how can I add my icon directory to the icon name search path? (I tried AppIndicator3.set_icon_theme_path(), but my icon names are still unrecognized.

Best Answer

To use a path to the icon is best illustrated with an example. In the example below, I keep the icons in the same directory as the script (indicator), which seems a convenient solution in your case.

The bottom line is that once you initiated your indicator:

class Indicator():
    def __init__(self):
        self.app = "<indicator_name>"
        iconpath = "/path/to/initial/icon/"

        -------------------------------

        self.testindicator = AppIndicator3.Indicator.new(
        self.app, iconpath,
        AppIndicator3.IndicatorCategory.OTHER)

        -------------------------------

you can change the icon with:

        self.testindicator.set_icon("/path/to/new/icon/")

enter image description here enter image description here

Example

In the example below, all icons, nocolor.png, purple.png and green.png are stored together with the script, but the path to the icons, set in

currpath = os.path.dirname(os.path.realpath(__file__))

could be anywhere.

#!/usr/bin/env python3
import os
import signal
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('AppIndicator3', '0.1')
from gi.repository import Gtk, AppIndicator3

currpath = os.path.dirname(os.path.realpath(__file__))

class Indicator():
    def __init__(self):
        self.app = 'show_proc'
        iconpath = currpath+"/nocolor.png"
        # after you defined the initial indicator, you can alter the icon!
        self.testindicator = AppIndicator3.Indicator.new(
            self.app, iconpath,
            AppIndicator3.IndicatorCategory.OTHER)
        self.testindicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE)       
        self.testindicator.set_menu(self.create_menu())

    def create_menu(self):
        menu = Gtk.Menu()
        item_quit = Gtk.MenuItem(label='Quit')
        item_quit.connect('activate', self.stop)
        item_green = Gtk.MenuItem(label='Green')
        item_green.connect('activate', self.green)
        item_purple = Gtk.MenuItem(label='Purple')
        item_purple.connect('activate', self.purple)
        menu.append(item_quit)
        menu.append(item_green)
        menu.append(item_purple)
        menu.show_all()
        return menu

    def stop(self, source):
        Gtk.main_quit()

    def green(self, source):
        self.testindicator.set_icon(currpath+"/green.png")

    def purple(self, source):
        self.testindicator.set_icon(currpath+"/purple.png")

Indicator()
signal.signal(signal.SIGINT, signal.SIG_DFL)
Gtk.main()

Note

...that if you need to update the icon from a second thread, you need to use

GObject.threads_init()

before

Gtk.main()

and you need to update the interface (either icon or indicator text) by using:

GObject.idle_add()

as applied e.g. here and here.

Related Question