在第一课写的那个简单的窗口程序在运行的时候,单击关闭按钮,会发现窗口程序确实是关闭了,但是在终端中显示的是它还处在运行状态。




tk按下esc退出_控件

窗口关闭了,但程序在终端仍在运行



想要完全关闭这个窗口程序最简单的方法就是关闭终端窗口,再重新开启一个即可。不过这并不是最好的方法,你可以按下Ctrl+C,这样目前在终端所运行的程序将被强制退出。为什么我明明已经关闭了窗口,我们写的程序怎么还在终端运行呢?

其实原因就出在所写的那个简单窗口程序最后一个函数gkt_main();我们只是调用了主事件循环,而未对来自用户点击关闭窗口的信号做任何的处理,所以即使窗口关闭了,循环仍在执行,程序并未真正退出。当按下Ctrl+C后,系统向此程序发出了中断信号,程序才真正终止了。

为解决该问题,就需要进一步地完善一下窗口的功能。

/*完善窗口的功能window.c*/
#include 
int main(int argc, char * argv[])
{
GtkWidget * window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
//新增代码
g_signal_connect(G_OBJECT(window),“delete_event”, G_CALLBACK(gtk_main_quit), NULL); //为信号连接回调函数
//设置窗口标题
gtk_window_set_title(GTK_WINDOW(window),”完善窗口的功能”);
//设置窗口的默认宽和高
gtk_window_set_default_size(GTK_WINDOW(window), 500, 100);
//设定窗口在显示器中出现的位置
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_widget_show(window);
gtk_main();
retrun FALSE;
}

编辑Makefile文件,并在终端执行make命令开始编译,编译完后,执行命令./window即可运行此程序。当你运行这个程序时会发现,窗口的标题有了,窗口的位置也出现在了屏幕的中央,而且窗口的尺寸也发生了变化,更重要的是当点击关闭按钮后,程序正常完整的退出了。




tk按下esc退出_回调函数_02

此时点击关闭按钮,程序正常退出



下面来分析一下上面新增的几段代码:

1. GTK+3.0的信号与回调函数机制

GTK+3.0采用了一种信号与回调函数机制来处理窗口外部传来的事件、消息或者信号。简单来说就是先为窗口或者控件定义一系列信号,在编程中引用信号名称为窗口或者控件添加回调函数,当信号发生时,程序会自动调用信号连接的回调函数。

窗口的信号有很多,如“key_press_event”按键按下时发生,“motion_notify_event”鼠标移动时发生,“foucs”在获得焦点时发生等等。这里主要介绍我们目前用到的“delete_event”信号,这一信号在窗口关闭时发生。

在退出我们编写的GTK+3.0程序时就需要调用gtk_main_quit();函数,它的功能就是退出主循环,也就是结束程序运行。但是一般情况下当我们点击窗口关闭时程序就退出了,但GTK+3.0并不会主动处理退出程序。这就需要为上面介绍的“delete_event”信号连接回调函数。

为信号连接回调函数有两种方式:

第一种方式是直接用GTK+3.0为我们定义好的函数(如退出函数gtk_main_quit()),在窗口或者控件创建完成后直接引用g_signal_connect宏。

g_signal_connect(G_OBJECT(window),“delete_event”, G_CALLBACK(gtk_main_quit), NULL);

第二种方式是先定义好回调函数,在窗口或者控件创建完成后引用g_signal_connect宏。

/*声明回调函数on_delete_event*/
void on_delete_event(GtkWidget * widget, GdkEvent * event, gpointer data)
{
gtk_main_quit();
return FALSE;
}
....
/*在主函数中为窗口的“delete_event”信号加回调函数*/
......
g_signal_connect(G_OBJECT(window),”delete_event”,G_CALLBACK(on_delete_evnet), NULL);
......

可以看出来,第一种方式更简单明了,而第二种方式虽然代码增加了,但是它的好处就是可以在on_delete_event函数中加入其他代码来处理程序开始时遗留的问题,例如释放已分配的内存、保存程序的配置等,当然还可以添加一些功能,询问用户是否真正退出程序。

2. 什么是g_signal_connect()

g_signal_connect()——信号连接,该函数的主要作用就是将一个事件(发出的信号)的请求及处理该事件的方法连接起来,函数原型为:

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

g_signal_connect宏格式有4个参数,分别是:

连接对象:就是要连接信号的控件的指针(注意:必须是已经创建完的控件的指针),这里需要用G_OBJECT宏来转换一下,即G_OBJECT(window);

信号名称:就是需要连接信号的名称,为字符串形式,用双引号引起来。不同的控件拥有的信号名称是不一样的,例如本例中的窗口控件的信号:“delete_event”。

回调函数:就是当信号发生时调用的函数,这里值用到函数名称,需要用G_CALLBACK宏来转换一下,即G_CALLBACK(gtk_main_quit);

传递给回调函数的参数:它的值的类型应该是gpointer。如果不是这一类型的可以强制转换一下,如果没有参数则为NULL。注意:这里只能传递一个参数,如果有多个参数,可以先将多个参数定义为一个结构,再将该结构作为参数传递过去。

3.回调函数该怎么写

不同的控件的信号是不同的,不同的信号的回调函数的格式也是不同的。但是有一个规律,那就是多数的回调函数是没有返回类型的,名称可以自己定义,但是最好能表达一定的意思,例如上面定义的回调函数

void on_delete_event(GtkWidget * widget, GdkEvent * event, gpointer data);

第一个参数widget是调用该回调函数的控件对象指针(即窗口控件的指针)

第二个参数event是指事件的类型

第三个参数data是指用户传递给此回调函数的参数,而且固定为gpointer类型。

4.改变窗口外观的几个函数

(1)设定窗口标题:

gtk_window_set_title(GtkWindow * window, const gchar * title);

window:窗口

title:标题

(2)设定窗口的默认宽高

gtk_window_set_default_size(GtkWidget *window, int width, int height);

window:窗口

width:宽度

height:高度

(3)设定窗口的位置

gtk_window_set_position(GtkWidget * window, GekWindowPosition positon);

window:窗口

position常用有5种情况:

GTK_WIN_POS_NONE:不固定

GTK_WIN_POS_CENTER:居中

GTK_WIN_POS_MOUSE:出现鼠标的位置

GTK_WIN_POS_CENTER_ALWAYS:窗口改变尺寸仍然居中

GTK_WIN_POS_CENTER_ON_PARENT:居于父窗口的中部

此外还有窗口设置函数如:设置控件的大小的函数

void gtk_widget_set_size_request(GtkWidget * widget, int width, int height);

widget:需要操作的控件,可以是任何控件

width:宽度

height:高度

设置窗口伸缩的函数:

void gtk_window_set_resizable(GtkWindow * window, gboolean resizable);

window:窗口

resizable:TURE默认属性,可伸缩,FALSE不可伸缩

至于其他更多的窗口设置函数可以参考GTK+3.0的API手册。

这一课主要介绍了GTK+3.0的信号与回调机制和回调函数的一般写法,还有就是关于窗口的一些简单设置函数,这也是编写GTK+3.0程序的基础和关键所在。