QT和GTK+.docx
- 文档编号:11639991
- 上传时间:2023-03-29
- 格式:DOCX
- 页数:34
- 大小:101.73KB
QT和GTK+.docx
《QT和GTK+.docx》由会员分享,可在线阅读,更多相关《QT和GTK+.docx(34页珍藏版)》请在冰豆网上搜索。
QT和GTK+
GTK+图形界面编程
本章将介绍Linux下的图形界面编程,重点介绍基于C语言的具有面向对象特征的GTK+图形界面编程。
主要介绍
GTK+图形界面应用程序的框架、基本原理、常用控件的使用。
本章重点:
GTK+程序的基本结构。
事件和消息处理。
常用控件的使用。
本章难点:
理解GTK+应用程序的基本原理。
熟悉常用控件的基本用法。
12.1Linux下的图形界面编程
12.1.1 Qt和GTK+
虽然Linux下的大多数开发是基于字符界面的,但在Linux环境下也可以开发出美观大方的图形界面。
经过多年的发展,目前已经存在多种用于在Linux下开发图形界面程序的开发包,其中较为常用的是Qt和GTK+。
Qt是一个跨平台的图形用户界面开发库,它不仅支持Linux操作系统,还支持所有类型的UNIX以及Windows操作系统。
Qt良好的封装机制使它模块化程度非常高,可重用性很强,Qt提供了丰富的API供开发人员使用。
使用Qt开发的图形用户界面程序具有良好的稳定性和健壮性。
桌面环境KDE(KDesktopEnVironment即K桌面环境)就是使用Qt作为其底层库开发出来的。
由于Qt使用C++面向对象编程语言作为其开发语言,而许多在Linux下从事开发的程序员更喜欢或更习惯于用C语言。
GTK+使用C语言作为开发语言。
它基于LGPL授权,因此GTK+是开放源代码而且完全免费的。
GTK+简单易用,执行效率高。
基于这些原因,GTK+拥有为数众多的拥护者。
Linux的桌面环境GNOME就是建立在GTK+基础上。
GTK+简介
图12-1GTK+在几种相关的
开发库中的位置。
图12-1中每层除了与其上下相邻的两层有联系外,似乎与其他层没有关系。
实际上,任何上层都可以调用位于它下面的各层提供的函数。
例如,GTK+不仅可以调用GDK函数,也可以调用glib和C库函数。
下面按层作简单的介绍,具体说明如表12-1所示。
表12-1 各层的具体含义
层
具体描述
C
有两类C库函数可供调用,一类是标准C的库函数,如printf、scanf;另一类是Linux的系统调用,如open、read、write、fork
glib
glib是GDK、GTK+、GNOME应用程序常用的库。
它包含内存分配、字符串操作、日期和时间、定时器等库函数,也包括链表、队列、树等数据结构相关的工具函数
X
它是控制图形显示的底层函数库,包括所有的窗口显示函数、响应鼠标和键盘操作的函数
GDK
GDK(GIMP绘图包)是为了简化程序员使用X函数库而开发的。
X库是其低层函数库,GDK对其进行了包装,从而使程序员的开发效率大为提高
GTK+
GTK+就是GIMP工具包,它把GDK提供的函数组织成对象,使用C语言模拟出面向对象的特征,这使得用它开发出来的图形界面程序更为简单和高效。
GTK+的一个重要组成部分是widget(控件,也称为小部件),按钮、文本编辑框、标签等都是widget
GNOME
GNOME库是对GTK+的扩展,GNOME桌面环境用来控制整个桌面。
GNOME使用GNOME对象和函数与桌面小部件交互,基本小部件由GTK+处理。
GNOME为了方便程序员还增加了一些专门的小部件
Application
Application即应用程序,它完成窗口的初始化,创建并显示窗口,进入消息循环,等待用户使用鼠标或键盘进行操作
简单地说,GTK+就是用C语言编写的用于开发图形界面程序的函数库。
GTK+来源于GIMP(GNUImageMinipulationProgram即GNU图像处理程序)。
GTK+在GDK(GIMPDrawingKit即GIMP绘图包)基础上创建,对它进行封装。
GTK+简单易用,它设计良好,灵活而富有扩展性。
它是自由软件,这意味着它不仅开放源代码,而且还可以免费使用。
由于它使用C语言作为其开发语言,而C语言是跨平台的,因此GTK+几乎可以在任何操作系统上使用。
在安装FederoCore或者RedHatLinux系列操作系统时,如果选择了安装应用程序开发包,那么操作系统安装完毕后,GTK+开发包就已经安装好了。
如果没有安装,请从网络上(http:
//www.gtk.org)免费下载一份GTK源代码并安装到系统上,也可以插入Linux安装光盘在系统提示下进行安装。
由于安装过程非常简单,这里就不再赘述了。
12.2一个简单的例子
先来看一个简单的GTK+图形界面程序的例子,了解这类程序的一般框架。
这个程序创建了一个窗口,并在窗口中放置了一个按钮,实现代码如例12-1所示。
例12-1 gtkwin.c
#include
/*定义回调函数hello,单击按钮时系统自动调用*/
voidhello(GtkWidget*widget,gpointer*data)
{
g_print("buttonclickedanddata=%s\n",(char*)data);
}
/*定义回调函数destroy,关闭窗口时系统自动调用*/
voiddestroy(GtkWidget*widget,gpointer*data)
{
gtk_main_quit();
}
intmain(intargc,char**argv)
{
/*定义指向控件的指针*/
GtkWidget*window;
GtkWidget*button;
/*初始化图形显示环境*/
gtk_init(&argc,&argv);
/*创建窗口,并设置当关闭窗口时,要执行的回调函数*/
window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(GTK_OBJECT(window),"destroy",
GTK_SIGNAL_FUNC(destroy),NULL);
/*设置窗口的属性*/
gtk_container_border_width(GTK_CONTAINER(window),20);
/*创建按钮,并设置当单击按钮时,要执行的回调函数*/
button=gtk_button_new_with_label("HelloWorld");
g_signal_connect(GTK_OBJECT(button),"clicked",
GTK_SIGNAL_FUNC(hello),"Iamfrombutton");
/*将按钮加入到窗口中*/
gtk_container_add(GTK_CONTAINER(window),button);
/*显示按钮和窗口*/
gtk_widget_show(button);
gtk_widget_show(window);
/*进入消息处理循环*/
gtk_main();
return0;
}
编译并运行:
[root@mcitmp]#gcc-ogtkwingtkwin.c`pkg-config--libs--cflagsgtk+-2.0`
[root@mcitmp]#./gtkwin
注意:
编译命令中的字符串"pkg-config--libs-cflagsgtk+-2.0"两边是反引号(在键盘上位于数字字符1的左边)。
运行程序,显示如图12-2所示的界面:
图12-2 运行界面
程序说明。
(1)所有的GTK+程序中都必须包含头文件gtk/gtk.h,它声明了所有GTK+编程中要使用的常量、数据结构和函数。
(2)所有GTK+程序开始都要调用函数gtk_init(gint*argc,gchar***argv)。
该函数定位和打开图形显示,并对颜色、信号等进行初始化。
在命令行输入的参数由该函数传递给GTK+,该函数读取并获得与它有关的命令行参数。
(3)函数gtk_window_new(GTK_WINDOW_TOPLEVEL)用于创建顶级窗口,GTK+程序的主窗口被称为顶级窗口。
虽然一个程序可以创建多个顶级窗口,但通常只创建一个。
(4)图形界面下,用户的任何一个操作(如单击鼠标左键,按下键盘上的某个键)都称为发生了一个事件,GTK+都有相应的消息信号产生,如果程序中定义了处理该消息信号的函数,在事件发生后,消息信号处理函数会自动调用。
这样的消息信号处理函数也称为回调函数,因为这种函数虽然是在程序里定义,但程序中并没有显式调用而是由系统在事件发生后自动调用。
hello()和destroy()就是两个处理消息的回调函数。
destroy函数中调用了GTK+函数gtk_main_quit,它使程序退出gtk_main()并完成一些清理工作。
g_signal_connect函数用于在控件和消息处理函数间建立关联,该函数的第一个参数为产生消息的控件,第二个参数是消息名,第三个参数是消息发生后要调用的函数名,第四个是传递给消息处理函数的参数,可以为空值(即NULL)。
关于消息和回调函数的详细内容请参考下一节。
(5)gtk_container_border_width函数用于设置窗口边框的宽度,这是一个设置窗口属性的函数。
(6)gtk_button_new_with_label函数创建一个带文本标签的按钮,它完成内存分配,并把所分配到的内存的首地址赋给GtkWidget类型的指针。
(7)函数gtk_container_add通知GTK+将按钮加入到主窗口中,函数gtk_widget_show用于显示控件。
(8)gtk_main()使GTK+进入消息处理循环。
每个GTK+应用程序都有一个gtk_main函数,该函数使程序进入休眠状态。
当有事件发生,如果程序中有相应的处理函数,gtk_main()就调用相应的消息处理函数。
(9)为了方便编译,可以在源程序所在目录下编写一个Makefile文件:
CC=gcc
program=gtkwin
#PATH+=/usr/include/gtk2.0
LDLIBS=`pkg-config--libsgtk+-2.0`
CFLAGS=-Wall-g`pkg-config--cflagsgtk+-2.0`
$(program):
$(program).o
$(CC)$(LDLIBS)$(program).o-o$(program)
$(program).o:
$(program).c
$(CC)$(CFLAGS)-c$(program).c
clean:
-rm-f$(program)
-rm-f*.o
此时编译程序可以简化为:
[root@mcitmp]#make
要删除编译所产生的中间文件和可执行文件,可以执行命令:
[root@mcitmp]#makeclean
如果要编译其他GTK+程序,把Makefile文件中的gtkwin改为相应源程序的文件名即可。
Makefile的编写和make命令的使用请参考第5章。
例12-1程序中用到了一些GTK+预定义的函数和数据类型,表12-2和表12-3对其作一个简单的介绍。
表12-2 GTK+预定义的函数
前缀
含义
G
glib定义的数据结构
g
glib声明的数据类型
g_
glib定义的函数
gtk_
GTK+定义的函数
Gtk
GTK+库的对象或数据结构
GTK
GTK+定义的宏或者常量
表12-3 GTK+预定义的数据类型
GTK+的数据类型
C语言数据类型
gchar
char
gint
int
glong
long
gboolean
char
gfloat
float
gdouble
double
guchar
unsignedchar
guint
unsignedint
gulong
unsignedlong
gpointer
void*
gint8
在任何平台上都是8位的整型
gint16
在任何平台上都是16位的整型
gint32
在任何平台上都是32位的整型
guint8
在任何平台上都是8位的无符号整型
guint16
在任何平台上都是16位的无符号整型
guint32
在任何平台上都是32位的无符号整型
12.3 消息和回调函数
图形用户界面的程序是事件驱动的程序。
程序进入gtk_main函数后,等待事件的发生,一旦发生某个事件,相应的信号将产生。
如果程序中定义了相应的消息处理函数,系统会自动进行调用。
消息处理函数(或称回调函数)的原型是:
voidcallback_func(GtkWidget*widget,gpointerfunc_data);
参数widget指向要接收消息的控件,参数func_data指向消息产生时传递给该函数的数据。
函数g_signal_connect在控件和消息处理函数间建立关联,该函数的原型是:
gulongg_signal_connect(GtkObject*object,gchar*name
Gcallbackcallback_func,gpointerfunc_data);
各参数说明如下。
object:
指向产生消息的控件。
name:
消息或事件的名称。
callback_func:
事件发生后要执行的回调函数。
func_data:
传递给回调函数的数据,与callback_func()的第二个参数相同。
该函数的返回值用于区分一个控件的一个事件对应的多个处理函数。
一个控件上可以发生多个事件,比如单击一个按钮,双击一个按钮。
对于一个控件上的每个事件可以有0个、1个或多个处理函数。
该事件发生时,将按声明的顺序逐个调用这些函数。
对应于某个事件,如果控件没有定义处理函数,那么事件发生时将没有响应,系统忽略此事件。
还有一个与g_signal_connect()类似的函数:
gintg_signal_connect_swapped(GtkObject*object,gchar*name
GCallbackcallback_func,GtkObject*slot_object);
它的第四个参数指向一个GTK+控件的指针。
它与g_signal_connect()的区别在于相应的回调函数只有一个参数:
voidcallback_func(GtkObject*object);
通常object指向一个控件。
可以在上一节的例子中"gtk_container_add(GTK_CONTAINER(window),button);"之前加上一条语句:
g_signal_connect(GTK_OBJECT(button),"clicked",
GTK_SIGNAL_FUNC(gtk_widget_destroy),GTK_OBJECT(window));
则对应于button按钮的"clicked"事件有两个处理函数,一个是程序中定义的回调函数hello,还有一个是GTK+预定义的gtk_widget_destroy()。
gtk_widget_destroy()的作用与程序中的destroy()相同。
编译运行程序,如果单击按钮,系统先调用hello函数(因为它先与button控件建立关联)在命令行上打印出一行消息,然后调用gtk_widget_destroy()退出程序。
如果要删除控件和消息处理函数的关联,可以调用g_signal_disconnect(),该函数的原型是:
voidg_signal__handler_disconnect(GtkObject*object,gulongid);
参数说明如下。
object:
要删除消息处理函数的控件。
id:
g_signal_connect()或g_signal_connect_swapped()函数的返回值。
下面这个函数可以删除某控件的所有消息处理函数:
voidg_signal_handlers_destroy(GtkObject*object);
12.4 GTK+的面向对象机制
对于那些没有接触过面向对象语言的读者来说,本节的内容可能比较难以理解。
不过没有关系,这并不会影响后面内容的掌握。
介绍本节的内容只是为了简单地介绍一下GTK+中是如何模拟面向对象机制的。
面向对象编程语言(如C++、Java)把数据和对数据的操作封装在一起构成类,由类来产生对象,由对象来构建程序。
类中对数据的操作由函数来完成,这种函数被称为成员函数或方法。
面向对象语言通过继承、重载、多态等机制大大增强软件的可重用性和可维护性。
C语言虽然不是面向对象语言,但GTK+以及建立在其上的GNOME库却使C语言模拟出了一些典型的面向对象机制,如封装、继承和多态。
为了较好的理解GTK+程序,了解GTK+中的面向对象机制也是很有必要的。
对象的一个主要特性是将数据和对数据的操作封装在一起,受保护的私有数据只能通过成员函数才能访问和修改。
GTK+使用C语言的结构体来模拟对象,虽然有些缺陷但基本模拟出了对象的基本特征。
有了对象作为基础,通过在对象中加入新的数据和对这些数据进行操作的函数,就实现了继承。
被继承的类(类相当于一种自定义数据类型,由类来定义对象)称为父类或基类,由基础类派生出来的类称为子类或派生类。
子类继承了父类的数据和对这些数据进行操作的成员函数,并加入了新的数据和成员函数,实现了对原有父类的重用和扩展,从而实现了可重用性和可扩展性。
GTK+中有一个类,它是所有其他类的父类,这个类是GtkObject。
GTK+中最常用的按钮控件也是一个类,它继承自GtkObject。
它与GtkObject的继承关系是:
GtkObject->GtkWidget->GtkContainer->GtkBin->GtkButton
使用C语言如何模拟继承呢?
对象(类)是由结构体模拟的,每一个子类所在的结构体都包含了父类的结构体,子类结构体的第一个成员是其父类结构体,示例代码如下:
structGtkObject{
...
};
structGtkWidget{
GtkObjectobject;
...
};
structGtkContainer{
GtkWidgetwidget;
...
};
structGtkBin{
GtkContainercontainer;
...
};
structGtkButton{
GtkBinbin;
...
};
从上述代码可以看到,每个子类都包含了其父类的所有数据,并且父类的数据位于子类结构体的开始。
对于一个GtkButton类型的button控件变量(它其实是一个指向GtkButton结构体的指针),通过宏GTKBIN(button)就得到了其父类(GTK+预定义的宏GTKBIN其实是进行了强制类型转换,把一个GtkButton类型的指针强制转化为GtkBin类型的指针)。
例12-1中的GTK_OBJECT(button)就是进行了这样的转换。
为了便于理解,我们写一个测试程序,如例12-2所示。
例12-2 test.c
#include
#include
#defineFATHER(child)(structFather*)(child)
voidprint1(inti)
{
printf("thisisfatherandi=%d\n",i);
}
voidprint2(inti)
{
printf("thisischild andi=%d\n",i);
}
structFather{
int a;
void(*pointer1_to_function)(int);
};
structChild{
structFatherf;
intb;
void(*pointer2_to_function)(int);
};
voidfather_member_funtion(structFather*f,char*string)
{
printf("\n");
f->pointer1_to_function(f->a);
printf("%s\n\n",string);
}
intmain()
{
structChild*p_child;
p_child=(structChild*)malloc(sizeof(structChild));
p_child->f.a=10;
p_child->f.pointer1_to_function=print1;
p_child->b =20;
p_child->pointer2_to_function =print2;
p_child->pointer2_to_function(p_child->b);
structFather*p_father=FATHER(p_child);
p_father->pointer1_to_function(p_father->a);
father_member_funtion(p_father,"hello");
return0;
}
程序输出:
thisischildandi=20
thisisfatherandi=10
thisisfatherandi=10
hello
程序说明。
(1)结构体Father相当于GTK+中的父类,而结构体Child就相当于子类。
结构体Father有一个成员变量和一个成员函数(实际上是一个指向函数的指针,函数指针的内容请参考第4章4.3.4指针和函数一节)。
结构体Child在其头部包含了结构体Father,并增加了一个成员变量和一个成员函数(也是一个函数指针)。
结构体Father和Child模拟了类,Child模拟继承了Father。
(2)程序定义了一个指向结构体Child的指针,并对Child中的成员进行了初始化。
然后调用了结构体Child的成员函数pointer2_to_function。
通过宏FATHER(p_child)将指针p_child强制转换为指向Father结构体的指针。
事实上,p_child和p_father的值是一样的,它们都保存着结构体Child的首地址。
宏FATHER(p_child)类似于例12-1程序gtk_container_add(GTK_CONTAINER(window),button)中的GTK_CONTAINER(window)。
我们注意到,p_father调用了它自己的成员函数pointer1_to_function。
(3)father_member_funtion函数是类Father成员函数的另一种实现方法,这种方法避免了在结构体Father中保存函数指针。
GTK+定义了很多生成对象或对对象进行操作的函数。
例如,下面就创建了一个对象:
GtkWidget*button;
button=gtk_button_new_with_label("label");
所有创建对象的函数在其名称上都有"new"这个词。
函数gtk_button_new_with_label创建了一个显示文本的
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- QT GTK