使用 Signal 關閉視窗


第一個 GTK 程式 中,當您按下視窗右上X鈕時,在GTK視窗的預設處理中,只 會隱藏視窗,而不會直接關閉程式,GTK有一套Signal與Callback函式的處理機制,在某個動作發生時,GTK會發出特定Signal,若您想 要進行某些處理,則需定義Callback函式,並透過g_signal_connect()等函式,將Signal與Callback函式加以連結。

以按下視窗右上X鈕為例,按下X鈕後,GTK預設會發出"destroy"的Signal,您可以使用g_signal_connect()將之連結至gtk_main_quit()函式,這個函式會結束gtk_main()的迴圈處理因而結束GTK程式:
#include <gtk/gtk.h>

int main(int argc, char *argv[]) {
GtkWidget *window;

gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "哈囉!GTK+!");

g_signal_connect(GTK_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show(window);
gtk_main();

return 0;
}

從g_、G_開頭的函式名稱可以知道,它們是GLib所提供的函式與巨集(在GTK+ 2.0中,Signal處理已由GTK移至GLib),g_signal_connect()第一個參數,必須是GtkObject或其衍生的類別實例, 代表Signal發出的來源物件,第二個參數是感興趣的信號,第三個參數是Callback函式,G_CALLBACK巨集強制會轉換函式型態為無參數無傳回值的GCallback函式型態:
#define G_CALLBACK(f) ((GCallback) (f))

在這邊使用GTK的gtk_main_quit()函式,第四個參數是可以傳遞給Callback函式的相關資料,在這邊不需 要,設定為NULL即可。

g_signal_connect()實際上是巨集(定義在/usr/include/glib-2.0/gobject/gsignal.h),方便使用g_signal_connect_data()函式:
#define g_signal_connect(instance, detailed_signal, c_handler, data) \
    g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, (GConnectFlags) 0)

g_signal_connect_data()會傳回gulong型態的handler id,函式的定義如下:
gulong g_signal_connect_data(gpointer instance,
                             const gchar *detailed_signal,
                             GCallback c_handler,
                             gpointer data,
                             GClosureNotify destroy_data,
                             GConnectFlags connect_flags);

如果您打算將Signal與Callback斷開連結,可以根據傳回的handler id來使用g_signal_handler_disconnect()函式,也可以根據handler id來使用g_signal_handler_is_connected()函式,測試Signal的連接狀態:
void g_signal_handler_disconnect(gpointer object, gulong id);
gboolean g_signal_handler_is_connected(gpointer instance, gulong handler_id);

例如一個連接Signal與斷開Signal的程式片段如下:
....
gulong handler_id = g_signal_connect(GTK_OBJECT(window), "destroy",
                                     G_CALLBACK(gtk_main_quit), NULL);
....
if (g_signal_handler_is_connected(window, id)) {
    g_signal_handler_disconnect(GTK_OBJECTS(window), handler_id);
}

若只是想暫停(block)某Signal處理,則可以使用g_signal_handler_block(),想恢復被暫停的Signal處理,則可以使用g_signal_handler_unblock():
void g_signal_handler_block(gpointer object, gulong id);
void g_signal_handler_unblock(gpointer object, gulong id);

一個被g_signal_handler_block()函式呼叫n次的Signal處理,也必須被g_signal_handler_unblock()相對應的次數,才可以恢復原本來未暫停的狀態。

若在未知handler id的情況下,想要中斷、暫停或恢復信號連結,則可以嘗試使用g_signal_handlers_disconnect_by_func()、g_signal_handlers_block_by_func()、g_signal_handlers_unblock_by_func(),這三者其實都是巨集:
#define g_signal_handlers_disconnect_by_func(instance, func, data) \
    g_signal_handlers_disconnect_matched ((instance), \
    (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA), \
    0, 0, NULL, (func), (data))

#define g_signal_handlers_block_by_func(instance, func, data) \
    g_signal_handlers_block_matched ((instance), \
    (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA), \
                                          0, 0, NULL, (func), (data))

#define g_signal_handlers_unblock_by_func(instance, func, data) \
    g_signal_handlers_unblock_matched ((instance), \
    (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA), \
    0, 0, NULL, (func), (data))

g_signal_handlers_disconnect_matched()、g_signal_handlers_block_matched ()、g_signal_handlers_unblock_matched()會傳回guint的數值,表示符合的handler數目:
guint g_signal_handlers_disconnect_matched(gpointer instance,
                                           GSignalMatchType mask,
                                           guint signal_id,
                                           GQuark detail,
                                           GClosure *closure,
                                           gpointer func,
                                           gpointer data);

guint g_signal_handlers_block_matched(gpointer instance,
                                      GSignalMatchType mask,
                                      guint signal_id,
                                      GQuark detail,
                                      GClosure *closure,
                                      gpointer func,
                                      gpointer data);

guint g_signal_handlers_unblock_matched(gpointer instance,
                                        GSignalMatchType mask,
                                        guint signal_id,
                                        GQuark detail,
                                        GClosure *closure,
                                        gpointer func,
                                        gpointer data);

更多有關Signal函式的說明,可以參考GObject參考文件的 Signals