Ask Ubuntu Asked by user.dz on December 9, 2020
This is NOT for application-indicators but system-indicators.
Picture from: https://wiki.ubuntu.com/DesktopExperienceTeam/ApplicationIndicators
The objective is to show indicator-sysmonitor in Greeter/Lock/Ubiquity screens. There is a work around in:
How to make indicator-sysmonitor as a default indicator on the login screen
I already get one working in C language, see my other question:
How to develop a System Indicator for Unity?
However, indicator-sysmonitor
is already developed in Python as many other application indicators. I don’t like the idea that developers obliged to port their projects to C or write a Python-C proxy if they want to show the indicator in greeter/lock/ubiquity screens. Instead, making indicator-sysmonitor creates a system indicator directly from python would be the best solution (no workarounds, and it will be a generic solution for all python projects that currently using appindicator).
I’m struggling to port it into Python. Here is my current code which doesn’t work. It does create DBus object for both Menu & Actions. It is listed in the XFCE indicators plugin. But not showed on the panel.
/usr/lib/indicator-test/indicator-test-service
#!/usr/bin/python2
import os
import sys
import gi
from gi.repository import Gio, GLib
APPLICATION_ID = 'local.sneetsher.indicator.test'
DBUS_MENU_PATH = '/local/sneetsher/indicator/test/desktop'
DBUS_ACTION_PATH = '/local/sneetsher/indicator/test'
def callback():
print ok
def quit_callback(notification, loop):
global connection
global exported_action_group_id
global exported_menu_model_id
connection.unexport_action_group (exported_action_group_id)
connection.unexport_menu_model (exported_menu_model_id)
loop.quit()
def cancel (notification, action, data):
if action == "cancel":
print "Cancel"
else:
print "That should not have happened (cancel)!"
def bus_acquired(bus, name):
# menu
submenu = Gio.Menu()
submenu.append("Show", "show")
item = Gio.MenuItem.new(None, "_header")
item.set_attribute([("x-canonical-type","s","com.canonical.indicator.root")])
item.set_submenu(submenu)
menu = Gio.Menu()
menu.append_item (item)
actions = Gio.SimpleActionGroup.new()
action1 = Gio.SimpleAction.new("_header", None)
actions.insert(action1)
action2 = Gio.SimpleAction.new('show', None)
actions.insert(action2)
action2.connect("activate",callback)
global connection
connection = bus
global exported_action_group_id
exported_action_group_id = connection.export_action_group(DBUS_ACTION_PATH, actions)
global exported_menu_model_id
exported_menu_model_id = connection.export_menu_model(DBUS_MENU_PATH, menu)
def setup ():
#bus connection
Gio.bus_own_name(Gio.BusType.SESSION, APPLICATION_ID, 0, bus_acquired, None, None)
if __name__ == '__main__':
connection = None
exported_menu_model_id = 0
exported_action_group_id = 0
password = ""
loop = GLib.MainLoop()
setup ()
loop.run()
local.sneetsher.indicator.test
[Indicator Service]
Name=indicator-test
ObjectPath=/local/sneetsher/indicator/test
[desktop]
ObjectPath=/local/sneetsher/indicator/test/desktop
[desktop_greeter]
ObjectPath=/local/sneetsher/indicator/test/desktop
[desktop_lockscreen]
ObjectPath=/local/sneetsher/indicator/test/desktop
local.sneetsher.indicator.test.service
[D-BUS Service]
Name=local.sneetsher.indicator.test
Exec=/usr/lib/indicator-test/indicator-test-service
90_unity-greeter.gschema.override
[com.canonical.unity-greeter]
indicators=['ug-accessibility', 'com.canonical.indicator.keyboard', 'com.canonical.indicator.session', 'com.canonical.indicator.datetime', 'com.canonical.indicator.power', 'com.canonical.indicator.sound', 'local.sneetsher.indicator.test', 'application']
I expect the reason why, I didn’t create the menu structure or its meta (pseudo items like _header
) as they are in the original C code.
Could anyone make a working port of this system indicator code in C to Python?
I've just uploaded a raw "working" Python example ported from the @user.dz C example. Here's the source code repository:
I will update it as I go along but any contribution is welcome.
Thanks for the useful information!
Ported source code of the main script. Note: It is initial copy as link may broke in future. For complete package and last update, follow the link above.
#!/usr/bin/python3
import sys
import os
import logging
import logging.handlers
logger = logging.getLogger('LoginHelper')
logger.setLevel(logging.DEBUG)
handler = logging.handlers.SysLogHandler(address = '/dev/log')
logger.addHandler(handler)
logger.debug("Login-Helper: Start")
os.environ["DISPLAY"] = ":0"
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import Gio
from gi.repository import GLib
class LoginHelperIndicator():
def __init__(self, dbus_name, dbus_path):
self.dbus_name = dbus_name
self.dbus_path = dbus_path
self.actions = Gio.SimpleActionGroup()
self.menu = Gio.Menu()
self.actions_export_id = 0
self.menu_export_id = 0
def activate_about (self, action, parameter):
# gtk_show_about_dialog(NULL,
# "program-name", PROJECT_NAME,
# "title", "About " PROJECT_NAME,
# "version", PROJECT_VERSION_MAJOR "." PROJECT_VERSION_MINOR,
# "license_type", GTK_LICENSE_GPL_3_0,
# "wrap_license", TRUE,
# "website", "https://github.com/sneetsher/mysystemindicator",
# "website_label", "https://github.com/sneetsher/mysystemindicator",
# "logo_icon_name", "indicator-" SHORT_NAME,
# NULL);
# g_message ("showing about dialog");
pass
def activate_private (self, action, parameter):
#g_message ("clicked private menu entry");
pass
def activate_exit (self, action, parameter):
#g_message ("exit the program");
Gtk.main_quit()
def on_bus_acquired (self, connection, name):
logger.debug ('Bus acquired: ' + str(connection))
error = None
item = Gio.MenuItem()
submenu = Gio.Menu()
action_state = GLib.Variant("a{sv}", {
'label': GLib.Variant("s", "Login-helper"),
'icon': GLib.Variant("s", "indicator-loginhelper"),
'accessible-desc': GLib.Variant("s", "Login Helper indicator")
})
self.actions.add_action(Gio.SimpleAction.new_stateful("_header", None, action_state))
about_action = Gio.SimpleAction.new("about", None)
about_action.connect("activate", self.activate_about)
self.actions.add_action(about_action)
private_action = Gio.SimpleAction.new("private", None)
private_action.connect("activate", self.activate_private)
self.actions.add_action(private_action)
exit_action = Gio.SimpleAction.new("exit", None)
exit_action.connect("activate", self.activate_exit)
self.actions.add_action(exit_action)
submenu.append("About", "indicator.about")
submenu.append("Exit", "indicator.exit")
item = Gio.MenuItem().new(None, "indicator._header")
item.set_attribute_value("x-canonical-type", GLib.Variant("s", "com.canonical.indicator.root"))
item.set_submenu(submenu)
self.menu = Gio.Menu.new()
self.menu.append_item(item)
self.actions_export_id = connection.export_action_group(self.dbus_path, self.actions)
if self.actions_export_id == 0:
#g_warning ("cannot export action group: %s", error->message);
#g_error_free (error);
return
self.menu_export_id = connection.export_menu_model(self.dbus_path + "/greeter", self.menu)
if self.menu_export_id == 0:
#g_warning ("cannot export menu: %s", error->message);
#g_error_free (error);
return
def on_name_lost (self, connection, name):
if self.actions_export_id:
connection.unexport_action_group(self.actions_export_id)
if (self.menu_export_id):
connection.unexport_menu_model(self.menu_export_id)
if __name__ == '__main__':
logger.debug('Login-Helper: Initializing Login Helper Indicator')
res_gtk = Gtk.init(sys.argv)
indicator = LoginHelperIndicator('com.canonical.indicator.loginhelper', '/com/canonical/indicator/loginhelper')
logger.debug ('Login-Helper: Res_gtk: ' + str(res_gtk))
res_own = Gio.bus_own_name(Gio.BusType.SESSION,
indicator.dbus_name,
Gio.BusNameOwnerFlags.NONE,
indicator.on_bus_acquired,
None,
indicator.on_name_lost)
logger.debug ('Login-Helper: Res_own: ' + str(res_own))
Gtk.main()
exit(0)
Correct answer by Marto on December 9, 2020
System Indicator Service
Well, it is really simpler then I expected. There is no specific API for it. Because it is just a GSimpleActionGroup & with corresponding GMenu's exported through DBus then Unity is told about their presence using declaration file with same name put in /usr/share/unity/indicators/
. No need for any other library.
Here a very small C language example:
Get a copy of tests/indicator-test-service.c
from libindicator
source
apt-get source libindicator
cp libindicator-*/tests/indicator-test-service.c .
cp libindicator-*/tests/com.canonical.indicator.test*
. indicator-test-service.c no changes
**#include <gio/gio.h>
typedef struct
{
GSimpleActionGroup *actions;
GMenu *menu;
guint actions_export_id;
guint menu_export_id;
} IndicatorTestService;
static void
bus_acquired (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
IndicatorTestService *indicator = user_data;
GError *error = NULL;
indicator->actions_export_id = g_dbus_connection_export_action_group (connection,
"/com/canonical/indicator/test",
G_ACTION_GROUP (indicator->actions),
&error);
if (indicator->actions_export_id == 0)
{
g_warning ("cannot export action group: %s", error->message);
g_error_free (error);
return;
}
indicator->menu_export_id = g_dbus_connection_export_menu_model (connection,
"/com/canonical/indicator/test/desktop",
G_MENU_MODEL (indicator->menu),
&error);
if (indicator->menu_export_id == 0)
{
g_warning ("cannot export menu: %s", error->message);
g_error_free (error);
return;
}
}
static void
name_lost (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
IndicatorTestService *indicator = user_data;
if (indicator->actions_export_id)
g_dbus_connection_unexport_action_group (connection, indicator->actions_export_id);
if (indicator->menu_export_id)
g_dbus_connection_unexport_menu_model (connection, indicator->menu_export_id);
}
static void
activate_show (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
g_message ("showing");
}
int
main (int argc, char **argv)
{
IndicatorTestService indicator = { 0 };
GMenuItem *item;
GMenu *submenu;
GActionEntry entries[] = {
{ "_header", NULL, NULL, "{'label': <'Test'>,"
" 'icon': <'indicator-test'>,"
" 'accessible-desc': <'Test indicator'> }", NULL },
{ "show", activate_show, NULL, NULL, NULL }
};
GMainLoop *loop;
indicator.actions = g_simple_action_group_new ();
g_simple_action_group_add_entries (indicator.actions, entries, G_N_ELEMENTS (entries), NULL);
submenu = g_menu_new ();
g_menu_append (submenu, "Show", "indicator.show");
item = g_menu_item_new (NULL, "indicator._header");
g_menu_item_set_attribute (item, "x-canonical-type", "s", "com.canonical.indicator.root");
g_menu_item_set_submenu (item, G_MENU_MODEL (submenu));
indicator.menu = g_menu_new ();
g_menu_append_item (indicator.menu, item);
g_bus_own_name (G_BUS_TYPE_SESSION,
"com.canonical.indicator.test",
G_BUS_NAME_OWNER_FLAGS_NONE,
bus_acquired,
NULL,
name_lost,
&indicator,
NULL);
loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (loop);
g_object_unref (submenu);
g_object_unref (item);
g_object_unref (indicator.actions);
g_object_unref (indicator.menu);
g_object_unref (loop);
return 0;
}**
com.canonical.indicator.test modified to add lock & greeter mode
[Indicator Service]
Name=indicator-test
ObjectPath=/com/canonical/indicator/test
[desktop]
ObjectPath=/com/canonical/indicator/test/desktop
[desktop_greeter]
ObjectPath=/com/canonical/indicator/test/desktop
[desktop_lockscreen]
ObjectPath=/com/canonical/indicator/test/desktop
com.canonical.indicator.test.service remove .in postfix from filename and change the executable path
[D-BUS Service]
Name=com.canonical.indicator.test
Exec=/usr/lib/x86_64-linux-gnu/indicator-test/indicator-test-service
Compile it
gcc -o indicator-test-service indicator-test-service.c pkg-config --cflags --libs gtk+-3.0
Manual Installation
sudo su
mkdir /usr/lib/x86_64-linux-gnu/indicator-test/
cp indicator-test-service /usr/lib/x86_64-linux-gnu/indicator-test/
cp com.canonical.indicator.test /usr/share/unity/indicators/
cp com.canonical.indicator.test.service /usr/share/dbus-1/services/
Configuration for Greeter, override the default indicators list
90_unity-greeter.gschema.override
com.canonical.unity-greeter]
indicators=['ug-accessibility', 'com.canonical.indicator.keyboard', 'com.canonical.indicator.session', 'com.canonical.indicator.datetime', 'com.canonical.indicator.power', 'com.canonical.indicator.sound', 'com.canonical.indicator.test', 'application']
Install
cp 90_unity-greeter.gschema.override /usr/share/glib-2.0/schemas/
glib-compile-schemas /usr/share/glib-2.0/schemas/
Test
sudo service lightdm restart
Notes DBus service is troublesome, if you want user to be able to close application anytime. It is better to use autostart instead, like default indicators do.
I have uploaded ready files here:
https://github.com/sneetsher/mysystemindicator_minimum
and a modified copy here:
https://github.com/sneetsher/mysystemindicator
Where I have tried different menu for different mode. It could be installed and tested quickly.
This seems too simple and can be easily ported to any other language that have support for GIO Gnome lib (including DBus). As I'm looking for python, I may add it later.
References: libindicator README: The indicator service file format https://bazaar.launchpad.net/~indicator-applet-developers/libindicator/trunk.14.04/view/head:/README
Answered by DCM on December 9, 2020
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP