在 GTK+ 的 .ui 文件中制作已知的子类小部件
Make known subclassed wigets in .ui files in GTK+
我正在查看 GNOME 日历应用程序,gcal-window.ui
文件中有这一行(在 292 上):
<object class="GcalQuickAddPopover" id="quick_add_popover">
在 gcal-window
ui、源代码和头文件所在的同一目录中,有定义 GcalQuickAddPopover
的文件。使 .ui
文件知道哪些对象存在,哪些不存在的规则是什么。如果我删除了 gcal-quick-add-popover 文件,它怎么知道或不知道它在那里?
原回答
这是通过使用 build 系统实现的。通常在 Gnome 中,这是 Meson Build System(在它是 Autotools 之前)。
作为非常简化的解释,此工具在运行时将查看各种meson.build
文件(例如,this one 用于视图)和 build 一个 Makefile 来 build 项目。在这些文件中,描述了文件和资源之间的依赖关系。实际上,介子的意义远不止于此,我鼓励您阅读它。
这是位于 gui
目录下的 meson.build
文件:
subdir('calendar-management')
subdir('event-editor')
subdir('gtk')
subdir('icons')
subdir('importer')
subdir('views')
calendar_incs += include_directories('.')
built_sources += gnome.compile_resources(
'gui-resources',
'gui.gresource.xml',
c_name: 'gui',
)
sources += files(
'gcal-application.c',
'gcal-calendar-popover.c',
'gcal-event-popover.c',
'gcal-event-widget.c',
'gcal-expandable-entry.c',
'gcal-meeting-row.c',
'gcal-quick-add-popover.c',
'gcal-search-button.c',
'gcal-weather-settings.c',
'gcal-window.c',
)
第6行可以看到包含了子目录views
。这是 gcal-window.ui
了解其观点的地方。
跟进
如果您要删除 quick_add_popover.ui
文件,当尝试使用您的实际 Makefile 进行 build 时,build 将失败,因为 Makefile 中的某处 Meson 会已经为您编写,该资源(在本例中为 quick_add_popover.ui
)将被引用,但在您的代码中它将消失。
要查看的其他有趣文件:
gui.gresource.xml
gui
。这是定义 ui 文件资源的地方。
meson.build
文件在 gui
下。这是通过 gnome.compile_resources
函数让 Meson 知道 gui.gressource.xml
的地方。
我建议您阅读有关介子的内容或在 YouTube 上查看以了解更多信息。
可以在 GtkBuilder UI definition 中使用的类型是在解析 XML 之前在 运行-time 注册的类型(通常通过调用 gtk_builder_new
或类似的方式)。
这是一个独立的例子来说明这一点。
/* cc ui.c -o ui $(pkg-config --cflags --libs gtk+-3.0) */
#include <gtk/gtk.h>
/* Define new type (MyCustomLabel) */
#define MY_TYPE_CUSTOM_LABEL my_custom_label_get_type()
struct _MyCustomLabel { GtkLabel label; };
G_DECLARE_FINAL_TYPE(MyCustomLabel, my_custom_label, MY, CUSTOM_LABEL, GtkLabel)
G_DEFINE_TYPE(MyCustomLabel, my_custom_label, GTK_TYPE_LABEL)
static void
my_custom_label_class_init(MyCustomLabelClass *cls)
{
}
static void
my_custom_label_init(MyCustomLabel *label)
{
gtk_label_set_label(GTK_LABEL(label), "This is my custom label");
}
/* Test case */
static const gchar *ui =
"<interface>"
"<object id='W' class='GtkWindow'>"
"<child>"
"<object class='MyCustomLabel'/>"
"</child>"
"</object>"
"</interface>";
int main()
{
GtkBuilder *builder;
GtkWidget *window;
gtk_init(NULL, NULL);
/* Ensures that MyCustomLabel has been registered with the type
* system. Without this call, the application crashes when parsing
* `ui`, i.e. at the `gtk_builder_new_from_string` call. */
g_type_ensure(MY_TYPE_CUSTOM_LABEL);
builder = gtk_builder_new_from_string(ui, -1);
window = GTK_WIDGET(gtk_builder_get_object(builder, "W"));
g_object_unref(builder);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
有关类型系统如何工作的更多详细信息,请参阅 GObject documentation。
我正在查看 GNOME 日历应用程序,gcal-window.ui
文件中有这一行(在 292 上):
<object class="GcalQuickAddPopover" id="quick_add_popover">
在 gcal-window
ui、源代码和头文件所在的同一目录中,有定义 GcalQuickAddPopover
的文件。使 .ui
文件知道哪些对象存在,哪些不存在的规则是什么。如果我删除了 gcal-quick-add-popover 文件,它怎么知道或不知道它在那里?
原回答
这是通过使用 build 系统实现的。通常在 Gnome 中,这是 Meson Build System(在它是 Autotools 之前)。
作为非常简化的解释,此工具在运行时将查看各种meson.build
文件(例如,this one 用于视图)和 build 一个 Makefile 来 build 项目。在这些文件中,描述了文件和资源之间的依赖关系。实际上,介子的意义远不止于此,我鼓励您阅读它。
这是位于 gui
目录下的 meson.build
文件:
subdir('calendar-management')
subdir('event-editor')
subdir('gtk')
subdir('icons')
subdir('importer')
subdir('views')
calendar_incs += include_directories('.')
built_sources += gnome.compile_resources(
'gui-resources',
'gui.gresource.xml',
c_name: 'gui',
)
sources += files(
'gcal-application.c',
'gcal-calendar-popover.c',
'gcal-event-popover.c',
'gcal-event-widget.c',
'gcal-expandable-entry.c',
'gcal-meeting-row.c',
'gcal-quick-add-popover.c',
'gcal-search-button.c',
'gcal-weather-settings.c',
'gcal-window.c',
)
第6行可以看到包含了子目录views
。这是 gcal-window.ui
了解其观点的地方。
跟进
如果您要删除 quick_add_popover.ui
文件,当尝试使用您的实际 Makefile 进行 build 时,build 将失败,因为 Makefile 中的某处 Meson 会已经为您编写,该资源(在本例中为 quick_add_popover.ui
)将被引用,但在您的代码中它将消失。
要查看的其他有趣文件:
gui.gresource.xml
gui
。这是定义 ui 文件资源的地方。meson.build
文件在gui
下。这是通过gnome.compile_resources
函数让 Meson 知道gui.gressource.xml
的地方。
我建议您阅读有关介子的内容或在 YouTube 上查看以了解更多信息。
可以在 GtkBuilder UI definition 中使用的类型是在解析 XML 之前在 运行-time 注册的类型(通常通过调用 gtk_builder_new
或类似的方式)。
这是一个独立的例子来说明这一点。
/* cc ui.c -o ui $(pkg-config --cflags --libs gtk+-3.0) */
#include <gtk/gtk.h>
/* Define new type (MyCustomLabel) */
#define MY_TYPE_CUSTOM_LABEL my_custom_label_get_type()
struct _MyCustomLabel { GtkLabel label; };
G_DECLARE_FINAL_TYPE(MyCustomLabel, my_custom_label, MY, CUSTOM_LABEL, GtkLabel)
G_DEFINE_TYPE(MyCustomLabel, my_custom_label, GTK_TYPE_LABEL)
static void
my_custom_label_class_init(MyCustomLabelClass *cls)
{
}
static void
my_custom_label_init(MyCustomLabel *label)
{
gtk_label_set_label(GTK_LABEL(label), "This is my custom label");
}
/* Test case */
static const gchar *ui =
"<interface>"
"<object id='W' class='GtkWindow'>"
"<child>"
"<object class='MyCustomLabel'/>"
"</child>"
"</object>"
"</interface>";
int main()
{
GtkBuilder *builder;
GtkWidget *window;
gtk_init(NULL, NULL);
/* Ensures that MyCustomLabel has been registered with the type
* system. Without this call, the application crashes when parsing
* `ui`, i.e. at the `gtk_builder_new_from_string` call. */
g_type_ensure(MY_TYPE_CUSTOM_LABEL);
builder = gtk_builder_new_from_string(ui, -1);
window = GTK_WIDGET(gtk_builder_get_object(builder, "W"));
g_object_unref(builder);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
有关类型系统如何工作的更多详细信息,请参阅 GObject documentation。