GTK 事件處理函式


為 了連結一個事件Signal與Callback函式,一樣是使用g_signal_connect()函式,不過處理事件Signal的Callback 函式與純綷的GTK Signal的Callback函式在宣告時有些不同,以下是處理事件Signal的Callback函式宣告方式:
gboolean callback_func(
          GtkWidget *widget, GdkEvent *event, gpointer callback_data);


Callback函式多了一個GdkEvent*參數,而傳回值的部份,可以控制事件是否進行下一步傳播,傳回TRUE表示這個事件到止已獲得處理,事件不用繼續傳播,傳回FALSE表示事件繼續傳播。

事件Signal的處理函式會在GTK Signal的處理函式之前先處理,以按下按鈕為例,基本上的順序為:
按鈕按下 --> 發出 GDK_BUTTON_PRESS --> GDK 預設處理函式
                 --> 發出 button_press_event Signal --> GTK 預設處理函式
         --> 發出 clicked Signal --> GTK 預設處理函式

您可以設置事件Signal的Callback函式,攔截button_press_event,當處理完傳回TRUE時,就 不會繼續預設的GTK處理函式,也就不會發出clicked的Signal,只有在傳回FALSE時,才會發出clicked的Signal,則設置的 GTK Signal處理函式才會被執行。

以下這個例子為例:
  • event_demo.c
#include <gtk/gtk.h>

// Signal處理函式
void button_clicked_callback(GtkWidget *button, gpointer data) {
g_print("clicked處理函式\n");
}

// 事件處理函式
gboolean button_press_callback(
GtkWidget *button, GdkEvent *event, gpointer data) {
GdkEventType type = event->type;

if(type == GDK_BUTTON_PRESS) {
g_print("button_press_event處理函式(%d, %d)\n",
(gint) event->button.x, (gint) event->button.y);
}

return FALSE;
}

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

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

button = gtk_button_new_with_label("按我");
gtk_container_add(GTK_CONTAINER(window), button);

g_signal_connect(GTK_OBJECT(button), "event",
G_CALLBACK(button_press_callback), NULL);
g_signal_connect(GTK_OBJECT(button), "clicked",
G_CALLBACK(button_clicked_callback), NULL);
g_signal_connect(GTK_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show(window);
gtk_widget_show(button);

gtk_main();

return 0;
}

在上面的程式中,以button_clicked_callback()函式來處理事件,因為 g_signal_connect()中設定為"event",表示所有事件都會經過button_press_callback處理,所以函式中使用 if判斷GdkEventType,只有當滑鼠按下時,顯示滑鼠的位置,最後傳回FALSE,執行結果如下所示:

button_press_event處理函式(58, 44)
clicked處理函式
button_press_event處理函式(134, 108)
clicked處理函式
button_press_event處理函式(66, 149)
clicked處理函式
button_press_event處理函式(146, 44)
clicked處理函式

如果button_clicked_callback()函式傳回TRUE,則button_clicked_callback()函式將不執行,"clicked處理函式"文字將不會顯示。

如果您已知要處理特定的事件類型,則您可以在Callback函式上宣告特定的事件類型,並在g_signal_connect()時指定特定的事件Signal,例如上面的範例也可以修改為以下,而執行結果相同:
  • event_demo.c
#include <gtk/gtk.h>

// 自訂Callback函式
void button_clicked_callback(GtkWidget *button, gpointer data) {
g_print("clicked處理函式\n");
}

gboolean button_press_callback(
GtkWidget *button, GdkEventButton *event, gpointer data) {
g_print("button_press_event處理函式(%d, %d)\n",
(gint) event->x, (gint) event->y);

return FALSE;
}

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

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

button = gtk_button_new_with_label("按我");
gtk_container_add(GTK_CONTAINER(window), button);

g_signal_connect(GTK_OBJECT(button), "button_press_event",
G_CALLBACK(button_press_callback), NULL);
g_signal_connect(GTK_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(GTK_OBJECT(button), "clicked",
G_CALLBACK(button_clicked_callback), NULL);

gtk_widget_show(window);
gtk_widget_show(button);

gtk_main();

return 0;
}