14-3 小节中,helloworld程序创建窗口使用代码创建。此外,也可使用 xml 完成窗口布局,代码仅仅用来处理数据逻辑,实现布局和数据处理相分离。
布局的 ui 代码,builder.ui:
<interface>
<object id="window" class="GtkWindow">
<property name="visible">True</property>
<property name="title">Grid</property>
<property name="border-width">10</property>
<child>
<object id="grid" class="GtkGrid">
<property name="visible">True</property>
<child>
<object id="button1" class="GtkButton">
<property name="visible">True</property>
<property name="label">Button 1</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object id="button2" class="GtkButton">
<property name="visible">True</property>
<property name="label">Button 2</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object id="quit" class="GtkButton">
<property name="visible">True</property>
<property name="label">Quit</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
<property name="width">2</property>
</packing>
</child>
</object>
<packing>
</packing>
</child>
</object>
</interface>
具体代码如下。与 14-3 小节相比,不再显示地创建 GtkApplication 进行程序的执行,而是利用 GObject 和 GtkBuilder 完成程序执行。
#include <gtk/gtk.h>
/*
* 定义一个打印函数,在点击按钮时调用该函数
* @param widget: 相当于GtkContainer 容器类的父类
* @param data: 用户点击按钮时传入的数据
*/
static void print_hello(GtkWidget *widget, char const *data){
g_print("hello: %s\n", data);
}
// 给定 ui 文件,进行 helloworld 程序编写
int main(int argc, char **argv){
gtk_init(&argc, &argv);
// 1. 加载布局文件 builder.ui
// 新建builder
GtkBuilder *builder = gtk_builder_new();
// 利用 builder 加载布局文件
GError *error = NULL;
if(gtk_builder_add_from_file(builder, "builder.ui", &error) == 0){ // 如果加载失败
g_printerr("Error loading files: %s\n", error->message);
g_clear_error(&error); // 退出之前不要忘记释放相应内存
return -1;
}
// 2. 获取布局文件中各构件,利用 ui 文件中的各构件 id 进行获取
// 获取窗口对象,其在 ui 文件中的 id 为 "window"
GObject *window = gtk_builder_get_object(builder, "window");
// 获取按钮1,按钮2,以及退出按钮
GObject *button1 = gtk_builder_get_object(builder, "button1");
GObject *button2 = gtk_builder_get_object(builder, "button2");
GObject *quit = gtk_builder_get_object(builder, "quit");
// 3. 对各构件进行事件的绑定
// 对窗口进行事件绑定
// destroy 相当于点击窗体右上角的 x 按钮,不绑定的情况下,点击 x 窗口消失,但主程序并不会退出,仍在后台运行
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
// 对按钮进行事件绑定
g_signal_connect(button1, "clicked", G_CALLBACK(print_hello), "button1");
g_signal_connect(button2, "clicked", G_CALLBACK(print_hello), "button2");
g_signal_connect(quit, "clicked", G_CALLBACK(gtk_main_quit), NULL); // 绑定退出函数
// builder 解引用,让其可以释放内存。可以解引用的原因是已经利用builder加载了布局,获取了各构件
g_object_unref(builder);
gtk_main(); // 运行该窗口程序
return 0;
}
注意窗口绑定的事件 destroy,不绑定的情况下,点击窗口右上角的 x,仅仅会导致窗口消失,但主程序不会退出:
绑定 destroy 事件后,点击窗口右上角 x ,可以发现程序会正常退出,不会在后台继续执行。