GTK中綁定一個回調函數到相應構件中基本上都是採用GObject中所提供的g_signal_connect()等函數,而回調函數的定義形式又是void function(GtkWidget *widget, gpointer data);
這兩者的形式很明顯就是隻允許傳一個參數。但一個參數就只能包含一個內容嗎?顯然C語言中的結構體此處就是一個必不可少的角兒了,所以呢,我們應該恰當的去定義一個結構體,包含我們需要傳遞的變量,然後進行傳參。
下面是一個代碼示例,其中創建的結構體包含了兩個變量:
struct EntryStruct
{
GtkEntry *usernameEntry;
GtkEntry *passwordEntry;
};
代碼如下:
#include <gtk/gtk.h> #include <stdlib.h> /*因爲要傳參給回調函數,故此處設直一個結構體以將usernameEntry和idEntry包裝起來*/ struct EntryStruct { GtkEntry *usernameEntry; GtkEntry *passwordEntry; }; void approveButton_clicked(GtkWidget* button, gpointer data); void backToMainButton_clicked(GtkWidget* button, gpointer data); int main(int argc, char *argv[]) { gtk_init(&argc, &argv); struct EntryStruct entries; GtkLabel *usernameLabel, *passwordLabel; GtkButton *approvetButton, *cancelButton; GtkTable *tableView; GtkWindow *window; window = (GtkWindow*) gtk_window_new(GTK_WINDOW_TOPLEVEL); /*初始化label*/ usernameLabel = (GtkLabel*) gtk_label_new(NULL); gtk_label_set_markup(usernameLabel, "<b>賬號</b>"); passwordLabel = (GtkLabel*) gtk_label_new(NULL); gtk_label_set_markup(passwordLabel, "<b>密碼</b>"); /*初始化entry*/ entries.usernameEntry = (GtkEntry*) gtk_entry_new(); entries.passwordEntry = (GtkEntry*) gtk_entry_new(); /*初始化button,並設置相應事件*/ approvetButton = (GtkButton*) gtk_button_new_with_label("確認"); g_signal_connect(GTK_OBJECT(approvetButton), "clicked", GTK_SIGNAL_FUNC(approveButton_clicked), (gpointer) & entries); cancelButton = (GtkButton*) gtk_button_new_with_label("取消"); g_signal_connect(GTK_OBJECT(cancelButton), "clicked", GTK_SIGNAL_FUNC(backToMainButton_clicked), NULL); /*創建新佈局*/ tableView = (GtkTable*) gtk_table_new(4, 4, FALSE); gtk_table_attach(tableView, GTK_WIDGET(usernameLabel), 2, 3, 1, 2, GTK_EXPAND , GTK_FILL, 30, 15); gtk_table_attach(tableView, GTK_WIDGET(entries.usernameEntry), 3, 4, 1, 2, GTK_EXPAND , GTK_FILL, 30, 15); gtk_table_attach(tableView, GTK_WIDGET(passwordLabel), 2, 3, 2, 3, GTK_EXPAND , GTK_FILL, 30, 15); gtk_table_attach(tableView, GTK_WIDGET(entries.passwordEntry), 3, 4, 2, 3, GTK_EXPAND , GTK_FILL, 30, 15); gtk_table_attach(tableView, GTK_WIDGET(approvetButton), 2, 3, 3, 4, GTK_FILL | GTK_EXPAND , GTK_FILL, 30, 15); gtk_table_attach(tableView, GTK_WIDGET(cancelButton), 3, 4, 3, 4, GTK_FILL | GTK_EXPAND , GTK_FILL, 30, 15); gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(tableView)); gtk_widget_show_all(GTK_WIDGET(window)); gtk_window_set_title(GTK_WINDOW(window), "用戶登錄"); gtk_window_set_default_size(GTK_WINDOW(window), 400, 300); gtk_main(); return EXIT_SUCCESS; } /*確認事件的回調函數*/ void approveButton_clicked(GtkWidget* button, gpointer data) { struct EntryStruct *entry = (struct EntryStruct*) data; GtkEntry *usernameEntry = (GtkEntry*) entry->usernameEntry; GtkEntry *passwordEntry = (GtkEntry*) entry->passwordEntry; char username[20]; char password[20]; strcpy(username, gtk_entry_get_text((GtkEntry*) usernameEntry)); strcpy(password, gtk_entry_get_text((GtkEntry*) passwordEntry)); //下面就是你自己的操作了 } /*取消事件的回調函數*/ void backToMainButton_clicked(GtkWidget* button, gpointer data) { }
當然,如果這篇博客就是這麼一段代碼也就
這段代碼中有一個缺陷,那就是參數雖然傳遞給了回調函數,但回調函數卻有可能無法獲取那兩個Entry實例 。
先說下缺陷在哪裏吧,就是在定義EntryStruct時(代碼的第17行),我並沒有添加一個static,結果就...當然在這個程序中還是沒有錯誤的。
static struct EntryStruct entries;
這個問題得說到C語言內存分配的三種形式:
1.在靜態存儲區域分配。這個在程序編譯的時候就已經分配好了,在整個程序運行其間都存在,如全局變量,static變量。
2.在棧上中分配。在執行函數時,函數內局部變量的存儲單元都可以在棧上創建。函數執行結束時這些存儲單元自動被釋放(即棧的pop)。
3.從堆上分配,亦即動態內存分配。程序在運行時由程序員手動去分配,通常採用malloc和free(C++中還多了一個new和delete)。
程序的問題就出在這裏,定義了一個局部的非靜態變量,C語言自動的將其壓入棧中,而函數運行完之後,結構體就會被釋放,雖然傳遞了一個指向結構體的指針過去,但這塊內存上的內容已經不是原來那樣的了。而加了static之後,就很理解了。(當然也可以將其定義爲全局變量,不過這個可是很容易污染程序的哦。)
這個程序的缺陷就在於此,雖然main是最後一個結束的函數(這也意味着結構體在程序運行期間是一直存在的)。但若出現在其他函數中,問題就顯而易見了,感覺這個問題還是讓人挺容易忽視的。