为什么 gtk_recent_manager_add_full () 默默地失败了?
Why does gtk_recent_manager_add_full () silently fail?
主要目标
我写了一个 python 简单的程序,可以将一个文件添加到 GTK3 中的最近文件列表中。它在 vim 打开文件时被调用。它工作正常,但是 vim 的启动时间乘以 10。现在我试图将它移植到 C 以改善这个缺陷。这是我尝试移植的 python 脚本的演示:
from gi import require_version
require_version('Gtk', '3.0')
from gi.repository import Gtk
manager = Gtk.RecentManager()
recent_data = Gtk.RecentData()
recent_data.app_name = "vim"
recent_data.mime_type = "text/plain"
recent_data.app_exec = "/usr/bin/vim"
manager.add_full("file:///home/lafleur/tweaks.txt", recent_data)
我的尝试
请参阅下面的代码示例。它编译得很好,但是当我 运行 它时我收到严重警告,然后过程成功,但该文件没有出现在 Nautilus 的最近文件中。
这是回溯:
$ ./a.out
adding file:///home/lafleur/tweaks.txt to recent files
(process:17646): GLib-GObject-CRITICAL **: 12:37:32.034: g_object_get: assertion 'G_IS_OBJECT (object)' failed
file added to recent files.
我不知道哪里出了问题。我关注了 GNOME's GTK3 documentation. Those docs state that the mandatory arguments to gtk_recent_manager_add_full ()
are the gtk_recent_manager, a uri and a GtkRecentData
object holding the file's MIME type, the application name and its callback. When compiled, the process complains that it needs an application description, which I added in the sample (see below). I found g_object_get ()
's definition here 中的文档,但这并没有给我任何线索。
问题
我的问题是:我如何知道发生了什么以及为什么该过程无法将现有 /home/lafleur/tweaks.txt
添加到 Nautilus 的最近文件列表中?我怎么知道我的代码中有什么不是有效的 GObject ?我是否错过了一些初始化,如 this SO answer 中所述?
这是代码示例:
#include <gtk/gtk.h>
int main (int argc, char **argv)
{
GtkRecentData recent_data;
GtkRecentManager *manager;
GError *error = NULL;
gchar *uri;
gboolean retval;
uri = g_filename_to_uri ("/home/lafleur/tweaks.txt", NULL, &error);
manager = gtk_recent_manager_get_default ();
if (error) {
g_warning ("%s", error->message);
g_error_free (error);
} else {
recent_data.mime_type = "text/plain";
recent_data.app_name = "vim";
recent_data.app_exec = "/usr/bin/vim";
recent_data.description = "the vim editor";
g_print ("adding %s to recent files\n", uri);
retval = gtk_recent_manager_add_full (
manager,
uri,
&recent_data
);
if (retval == TRUE) {
g_print ("file added to recent files.\n");
} else {
g_warning ("there was a problem.\n");
}
g_free (uri);
}
return retval;
当从 C 中使用 GTK API 时,您需要初始化 GTK 本身,例如:
gtk_init (&argc, &argv);
其中 argc
和 argv
是指向您在 main
中获得的参数计数器和向量的指针。如果您不这样做,那么接下来的任何行为都是未定义的行为。
Python 绑定,出于 GTK 1 和 2 时代的向后兼容性原因,导入时会自动调用 gtk_init()
。
此外,GtkRecentManager
在 GTK 主循环中对更新进行排队,以将多个写入合并为一个;这在 GUI 应用程序中通常不是问题,但如果您正在编写 CLI 工具,您还需要旋转主循环,直到 GtkRecentManager
发出“已更改”信号。
一旦您将程序修改为先调用 gtk_init()
,然后旋转主循环直到写入完成,您的程序就会运行,例如:
#include <stdlib.h>
#include <gtk/gtk.h>
int main (int argc, char **argv)
{
GError *error = NULL;
char *uri = g_filename_to_uri ("/home/lafleur/tweaks.txt", NULL, &error);
// Bail out early in case of error
if (error != NULL) {
g_warning ("%s", error->message);
g_error_free (error);
return EXIT_FAILURE;
}
// You can pass (NULL, NULL) if you don't have arguments; if you
// want to deal with the possibility of not having a display
// connection, you can use gtk_init_check() instead.
gtk_init (&argc, &argv);
// Create the recent manager
GtkRecentManager *manager = gtk_recent_manager_get_default ();
// Create a main loop; the recent manager will schedule writes
// within the loop, so it can coalesce multiple operations.
GMainLoop *loop = g_main_loop_new (NULL, FALSE);
// Once we receive the "changed" signal from the recent manager
// we stop the main loop; we use the "swapped" variant so that
// the callback will be invoked with the signal's user data
// as the first argument; in this case, we're going to call
// g_main_loop_quit() on the loop object
g_signal_connect_swapped (manager, "changed",
G_CALLBACK (g_main_loop_quit),
loop);
GtkRecentData recent_data = {
.mime_type = "text/plain",
.app_name = "vim",
// The "app_exec" key should be the command line needed to open
// the file you're adding; the %f escape sequence means "use the
// path of the file", whereas the %U escape sequence means "use
// the URL of the file".
.app_exec = "/usr/bin/vim %f",
.description = "the vim editor",
};
g_print ("adding %s to recent files\n", uri);
gboolean retval = gtk_recent_manager_add_full (
manager,
uri,
&recent_data
);
// Never compare boolean values for equality; gboolean is an int-sized
// type, which means anything that is not FALSE/0 will have a TRUE value
if (retval) {
g_print ("file added to recent files.\n");
} else {
g_warning ("there was a problem.\n");
}
g_free (uri);
// Start the loop; this will block until GtkRecentManager emits the
// "changed" signal, telling us the the recently used files list has
// been updated
g_main_loop_run (loop);
// Use standard exit codes
return retval ? EXIT_SUCCESS : EXIT_FAILURE;
}
由于您必须调用 gtk_init()
,因此您将 需要连接到您的显示服务器;这意味着您不能在图形会话之外的纯虚拟终端下 运行 该程序。如果您需要这样做,您将需要使用 GBookmarkFile
API 编写自己的“最近的管理器”,这与 GTK 内部使用的 API 相同。但是,GBookmarkFile
API 肯定是更底层的,它需要您了解最近使用的文件所使用的文件格式和位置。
主要目标
我写了一个 python 简单的程序,可以将一个文件添加到 GTK3 中的最近文件列表中。它在 vim 打开文件时被调用。它工作正常,但是 vim 的启动时间乘以 10。现在我试图将它移植到 C 以改善这个缺陷。这是我尝试移植的 python 脚本的演示:
from gi import require_version
require_version('Gtk', '3.0')
from gi.repository import Gtk
manager = Gtk.RecentManager()
recent_data = Gtk.RecentData()
recent_data.app_name = "vim"
recent_data.mime_type = "text/plain"
recent_data.app_exec = "/usr/bin/vim"
manager.add_full("file:///home/lafleur/tweaks.txt", recent_data)
我的尝试
请参阅下面的代码示例。它编译得很好,但是当我 运行 它时我收到严重警告,然后过程成功,但该文件没有出现在 Nautilus 的最近文件中。
这是回溯:
$ ./a.out
adding file:///home/lafleur/tweaks.txt to recent files
(process:17646): GLib-GObject-CRITICAL **: 12:37:32.034: g_object_get: assertion 'G_IS_OBJECT (object)' failed
file added to recent files.
我不知道哪里出了问题。我关注了 GNOME's GTK3 documentation. Those docs state that the mandatory arguments to gtk_recent_manager_add_full ()
are the gtk_recent_manager, a uri and a GtkRecentData
object holding the file's MIME type, the application name and its callback. When compiled, the process complains that it needs an application description, which I added in the sample (see below). I found g_object_get ()
's definition here 中的文档,但这并没有给我任何线索。
问题
我的问题是:我如何知道发生了什么以及为什么该过程无法将现有 /home/lafleur/tweaks.txt
添加到 Nautilus 的最近文件列表中?我怎么知道我的代码中有什么不是有效的 GObject ?我是否错过了一些初始化,如 this SO answer 中所述?
这是代码示例:
#include <gtk/gtk.h>
int main (int argc, char **argv)
{
GtkRecentData recent_data;
GtkRecentManager *manager;
GError *error = NULL;
gchar *uri;
gboolean retval;
uri = g_filename_to_uri ("/home/lafleur/tweaks.txt", NULL, &error);
manager = gtk_recent_manager_get_default ();
if (error) {
g_warning ("%s", error->message);
g_error_free (error);
} else {
recent_data.mime_type = "text/plain";
recent_data.app_name = "vim";
recent_data.app_exec = "/usr/bin/vim";
recent_data.description = "the vim editor";
g_print ("adding %s to recent files\n", uri);
retval = gtk_recent_manager_add_full (
manager,
uri,
&recent_data
);
if (retval == TRUE) {
g_print ("file added to recent files.\n");
} else {
g_warning ("there was a problem.\n");
}
g_free (uri);
}
return retval;
当从 C 中使用 GTK API 时,您需要初始化 GTK 本身,例如:
gtk_init (&argc, &argv);
其中 argc
和 argv
是指向您在 main
中获得的参数计数器和向量的指针。如果您不这样做,那么接下来的任何行为都是未定义的行为。
Python 绑定,出于 GTK 1 和 2 时代的向后兼容性原因,导入时会自动调用 gtk_init()
。
此外,GtkRecentManager
在 GTK 主循环中对更新进行排队,以将多个写入合并为一个;这在 GUI 应用程序中通常不是问题,但如果您正在编写 CLI 工具,您还需要旋转主循环,直到 GtkRecentManager
发出“已更改”信号。
一旦您将程序修改为先调用 gtk_init()
,然后旋转主循环直到写入完成,您的程序就会运行,例如:
#include <stdlib.h>
#include <gtk/gtk.h>
int main (int argc, char **argv)
{
GError *error = NULL;
char *uri = g_filename_to_uri ("/home/lafleur/tweaks.txt", NULL, &error);
// Bail out early in case of error
if (error != NULL) {
g_warning ("%s", error->message);
g_error_free (error);
return EXIT_FAILURE;
}
// You can pass (NULL, NULL) if you don't have arguments; if you
// want to deal with the possibility of not having a display
// connection, you can use gtk_init_check() instead.
gtk_init (&argc, &argv);
// Create the recent manager
GtkRecentManager *manager = gtk_recent_manager_get_default ();
// Create a main loop; the recent manager will schedule writes
// within the loop, so it can coalesce multiple operations.
GMainLoop *loop = g_main_loop_new (NULL, FALSE);
// Once we receive the "changed" signal from the recent manager
// we stop the main loop; we use the "swapped" variant so that
// the callback will be invoked with the signal's user data
// as the first argument; in this case, we're going to call
// g_main_loop_quit() on the loop object
g_signal_connect_swapped (manager, "changed",
G_CALLBACK (g_main_loop_quit),
loop);
GtkRecentData recent_data = {
.mime_type = "text/plain",
.app_name = "vim",
// The "app_exec" key should be the command line needed to open
// the file you're adding; the %f escape sequence means "use the
// path of the file", whereas the %U escape sequence means "use
// the URL of the file".
.app_exec = "/usr/bin/vim %f",
.description = "the vim editor",
};
g_print ("adding %s to recent files\n", uri);
gboolean retval = gtk_recent_manager_add_full (
manager,
uri,
&recent_data
);
// Never compare boolean values for equality; gboolean is an int-sized
// type, which means anything that is not FALSE/0 will have a TRUE value
if (retval) {
g_print ("file added to recent files.\n");
} else {
g_warning ("there was a problem.\n");
}
g_free (uri);
// Start the loop; this will block until GtkRecentManager emits the
// "changed" signal, telling us the the recently used files list has
// been updated
g_main_loop_run (loop);
// Use standard exit codes
return retval ? EXIT_SUCCESS : EXIT_FAILURE;
}
由于您必须调用 gtk_init()
,因此您将 需要连接到您的显示服务器;这意味着您不能在图形会话之外的纯虚拟终端下 运行 该程序。如果您需要这样做,您将需要使用 GBookmarkFile
API 编写自己的“最近的管理器”,这与 GTK 内部使用的 API 相同。但是,GBookmarkFile
API 肯定是更底层的,它需要您了解最近使用的文件所使用的文件格式和位置。