背景:
不适用于应用指标但系统指标。
图片来自:https://wiki.ubuntu.com/DesktopExperienceTeam/ApplicationIndicators
目的是展示指标-系统监视器在 Greeter/Lock/Ubiquity 屏幕中。有一个解决方法:
C 原始代码:(工作正常)
我已经用 C 语言实现了一个,请参见我的另一个问题:
但是,
indicator-sysmonitor
与许多其他应用程序指示器一样,它已经用 Python 开发。我不喜欢开发人员必须将他们的项目移植到 C 或编写 Python-C 代理才能在欢迎/锁定/通用屏幕中显示指示器的想法。相反,让 indicator-sysmonitor 直接从 python 创建系统指示器将是最好的解决方案(没有变通方法,它将成为所有当前使用 appindicator 的 python 项目的通用解决方案)。
Python 代码:(我将 C 代码移植到 Python 的尝试失败了)
我正在努力将它移植到 Python 中。这是我当前无法运行的代码。它确实为菜单和操作创建了 DBus 对象。它在 XFCE 指示器插件中列出。但未显示在面板上。
/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']
问题:
我猜测原因是,我没有创建菜单结构或其元数据(类似的伪项_header
),就像在原始 C 代码中一样。
有人可以制作一个工作端口吗此系统指示器代码用 C 语言编写到 Python?
答案1
我刚刚上传了一个原始的“可运行” Python 示例,它移植自@user.dz C 示例。这里是源代码存储库:
我会随时更新它,但欢迎任何贡献。
感谢提供有用的信息!
移植了主脚本的源代码。注意:这是初始副本,因为链接将来可能会中断。要获取完整包和最新更新,请点击上面的链接。
#!/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)
答案2
系统指示器服务
嗯,它确实比我预期的要简单。它没有特定的 API。因为它只是一个 GSimpleActionGroup 和通过 DBus 导出的相应 GMenu,所以 Unity 可以使用放入的同名声明文件来了解它们的存在/usr/share/unity/indicators/
。不需要任何其他库。
这里有一个很小的 C 语言示例:
tests/indicator-test-service.c
从libindicator
源获取副本
apt-get source libindicator
cp libindicator-*/tests/indicator-test-service.c .
cp libindicator-*/tests/com.canonical.indicator.test*
。 指标测试服务.c没有变化
**#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.测试修改添加锁定和迎宾模式
[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.测试.服务从文件名中删除 .in 后缀并更改可执行路径
[D-BUS Service]
Name=com.canonical.indicator.test
Exec=/usr/lib/x86_64-linux-gnu/indicator-test/indicator-test-service
编译
gcc -o indicator-test-service indicator-test-service.c pkg-config --cflags --libs gtk+-3.0
手动安装
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/
Greeter 的配置,覆盖默认指标列表
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']
安装
cp 90_unity-greeter.gschema.override /usr/share/glib-2.0/schemas/
glib-compile-schemas /usr/share/glib-2.0/schemas/
测试
sudo service lightdm restart
笔记 如果希望用户能够随时关闭应用程序,DBus 服务会很麻烦。最好使用自动启动,就像默认指示器那样。
我已将准备好的文件上传到这里:
https://github.com/sneetsher/mysystemindicator_minimum
修改后的副本如下:
https://github.com/sneetsher/mysystemindicator
我尝试了不同模式的不同菜单。可以快速安装和测试。
这似乎太简单了,可以轻松移植到任何其他支持 GIO Gnome lib(包括 DBus)的语言。由于我正在寻找 python,我可能会稍后添加它。
参考: libindicator README:指标服务文件格式https://bazaar.launchpad.net/~indicator-applet-developers/libindicator/trunk.14.04/view/head:/README