一般在学习一项新技术的时候,我最希望看到的不是那一本本厚厚的理论书籍或者编程技巧,即便没有什么技巧,我希望的是能够看到我的成果,即便只是一个小小的什么都不能做的窗口,也能满足一下我的好奇心。所以,现在我们先来写一个小程序,看看GTK+程序是怎样编写的。
由于上次我们已经配置好了编译环境,所以,直接打开你所喜爱的IDE或者记事本,敲下如下的代码。这里我是在Windows平台下使用VS2005编译的,有些术语在其他平台下可能会有出入,但整体是类似的。
#include <gtk/gtk.h>
int main( int argc, char** argv)
{
GtkWidget* window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_show(window);
gtk_main();
return 0;
}
int main( int argc, char** argv)
{
GtkWidget* window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_show(window);
gtk_main();
return 0;
}
然后进行编译连接,运行之后,如果没有错误的话,将会出现一个小窗口。嗯,这就是我们用GTK+编写的界面了。
下面来看看这段程序。首先第一行,是引用的头文件,一般来说只要引用这个gtk.h就可以了。按照前面所述的方法引入include文件的话,就是在gtk目录下的gtk.h了。
然后是main函数定义,和C语言一模一样。
main函数中,第一句声明一个GtkWidget的指针。前面说过,GTK+是按照面向对象思想设计的,不妨把这个GtkWidget当作类吧,虽然C编译器并不这么认为。然后是gtk_init调用,这是初始化GTK+环境。写过OpenGL程序的朋友应该比较清楚,OpenGL里面也有一个类似的init函数。然后对前面声明的指针赋值。看看这个函数名字:gtk_window_new,很清楚是新建一个window指针。按照面向对象的写法就是:
GtkWidget* window = new GtkWindow(GTK_WINDOW_TOPLEVEL);
怎么样?清楚很多了吧?其实这里就已经暗示出了,GtkWindow继承了GtkWidget类。这些在后面就会看出,GtkWidget其实是所有控件的父类。传递的参数是GTK_WINDOW_TOPLEVEL,指明是一个顶层的窗口。然后使用gtk_widget_show设置显示,同样把它想象成面向对象的语法就是:
window -> show();
最后一句gtk_main,将我们的程序带入GTK+的事件监听循环。
这样,我们的程序就介绍完了。但是也许就会发现一个问题:怎么后面还有一个黑黑的控制台窗口啊?这是因为默认运行方式是Debug,换成Release看看?那个丑丑的窗口没有了吧?而且生成的二进制文件也比那个小了很多。
嗯,当我们按下关闭按钮时,窗口是不见了,但是程序并没有退出啊?对哦,因为我们没有添加事件监听啊,所以当关闭窗口的时候,GTK+也不知道该怎么做。所以,我们继续修改代码如下:
#include <gtk/gtk.h>
int main( int argc, char** argv)
{
GtkWidget* window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(GTK_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show(window);
gtk_main();
return 0;
}
int main( int argc, char** argv)
{
GtkWidget* window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(GTK_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show(window);
gtk_main();
return 0;
}
注意,我们添加了一个事件监听函数:
g_signal_connect(GTK_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
它的意思是,对于对象window,当"destroy"时间发生时,调用gtk_main_quit函数,传给这个函数的参数是NULL。如果我们使用Java语言的事件监听写法,就是这样子的:
window.addDestroyListener(new CallbackFunc(){
gtk_main_quit();
});
不过这段代码应该也是比较容易读懂的。其实这就是GTK+中添加事件监听的写法,每个控件的每个事件监听都是这样编写代码,很统一。修改完成后运行一下,点下关闭按钮:哈,程序退出了!
那么,我们再来修改一下程序吧:
#include <gtk/gtk.h>
int main( int argc, char** argv)
{
GtkWidget* window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Hello world!");
g_signal_connect(GTK_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show(window);
gtk_main();
return 0;
}
int main( int argc, char** argv)
{
GtkWidget* window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Hello world!");
g_signal_connect(GTK_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show(window);
gtk_main();
return 0;
}
运行一下看看?窗口标题已经换成了经典的Hello world!了。
读一下代码,我们添加了这么一句:
gtk_window_set_title(GTK_WINDOW(window), "Hello world!");
如果你能想到它的面向对象语法,就达到目的了啊:
((GtkWindow*)window) -> setTitle("Hello world!");
注意,因为C编译器是不理解面向对象的多态机制的,所以,GTK+使用了很多宏来进行类型转换,比如这里的GTK_WINDOW。还记得我们声明的是GtkWidget指针,需要转换成GtkWindow指针才能使用set_title函数的。同时,这个宏也有类型检测的功能,如果不能转换,是会抛出异常的。
好了,现在经过一步步的代码添加,我们已经了解了GTK+程序的编写过程,并且能够创建一个窗体,添加事件监听和修改控件属性——GUI编程的主体不就是这些吗?剩下的就是一些细节,和对于庞大的控件库的学习了。
本文转自 FinderCheng 51CTO博客,原文链接:
http://blog.51cto.com/devbean/108758