GTK+編程入門(3)—響應GKT+的信號

GTK+編程入門(2)—響應GTK+的信號(2015-7-24)

分類:GTK+

  在這之前,先來看一個對上一個簡單程序的改進程序gtk.c。

#include <gtk/gtk.h>

static void hello(GtkWidget *widget, gpointer data){    
    /* 輸出信息 */                      
    g_printf("Hello World!\n");
}

static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data){
    g_printf("delete event occurred.\n");

    /* 如果返回FALSE,GTK+會發出一個“destroy”信號 */
    return TRUE;
}

static void destroy(GtkWidget *widget, gpointer data){
    /* 輸出構件的名字 */
    g_printf("%s :exit!\n", gtk_widget_get_name(widget));

    /* 退出主循環 */
    gtk_main_quit();
}

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

    gtk_init(&argc, &argv);

    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

    /* 註冊回調函數 */
    g_signal_connect(window, "delete-event", G_CALLBACK(delete_event), NULL);
    g_signal_connect(window, "destroy", G_CALLBACK(destroy), NULL);

    /* 設置窗口邊距 */
    gtk_container_set_border_width(GTK_CONTAINER(window), 10);
    /* 設置構件名稱 */
    gtk_widget_set_name(GTK_WIDGET(window), "MainWindow");
    /* 創建一個按鈕 */
    button = gtk_button_new_with_label("Hello World!");

    /* 註冊單擊按鈕事件的回調函數 */
    g_signal_connect(button, "clicked", G_CALLBACK(hello), NULL);
    g_signal_connect_swapped(button, "clicked", G_CALLBACK(gtk_widget_destroy), window);
    /* 把構件添加到窗口內 */
    gtk_container_add(GTK_CONTAINER(window), button);

    /* 顯示按鈕 */
    gtk_widget_show(button);
    /* 顯示窗口 */
    gtk_widget_show(window);

    /* 進入GTK+主循環 */
    gtk_main();

    return 0;
}

  它的編譯和運行:

biantiao@lazybone1994-ThinkPad-E430:~/sh/GTK+$ gcc -o ex_gtk ex_gtk.c `pkg-config --cflags --libs gtk+-3.0`
biantiao@lazybone1994-ThinkPad-E430:~/sh/GTK+$ ./ex_gtk
Hello World!
MainWindow :exit!
biantiao@lazybone1994-ThinkPad-E430:~/sh/GTK+$

  運行效果圖:
窗口
全屏幕顯示

GTK+中的事件和信號

  在GTK+中,一個事件(event)就是一個從 X Window傳來的信息。事件是通過信號(signal)來傳遞的。當一個事件(比如單擊鼠標)發生時,事件所作用的控件(比如被單擊的按鈕)就會發出一個信號(比如“clicked”)來通知應用程序。如果應用程序已經將該信號與另一個回調函數連接起來,GTK+就會自動調用該回調函數執行相關的操作,從而完成一次由事件所引發的行爲。與信號相連接的回調函數稱爲信號處理函數。當爲事件所發出的信號連接了信號處理函數時,稱響應了某個事件。
  GTK+中有通用於所有構件的公共信號(比如:“destroy”),也有專屬於某類構件的專有信號(比如:toggle buttong具有的toggled信號)。
  如上所述,在GTK+中要讓應用程序響應某個事件,必須事先給該事件發出的信號連接一個信號處理函數。這需要用到g_signal_connect函數。其原型如下:

gulong g_signal_connect(gpointer *object, const gchar *name, GCallback func, gpointer func_data);

  函數各參數和返回值含義如下:
1. object:發出信號的構件
2. name:信號名稱
3. func:事件發生時將調用的信號處理函數
4. func_data:事件發生時傳遞給信號處理函數的用戶數據
5. 返回值:成功返回信號處理函數的ID(非0值);失敗時返回0

  把例子當中的連接信號函數的代碼揪出來看看。

/* 註冊回調函數 */
    g_signal_connect(window, "delete-event", G_CALLBACK(delete_event), NULL);
    g_signal_connect(window, "destroy", G_CALLBACK(destroy), NULL);

  在這裏發出信號的構件是window,信號的名稱分別爲”delete-event”和”destroy”,傳遞給信號處理函數的用戶數據爲NULL。
  G_CALLBACK()是什麼東東?
  信號處理函數以GCallback類型聲明。實際上,在GTK+中,不同的信號所對應的信號處理函數的類型可能是不同的。作爲一種設計策略,GTK+中使用GCallback類型表示通用的回調函數(信號處理函數)類型。其定義爲void (*GCallback)(void)同時,GTK+定義了一個通用回調函數類型轉換宏G_CALLBACK(),在實際調用g_signal_connect函數時,應將一個具有以下形式的具體的回調函數經G_CALLBACK()宏進行強制類型轉換後傳遞給func參數。

void callback_func(GtkWidget *widget,
                    ... /*其它參數*/
                    gpointer callback_data);

  雖然不同信號所對應的信號處理函數的類型可能不同,但是其第一個參數和最後一個參數是固定的。第一個參數widget爲發出信號的構件;最後一個參數callback_data是用戶數據,當信號處理函數被調用時,它將得到g_signal_connect函數連接信號時提供的func_data參數。
  再把定義的信號處理函數揪出來看看。

static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data){
    g_printf("delete event occurred.\n");

    /* 如果返回FALSE,GTK+會發出一個“destroy”信號 */
    return TRUE;
}

  在GTK+中,事件具有一個“傳播”的過程。一個事件可能在一個構件上先後引發不同的信號。同時,對每個事件,信號先由它直接作用的構件引發,然後是它的直接父構件,然後是父構件的父構件,依次向上遞歸,這個過程稱爲事件“冒泡”。因此,一個事件可能在多個構件上面分別引用多個信號。
  另外,GTK+允許爲一個構件的一個信號連接多個信號處理函數,當相應信號被傳播時,這些信號處理函數將按連接的順序依次被調用。
  GTK+事件的信號處理函數必須返回一個gint型整數值。最後一個運行的信號處理函數決定了信號引發的返回值。如果返回的是TRUE,GTK+主循環會停止當前事件的傳播過程,否則將繼續事件的傳播。
  例如,對於實例程序,連接delete-event信號的信號處理函數delete_event,它最後返回TRUE終止了事件的傳播。如果返回FALSE,GTK+會發出一個“destroy”信號,該信號會使主窗口關閉。

g_signal_connect_swapped函數

  和g_signal_connect函數一樣,它也可用於連接信號和信號處理函數,其原型:

gulong g_signal_connect_swapped(gpointer *object,
                                const gchar *name,
                                GCallback func,
                                gpointer *callback_data;
                                );

  該函數和g_signal_connect函數的區別在於回調函數,g_signal_connect_swapped函數要求連接的信號處理函數具有以下形式:

void callback_func(gpointer callback_data,
                    ... /* 其它參數 */
                    GtkWidget *widget);

與g_signal_connect函數連接信號處理函數相比較,兩者的第一個參數和最後一個參數的位置幹好相反,在GTK+程序中一般不用g_signal_connect_swapped函數連接信號和信號處理函數,該函數的僅用於連接“只帶一個構件或對象作爲參數”的GTK+內置應用接口函數作爲信號處理函數的時候。例如,實例中的

g_signal_connect_swapped(button, "clicked", G_CALLBACK(gtk_widget_destroy), window);

在這個調用中,第四個參數,即傳遞給信號處理函數的“用戶數據”是window。而信號處理函數是gtk_widget_destroy,它是GTK+內置的應用接口函數,作用是銷燬一個構件。其原型爲:
void gtk_widget_destroy(GtkWidget *widget);
作用是銷燬一個構件。

調用g_signal_emit_by_name手動產生一個信號

  g_signal_emit_by_name函數的作用是手動產生一個信號以區別GTK+自動產生的信號。該函數的原型如下:

void g_signal_emit_by_name(gpointer instance,
                            const gchar *detailed_signal, ...);

  這個函數的instance參數爲信號所作用的目標,一般是一個構件。detailed_signal是表示信號的具體字符串,如“destroy”。省略號部分表示兩個可選的參數,前一個參數爲信號的“用戶數據”,後一個參數爲存放信號處理函數的返回值的地址。
  在GTK+中,使用共用體GdkEvent類型來表示一個事件,該類型定義如下:

typedef union _GdkEvent
{
    GdkEventType            type;           /* 事件類型 */
    GdkEventAny             any;            /* 通用事件頭部 */
    GdkEventExpose          expose;         /* 以下爲具體的事件類型 */
    GdkEventVisibility      visibility;
    GdkEventMotion          motion;
    GdkEventButton          button;
    GdkEventScroll          scroll;
    GdkEventKey             key;
    GdkEventCrossing        crossing;
    GdkEventFocus           focus_change;
    GdkEventConfigure       configure;
    GdkEventProperty        property;
    GdkEventSelection       selection;
    GdkEventOwnerChange     owner_change;
    GdkEventProximity       proximity;
    GdkEventDND             dnd;
    GdkEventWindowState     window_state;
    GdkEventSetting         setting;
    GdkEventGrabBroken      grab_broken;
};

  其中type成員是一個枚舉值,用於指明事件類型。事件類型GdkEventType列出了GTK+中所有的事件類型,其定義如下:

typedef enum{
    GDK_NOTHING                 = -1,
    GDK_DELETE                  = 0,
    GDK_DESTROY                 = 1,
    GDK_EXPOSE                  = 2,
    GDK_MOTION_NOTIFY           = 3,
    GDK_BUTTON_PRESS            = 4,
    GDK_2BUTTON_PRESS           = 5,
    GDK_3BUTTON_PRESS           = 6,
    GDK_BUTTON_RELEASE          = 7,
    GDK_KEY_PRESS               = 8,
    GDK_KEY_RELEASE             = 9,
    GDK_ENTER_NOTIFY            = 10,
    GDK_LEAVE_NOTIFY            = 11,
    GDK_FOCUS_CHANGE            = 12,
    GDK_CONFIGURE               = 13,
    GDK_MAP                     = 14,
    GDK_UNMAP                   = 15,
    GDK_PROPERTY_NOTIFY         = 16,
    GDK_SELECTION_CLEAR         = 17,
    GDK_SELECTION_REQUEST       = 18,
    GDK_SELECTION_NOTIFY        = 19,
    GDK_PROXIMITY_IN            = 20,
    GDK_PROXIMITY_OUT           = 21,
    GDK_DRAG_ENTER              = 22,
    GDK_DRAG_LEAVE              = 23,
    GDK_DRAG_MOTION             = 24,
    GDK_DRAG_STATUS             = 25,
    GDK_DROP_START              = 26,
    GDK_DROP_FINISHED           = 27,
    GDK_CLIENT_EVENT            = 28,
    GDK_VISIBILITY_NOTIFY       = 29,
    GDK_SCROLL                  = 31,
    GDK_WINDOW_STATE            = 32,
    GDK_SETTING                 = 33,
    GDK_OWNER_CHANGE            = 34,
    GDK_GRAB_BROKEN             = 35,
    GDK_DAMAGE                  = 36,
    GDK_EVENT_LAST              /* 用作哨兵 */
}GdkEventType;

  GdkEvent的any成員GdkEventAny類型定義如下:

struct GdkEventAny{
    GdkEventType    type;   //事件類型
    GdkWindow * window;     //事件的目標窗口
    gint8   send_event;     //手動引發(用XSendEvent)或由GDK引發
}

  GdkEvent類型的成員中,除了type和any成員外,其他成員都表示某一個具體的事件類型。GTK+中每一個具體的類型均以GdkEventAny結構體的三個成員開頭,因此,GdkEventAny是一個通用的事件的頭部。

GTK+中常用的具體事件類型

GdkEventButton類型

  GTK+類型對應與鼠標操作相關的事件,如鼠標按鍵(引發“clicked”,“button_press_event”信號),鼠標移動(引發“motion_notify_event”信號)其類型定義如下:

typedef struct{
    GdkEventType type;      //通用事件的三個頭部
    GdkWindow *window;
    gint8 send_event;
    guint32 time;           //事件發生事件(毫秒計)
    gdouble x;              //相對事件窗口的座標,可能爲負
    gdouble y;
    gdouble *axes;          //設備座標,對於鼠標爲NULL
    guint state;            //修改鍵屏蔽值,指示哪個組合鍵或鼠標按鍵是按下的
    guint button;           //被按下或釋放的鼠標鍵:從1到5編號
    GdkDevice *device;      //硬件設備(如圖形輸入板或鼠標)
    gdouble x_root;         //相對於根窗口的絕對座標
    gdouble y_root;
}GdkEventButton;

GdkEventKey類型

  GdkEventKey類型對應與鍵盤操作相關的事件,如鍵盤按鍵按下(引發“key-press-event”信號)和鍵盤按鍵釋放(引發“key-release-event”信號),其類型定義如下:

typedef struct {
    GdkEventType type;
    GdkWindow *window;
    gint8   send_event;
    gint32  time;
    guint state;                /* 修改鍵屏蔽值 */
    guint keyvalue;             /* 鍵值 */
    gint length;                /* string成員的長度 */
    gchar *string;              /* 按鍵的字符串表示(已棄用) */
    guint16 hardware_keycode;   /* 按鍵的原始編碼 */
    guint8 group;               /* 鍵盤組 */
    guint is_modifier : 1;
}GdkEventKey;

  GTK+中另外兩個常用的事件類型和構件的顯示有關,它們是GdkEventConfigure和GdkEventExpose。還有一個較常見的爲焦點變更事件相關的GdkEventFocus事件。

GdkEventConfigure事件

  GdkEventConfigure事件在一個窗口的尺寸或位置改變時發生(引發“configure_event”信號),其類型定義如下:

typedef struct{
    GdkEventType type;
    GdkWindow *window;
    gint8 send_event;
    gint x, y;          /* 相對於父窗口的新座標 */
    gint width;         /* 新的尺寸 */
    gint height;
}GdkEventConfigure;

GdkEventExpose事件

  GdkEventExpose事件在一個窗口變爲可見並需要重繪時發生(引發“expose_event“信號),它的類型定義如下:

typedef struct{
    GdkEventType type;
    GdkWindow *window;
    gint8 send_event;
    GdkRectangle area;      /* 需要重繪的區域外圍矩形 */
    GdkRegion *region;      /* 需要重繪的區域,裁剪區 */
    gint    count;          /* 後續的GDK_EXPOSE事件的個數 */
}GdkEventExpose;

GdkEventFocus事件

  GdkEventFocus事件與焦點變更(引發“focus_in_event”和“focus_out_event”信號),它的類型定義如下:

typedef struct{
    GdkEventType type;
    GdkWindow *window;
    gint8 send_event;
    gint16 in;          /* 獲得焦點時爲TRUE,失去焦點時爲FALSE */
}GdkEventFocus;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章