GTK之信号和回调函数
在 2.0 版,信号系统已从 GTK 移到 GLib,因此在函数和类型的说明中有前缀 “g_” 而不是 “gtk_”。
GTK 是一个事件驱动的工具包,意味着它会等在gtk_main() 那里, 直到下一个事件发生, 才把控制权传给适当 的函数。控制权的传递是使用“信号”的办法来完成的。(注意这里的信号并不等同于Unix 系统里的信号,并且也不是用它们实现的,虽然使用的术语是一样的。)
1.常用信号
构件能够发送各种信号,信号可以由用户操作而引起,也可以由函数模拟事件产生,常见信号如下。
构件 | 信号 | 意义 | 产生信号函数 |
---|---|---|---|
windows | “destroy” | 关闭窗口时发出该信号 | |
button | “clicked” | 点击按钮,这是按下和释放操作的组合 | gtk_button_clicked(button) |
“enter” | 鼠标移到按钮之上 | gtk_button_enter(button) | |
“leave” | 鼠标离开按钮 | gtk_button_leave(button) | |
“pressed” | 按下按钮 | gtk_button_pressed(button) | |
“released” | 释放按钮 | gtk_button_released(button) | |
toggle_button | “toggled” | 开关按钮 | |
listbox | “select” | 列表框被选择 | |
“selection_changed” | 列表框选择被更改 | ||
GTKRadioMenuItem | “activate” | 菜单选项被选择 |
GTK+的两个基本机制是delete_event事件和destroy信号,当将要关闭窗口时,出现delete_event事件。当关闭窗口时,发出destroy信号。对于delete_event事件,顶层窗口应该设有相应的回呼函数。因为delete_event表示用户需要关闭应用软件。对于delete_event信号增加回呼函数包括两个步骤。
delete_event的回呼函数应该返回布尔值,用以表示是否允许关闭窗口。返回TRUR值保持窗口打开,返回FALSE值表示窗口可以关闭。如果关闭窗口,接着发出destroy信号告诉应用程序就要关闭窗口。
2.信号连接和处理函数
2.1 信号连接函数 g_signal_connect
/* 信号连接函数*/
#define g_signal_connect(instance, detailed_signal, c_handler, data)
形参:instance --void*指针,要发出信号的构件
detailed_signal -- char *指针,连接的信号的名称c_handler --信号被捕获时所要调用的函数data -- void*指针,传递给信号处理函数(回调函数)的数据
返回值:gulong类型,返回识别回调函数的标识
第三个参数即回调函数一般形式如下:
void callback_func( GtkWidget *widget,gpointer callback_data );
形参:widget -- 指向发出信号的构件的指针callback_data --g_signal_connect传入的参数
注意:上面回调函数的声明只是一般的形式, 有些构件的特殊信号会用不同的调用参数。
每个信号和每个对象可以有多个回调函数,并且它们会按设置的顺序依次运行。
2.2 g_signal_connect返回值
信号连接函数g_signal_connect返回值为glong类型,返回一个识别回调函数的标志。因为每个信号和每个对象可以有多个回调函数,并且它们会按设置的顺序依次运行。所以可以通过这个返回标志,可通过下面函数将实现的回调函数删除:
g_signal_handler_disconnect(gpointer instance, gulong handler_id)
所以,通过传递你想在上面删除处理函数的构件,以及某个signal_connect函数返回的标识,你就可以中断一个信号处理函数的连接。
也可以使用g_signal_handler_block() 和 g_signal_handler_unblock() 这类函数来暂时断开信号处理函数的连接。
void g_signal_handler_block (gpointer instance, gulong handler_id);
void g_signal_handlers_block_by_func( gpointer object,GCallback func,gpointer data );
void g_signal_handler_unblock (gpointer instance,gulong handler_id);
void g_signal_handlers_unblock_by_func( gpointer object,GCallback func,gpointer data );
2.3 信号连接函数 g_signal_connect_swapped
/*信号连接函数*/
#define g_signal_connect_swapped(instance, detailed_signal, c_handler, data)
g_signal_connect_swapped()和g_signal_connect() 相同,只是回调函数只用一个参数,一个指向GTK对象的指针。所以当使用这个函数连接信号时,回调函数应该是这样的形式:void callback_func( GtkObject *object );
拥有两个函数来设置信号连接的目的只是为了允许回调函数有不同数目的参数。GTK 库中许多函数仅接受一个单独的构件指针作为其参数,所以对于这些函数你要用 g_signal_connect_swapped(),然而对你自己定义的函数,你可能需要附加的数据提供给你的回调函数。
3 事件
除有信号机制外,还有一套 events 反映 X 事件机制。回调函数可以与这些事件连接。
• event
• button_press_event
• button_release_event
• scroll_event
•motion_notify_event
• delete_event
• destroy_event
• expose_event
• key_press_event
• key_release_event
• enter_notify_event
• leave_notify_event
• configure_event
• focus_in_event
• focus_out_event
• map_event
• unmap_event
• property_notify_event
• selection_clear_event
• selection_request_event
• selection_notify_event
• proximity_in_event
• proximity_out_event
• visibility_notify_event
• client_event
• no_expose_event
• window_state_event
为了连接一个回调函数到这些事件之一,你使用函数 g_signal_connect(),像前面介绍的一样,用上面事件名之一作为detailed_signal参数。事件的回调函数与信号的回调函数有一点点不同:
gint callback_func( GtkWidget *widget,GdkEvent *event,gpointer callback_data );
GdkEvent 是一个C联合结构,它的类型依赖于上述事件中的哪个事件发生了。为了让我们得知发生了哪个事件,每个可能的类型都有一个type成员来反映发生的事件。事件结构的其它部分将依赖于这个事件的类型。类型的可能的值有:
GDK_NOTHING
GDK_DELETE
GDK_DESTROY
GDK_EXPOSE
GDK_MOTION_NOTIFY
GDK_BUTTON_PRESS
GDK_2BUTTON_PRESS
GDK_3BUTTON_PRESS
GDK_BUTTON_RELEASE
GDK_KEY_PRESS
GDK_KEY_RELEASE
GDK_ENTER_NOTIFY
GDK_LEAVE_NOTIFY
GDK_FOCUS_CHANGE
GDK_CONFIGURE
GDK_MAP
GDK_UNMAP
GDK_PROPERTY_NOTIFY
GDK_SELECTION_CLEAR
GDK_SELECTION_REQUEST
GDK_SELECTION_NOTIFY
GDK_PROXIMITY_IN
GDK_PROXIMITY_OUT
GDK_DRAG_ENTER
GDK_DRAG_LEAVE
GDK_DRAG_MOTION
GDK_DRAG_STATUS
GDK_DROP_START
GDK_DROP_FINISHED
GDK_CLIENT_EVENT
GDK_VISIBILITY_NOTIFY
GDK_NO_EXPOSE
GDK_SCROLL
GDK_WINDOW_STATE
GDK_SETTING
所以, 连接一个回调函数到这些事件之一,我们会这样用:
g_signal_connect (G_OBJECT (button), "button_press_event",G_CALLBACK (button_press_callback), NULL);
这里假定button是一个按钮构件。现在,当鼠标位于按钮上并按一下鼠标时,函数 button_press_callback() 会被调用。这个函数应该声明为:
static gint button_press_callback( GtkWidget *widget,GdkEventButton *event,gpointer data );
注意,我们可以把第二个参数类型声明为 GdkEventButton,因为我们知道哪个类型的事件会发生。
这个函数的返回值指示这个事件是否应该由 GTK 事件处理机制做进一步的传播。 返回 TRUE 指示这个事件已经处理了,且不应该做进一步传播。 返回 FALSE 继续正常的事件处理。
3.1 按键连接信号关闭窗口示例
#include <gtk/gtk.h>
void hello(GtkWidget *widget,gpointer data)
{g_print("按键信号触发成功\n");
}
/*事件处理回调函数*/
gint delete_event(GtkWidget *widget,GdkEvent *evnet,gpointer data)
{g_print("事件处理回调函数调用成功\n");return TRUE;//TRUE表示继续,FALSE表示关闭
}void destroy(GtkWidget *widget,gpointer data)
{g_print("销毁窗口\n");gtk_main_quit();//退出程序
}
int main(int argc,char *argv[])
{GtkWidget *window;GtkWidget *button;/*gtk初始化*/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),"delete_event",G_CALLBACK(delete_event),NULL);g_signal_connect(GTK_OBJECT(window),"destroy", G_CALLBACK(destroy), NULL);/*设置容器对象属性:设置窗口边框宽度为100*/gtk_container_set_border_width(GTK_CONTAINER(window),100);/*创建新的按钮,设置按钮信息为hello*/button=gtk_button_new_with_label("hello");g_signal_connect(GTK_OBJECT(button),"clicked",G_CALLBACK(hello),NULL);g_signal_connect_swapped(GTK_OBJECT(button),"clicked",G_CALLBACK(gtk_widget_destroy),GTK_OBJECT(window));/*将按钮放入窗口中*/gtk_container_add(GTK_CONTAINER(window),button);/*显示控件*/gtk_widget_show_all(window);/*等待事件*/gtk_main();return 0;
}
- 程序运行效果:
当我们用鼠标按下hello按钮,按键发出clicked信号,触发hello回调函数,接着调用下一个信号处理函数gtk_widget_destroy(),将窗口构件参数传递给该函数,销毁窗口,从而窗口发出destroy信号,然后调用我们实现号的回调函数destroy(),实现程序退出。