linux环境程序设计教学第22章界面开发基础_第1页
linux环境程序设计教学第22章界面开发基础_第2页
linux环境程序设计教学第22章界面开发基础_第3页
linux环境程序设计教学第22章界面开发基础_第4页
linux环境程序设计教学第22章界面开发基础_第5页
已阅读5页,还剩54页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第22章界面开发基础在程序设计中,界面设计是最有难度和最富于挑战性的工作之一。因为其中不仅涉及编程的问题,还需要关心用户的需求、使用是否方便和美感方面的问题。本章开始将介绍Linux系统上使用C语言设计界面的相关知识,其中包括Linux系统的桌面环境、界面开发的函数库、界面开发工具、窗体信号和事件,以及与界面开发有关的数据结构和编程技巧。本书的重点部分为GNOME桌面上的GTK+图形界面开发技术,这是当前Linux系统中最流行的界面技术。22.1Linux常用桌面环境在计算机中,桌面环境(DesktopEnvironment)为操作系统提供一个图形用户界面(GUI)。典型的桌面环境包括图标、视窗、工具栏、文件夹、壁纸等对象,并且具备像鼠标拖放这样的操作能力。整体而言,桌面环境在设计和功能上的特性赋予了它与众不同的外观和使用感受。与其它商业操作系统不同之处是,Linux系统具有多种桌面环境供用户选择,如GNOME、KDE、Xfce、HILDON等。它们有些是根据不同的应用方向编写,例如GNOME通常作为工作站上的桌面环境,KDE通常作为服务器上的桌面环境。有些是针对特殊的硬件平台而设计,例如HILDON是为移动设备开发的桌面环境。下面将分别介绍这几种常用的桌面环境。22.1.1GNOME桌面环境GNOME,即GNU网络对象模型环境(TheGNUNetworkObjectModelEnvironment),又称为GNOME项目。它作为GNU项目的子项目,目标是为Linux操作系统构造一个功能完善、操作简单以及界面友好的桌面环境。Fedora、Ubuntu等Linux发布版选择的就是GNOME桌面环境作为标准配置。GNOME项目包含两方面内容,首先是提供GNOME桌面环境,让Linux系统的最终用户能使用到符合直觉,并且十分吸引人的桌面。其次是提供GNOME开发平台,该平台是一个能使开发的应用程序与桌面其它部分集成的可扩展框架。22.1.1GNOME桌面环境GNOME桌面使用方便,易于管理,其优点包括易用性和国际化。易用性指的是几乎所有人都可以轻松运用此桌面和应用程序,即使是并不熟悉Linux的新用户或残障人士。国际化是用于为桌面和应用程序提供多种语言的选择。一个典型的GNOME桌面环境包含下列组件。面板:就是GNOME桌面上的区域,通过这些区域可以访问所有的系统应用程序和菜单。面板可自由配置,如图所示。22.1.1GNOME桌面环境菜单:可以通过菜单访问所有GNOME桌面功能。其中“应用程序”菜单访问几乎所有标准功能、命令和配置选项。窗口:可以同时显示多个窗口。在每个窗口中都可以运行不同的应用程序。窗口管理器为窗口提供框架和按钮。窗口管理器可以执行诸如移动、关闭和改变窗口大小这些标准操作。工作区:可以将GNOME桌面分为几个独立的工作区。工作区是指用户在其中工作的离散区域。用户可以指定GNOME桌面上的工作区数量。使用时能在不同的工作区间切换,但每次只能显示一个工作区。Nautilus文件管理器:Nautilus文件管理器提供了一个集成的访问点,可以访问文件和应用程序。在文件管理器窗口内能显示文件内容,或者从文件管理器中用相应的应用程序打开文件。另外,可以使用文件管理器管理文件和文件夹。桌面:桌面位于桌面上所有其它组件的后面。桌面是用户界面的活动组件。将对象放在桌面上可以快速访问文件和目录,或启动常用的应用程序。也可以在桌面上右击打开一个菜单。首选项:GNOME桌面包含专用的首选项工具。每一个工具控制GNOME桌面行为的一个特定部分。要启动首选项工具,请从“主菜单”中选择“系统设置(首选项)”命令。从子菜单中选择要配置的项目。22.1.1GNOME桌面环境GNOME开发平台由一系列子项目组成,其中最重要的子项目如表所示。项

名说

明ATK可达性工具包Bonobo复合文档技术GObject用于C语言的面向对象框架GConf保存应用软件设置GNOMEVFS虚拟档案系统GNOMEKeyring安全系统GNOMEPrintGNOME软件打印文档GStreamerGNOME软件的多媒体框架GTK+构件工具包Cairo复杂的2D图形库HumanInterfaceGuidelinesSun微系统公司提供的使得GNOME应用软件易于使用的研究和文档LibXML为GNOME设计的XML库ORBit使软件组件化的CORBAORBPangoi18n文本排列和变换库Metacity窗口管理器22.1.1GNOME桌面环境GNOME拥有很多子项目,常见的有时钟、天气预报和CPU性能监视器。不过,并非所有的子项目都被包含在GNOME发布版内,只有非常成熟,并且是大多数用户迫切需求的项目才被发布。开发基于GNOME应用软件的函数库为GTK+,因为GNOME桌面环境本身就是使用GTK+开发的。除此以外,较为底层的X11函数库或Cairo函数库在开发中也十分常用,通常是在有特殊要求的应用程序中使用。22.1.2KDE桌面环境KDE,全称为K桌面环境(KDesktopEnvironment),先于GNOME桌面环境产生,多用在Linux服务器版和程序开发工作站中,如RedHat、OpenSUSE等Linux发行版使用KDE桌面作为默认配置。KDE桌面核心为Qt程序库,是TrollTech公司的专利技术,因此很多自由软件开发者存在版权问题上的顾虑,但是其绚丽的界面效果又吸引了开发者的注意。Fedora和Ubuntu也推出配置KDE桌面环境的发行版。典型的KDE桌面环境如图所示。22.1.2KDE桌面环境KDE开发语言为C++,并且使用面向对象思想设计。KDE提供的Qt/KOM/OpenParts开发平台组合可与商业软件相媲美,在该平台上组件的复用性极高,GUI程序执行效率也超过其它桌面环境,这也是KDE广受欢迎的因素之一。KDE应用程序从KApplication类派生,而图形界面应用线程从KTopLevelWidget类派生。为了简化图形界面开发难度,KDE集成了所见即所得界面设计软件,并能够与Kdevelop环境无缝结合。KDE目前的体系结构如图所示。22.1.2KDE桌面环境KDE桌面环境由一系列子系统构成,在KDE上进行开发可从这些子系统继承相应的对象。主要的子系统如表所示。系

名说

明DCOP桌面通信协议(DesktopCommunicationProtocol)KIO网络透明的输入输出SYSCOCA用于C语言的面向对象框架GConf系统配置程序Kparts嵌入式组件(动态链接库)KHTMLHTML4.0兼容的库XMLGUI动态的GUI体系结构(KAction)aRts多媒体系统,类似于GStreamer22.1.2KDE桌面环境KDE桌面环境最新版本为KDE4。KDE3经过5年的发展后,功能越来越丰富,所提供的应用程序也越来越多。但是,KDE3的维护难度随之增加,很多并非用户必需的软件占用了大量系统资源。KDE4设计时已经考虑到KDE3过于臃肿的问题,因此将功能进行了适当简化,并减少系统资源的占用率,还原了KDE桌面环境本色。22.1.3移动设备与HILDON桌面环境随着移动设备的逐渐普及,越来越需要拥有一个能在多种厂商的硬件环境上运行的操作系统。Linux系统拥有良好的延展性,可为移动设备进行裁剪,将一些最适合移动设备应用的内核组件和应用程序移植到ARM、x86等系统结构的设备上,由此产生了多个针对移动设备的操作系统,例如Maomo和Moblin系统。前者是由诺基亚公司开发的一款运行在智能移动电话上的操作系统,后者是英特尔公司开发的面向于移动互联网设备、车载电脑、Netbook和健康电子设备上的操作系统。这两种操作系统使用的桌面环境是诺基亚公司以GTK+和其它GNOME子项目为基础建立的HILDON桌面环境,它提供了对手写板的支持,以及适用于手持设备上的大字号界面风格等。基于HILDON桌面环境的Ubuntu发布版主界面如图所示。22.1.3移动设备与HILDON桌面环境HILDON桌面环境是GNOME的裁剪版,所以它保留了GNOME的大多数特性,但同时也增加了一些新组件。其中最重要的组件是Matchbox,它是专门针对移动设备开发的窗体管理器。移动设备的屏幕通常较小,Matchbox每次只允许一个程序的窗体出现在主屏幕上。HILDON体系结构如图所示。22.2GTK+图形环境介绍GNOME桌面环境是Linux系统中最常用的桌面环境,本节将介绍与GNOME桌面环境相关的开发库。GTK+库是其中最重要的函数库,它提供了基本的窗体构件。GLib库提供了GObject对象,使GTK+具备面向对象的特征。另外GNOME桌面环境还有一系列底层的函数库,在开发图形界面时了解这些函数库也非常重要。GTK+是设计GIMP软件时创建的函数库,后来发展为Linux系统下开发图形界面的应用程序的主流开发工具之一,并成为GNU计划的重要组成部分。GTK+设计图形界面应用程序简单高效,开发者可以从较高层次开始界面的设计。例如,它可以显著节约开发时间,让开发者把精力集中在项目真正重要和真正独特的地方,而不必重复公共的功能。对于用户来说,这意味着他们使用的应用程序之间具有更好的一致性:工具包能在哪使用,应用程序就能跟到哪里。在现实中,现代的图形界面函数库的作用不仅仅是避免重复,它们提供了许多高级功能。开发者希望在应用程序中拥有这些功能,但是用别的方法得不到这些功能。因为在这类函数库上所投入的时间和工作,要远远超过在单一应用程序上的花费。22.2GTK+图形环境介绍GTK+的优势在于,它提供了广泛的选项,用于把工作扩展到尽可能多的人,其中包括一个针对国际化、本地化和可访问性的完善的框架。对开发者和用户来说,GTK+简单易用、设计良好、灵活而可扩展。并且GTK+是可移植的,它得到了积极的开发与维护,围绕它有一个充满活力的社区。总结起来,GTK+具有3个重要特性,国际化、本地化和可访问性(通常分别缩写为i18n、l10n和a11y)。它们的介绍如下:国际化是将程序兼容非开发程序语言的过程,所以应用程序不依赖于对任何特定语言的任何假设。此外还要考虑所使用的不同脚本和字母表、不同的书写方向、输入法等。本地化与i18n密切相关,因为国际用户准备应用程序不仅仅是改变语言。程序还必须能够理解并尊重日期、货币显示、数字标注、文本排序所使用的不同习惯,以及许多可能不太注意的细节之处。例如有些符号的使用,在世界的不同地方可能会被认为是不恰当的或无礼的。可访问性是让每个人都可以使用的应用程序。有些用户的视力不佳,有些人可能不能用键盘或鼠标,而有些人可能只能移动他们的眼睛。要确保每个需要使用该应用程序的用户都能使用需要做许多工作。22.2GTK+图形环境介绍GTK+是可定制的,这样就可以让它适应各种用户的需求。GTK+有一个系统可以在所有应用程序之间复制设置,包括主题的选择。主题是一组同时发布的定制设置,它会影响GTK+的基本控件的视觉效果,甚至某种程度上的行为方式。使用主题可以获得与众不同的观感,如图所示。GTK+3是GTK+最新版本,其新特性包括使用Pango改进的文本渲染、新主题引擎、使用ATK改进的可达性、完全转换到使用UTF-8的Unicode和更灵活的应用程序接口。但是它和GTK+1不完全兼容,因此必须由开发者做移植工作。由于GTK+1更快、相对更简单或更加适合嵌入式应用,所以还被继续使用。在后面的章节中,还会继续对GTK+做进一步探讨,GTK+是本书界面开发部分的重点。22.3GLib库介绍GLib是GTK+和GNOME工程的基础底层核心程序库,是一个综合用途的实用的轻量级的C程序库。GLib对C语言进行了扩展,将一些常用数据结构和相关处理函数封装到其中。为了便于移植,GLib提供了一套与硬件无关的数据类型,以及进程、动态调用、线程、主事件循环和常用的宏。GLib由基础类型、对核心应用的支持、实用功能、数据类型和对象系统5个部分组成。22.3.1基础类型基础类型是在ANSIC标准数据类型的基础上进行的扩展,去掉了类型字长与硬件的关系。例如int类型可能在某些平台上字长为2字节,而在另一些平台上字长为4字节,为程序的移植带来麻烦。而GLib提供的gint类型统一为4字节。所有GLib基础类型均以小写字母g为前缀,其后是标准数据类型的名称。例如,gpointer是指针类型(void*)、guint是无符号整型(unsignedint)。这些类型与ANSIC标准数据类型可以混合使用,不影响程序性能。C语言中没有布尔类型,GLib增加了gboolean类型定义布尔值,并且用常量TRUE和FALSE作为该类型的值。为了方便地操作这些基础类型,GLib还定义了一组相关的宏。如G_MAXINT可表示最大的gint类型数,G_E表示自然对数,G_PI表示圆周率。宏GPOINTER_TO_INT()将指针类型转为整型,宏GINT_TO_POINTER()将整型转为指针。22.3.2对核心应用的支持GLib对核心应用的支持包括事件循环、内存操作、线程操作、动态链接库的操作和出错处理与日志等。(22_3_2.c)上例程创建了3个线程,其中t_1和t_2操作互斥对象,t_3检索前两个线程是否结束。若t_1与t_2已结束,则执行g_main_loop_quit()函数退出主事件循环。由于线程的运行是不确定的,所以每次输出结果可能不相同。程序中,首先定义一个结构类型,用于保存创建的主事件循环的指针和线程运行时的最多循环次数。在ANSIC标准中,要为数据结构分配内存可使用表达式“type*type=(type*)malloc(sizeof(type));”,释放时用free()函数。malloc()表达式极为冗长,使用起来很不方便。GLib中提供了类似的函数g_malloc()和g_free(),为了方便调用,可使用g_malloc()函数封装的宏g_new()。该宏有两个参数,第一个是数据类型,第二个是分配空间的长度,这段代码中只用到了一个Arg数据结构,所以是g_new(Arg,1)。在程序结束时用g_free()来释放。使用g_thread_init()函数进行线程初始化,先用g_thread_supported()函数判断初始化是否成功,成功时返回TRUE。然后用g_main_loop_new()函数创建主事件循环对象GMainLoop,用g_main_loop_run()函数运行主事件循环。最后,在程序结束前,使用g_main_loop_quit()函数主事件循环,否则程序无法退出。该函数在线程3内调用。22.3.3实用功能GLib实用功能提供了多种常用的算法,这些算法涵盖字符串处理、计时器、随机数和XML解析等数十种功能。(22_3_3.c)GLib中,创建对象的函数以g为前缀,new为后缀。代码中首先用g_timer_new()函数创建了计时器,然后用g_timer_start()函数执行计时器。随机数因子使用g_rand_new()创建,并启动一个G_MAXINT次循环,在循环内用g_rand_int_range()函数创建随机数并输出。循环次数到达后,首先释放循环因子,停止计时器。最后,将计时器中的数值输出到终端。22.3.4数据类型GLib中定义了字符串、链表、堆栈和队列等十余种常用数据结构类型,并定义了相关的操作函数。(22_3_4)GLib的字符串处理函数与C++的字符串对象一样方便。代码中GString说明符用于声明字符串类型,然后用g_string_new()函数创建字符串。g_string_append()函数用于向字符串后追加内容,g_string_erase()函数用于删除指定内容,g_string_prepend()函数用于在字符串前插入内容,g_string_insert()函数用于在指定位置插入内容。g_print()函数的作用和格式与printf()函数相同。GLib提供了内存块数据类型,可用于分配较大的内存空间,并且可以随时改变内存块的长度。(22_3_4)代码中,g_mem_chunk_new()函数用于创建内存块,然后用g_mem_chunk_alloc()函数创建对象。该内存块分配的长度为50字节,实际使用的长度为80字节,可见其本身将占用一定的内存空间。此外,GLib支持几乎所有C语言的数据结构类型,这些数据结构一般以G为前缀。例如,GLink为单向链表。所有相关函数以g_link为前缀。22.4GObject对象介绍GLib为C语言提供了面向对象的GObject对象系统。该系统在语法上与ANSIC完全兼容,同时具有跨平台特性,甚至可以被其它程序设计语言使用。GObject对象系统使用GLib提供的数据结构,同时也使用了GLib提供的相关算法,其扩展性、灵活性不逊于C++语言。GTK+等函数库使用的对象系统正是GObject。因此,掌握GObject对象系统是掌握GTK+函数库的基础,也为使用GStreamer函数库带来了方便。该系统的本质可以概括为以下几点。GObject对象系统是一个通用的面向对象系统,可以实现面向对象思想的所有设计方法。如派生、继承等。GObject对象系统使用GLib基础类型。GObject对象系统提供的GObject基本类型是类的实现。GObject对象系统提供了完善的信号机制,允许用户使用非常灵活的自定义虚拟函数,以及回调函数。GObject对象系统具备可扩展性,能够在该基础之上开发出更为复杂的对象系统,支持所有的能被用作处理对象属性或其它参数化类型的基本类型。22.4.1对象系统面向对象思想是当前主流的程序设计思想,缺乏面向对象系统,C语言将难以快速地完成复杂应用程序。GObject系统是GLib对C语言的扩展,使其具备了面向对象特性。该系统的实现以Gtype为基础,Gtype是GLib运行时类型的认证与管理系统。Gtype可以定义任何复杂的数据结构,并通过GLib基础类型与ANSIC标准兼容。使用Gtype和GObject前,首先要调用g_type_init()函数对其进行初始化。实现类型可分为静态类型和动态类型两种,前者无法在运行时进行加载或卸载操作,而后者运行时加载和卸载非常灵活。新类型使用前必须创建,g_type_register_static()函数用于创建静态类型,其自身信息的数据结构为GTypeInfo类型。g_type_register_dynamic()函数用于创建动态类型,其自身数据结构为GTypePlugin类型。新类型创建后,GObject会自动注册。因此,创建函数只需要运行一次。如果需要创建基础类型,可使用g_type_register_fundamental()函数来注册,它同时使用GTypeInfo和GTypeFundamentalInfo类型作为自身的数据结构。22.4.1对象系统对象由对象ID、类结构和实例3部分组成。对象ID是对象唯一的标识,用于识别对象的身份。类结构是对象内部的数据成员,由所有对象共同拥有。实例是由类创建的对象,每个类可创建多个实例。(22_4)上例是类的创建代码,该代码被定义在头文件中。创建对象时,只需要在源代码文件中包含该头文件即可。头文件部分包括预处理、宏定义、数据结构定义和函数原型定义。数据结构定义部分创建了两个数据结构对象Baby和BabyClass,其中结构类型_Baby是Baby对象的实例。在代码中,每创建一个Baby对象,相应的Baby结构也会在同一时间创建。Baby对象中parent成员是父类的指针,GObject对象系统的父类为GObject类。其它成员可以是公共成员,例如数据成员age和name分别表示年龄和名字。cry成员是类的方法,表示哭闹。数据类型_BabyClass是Baby对象的类结构,它也被所有Baby对象的实例所共享。BabyClass中的parent_class成员是GObjectClass类型,表示其父类为GObject,因为GObejctClass是所有对象的父类。函数指针Baby_born为所有Baby对象实例共有。此外,代码中还定义了3种成员函数的原型。baby_get_type()等函数是用于获得或设置对象的成员数据。baby_new()等函数是用来创建对象的实例。baby_info()函数用来显示此对象的当前状态。22.4.2GObject系统中宏定义在上例中定义了两个重要的宏,其中宏BABY_TYPE()是对baby_get_type()函数的封装,用于直接获得对象的信息。宏BABY()是对宏G_TYPE_CHECK_INSTANCE_CAST()的再次封装,目的是将一个Gobject对象强制转换为Baby对象,对于对象继承的意义非常重大。(22_4)程序中,将该对象的所有函数予以实现。baby_init()和baby_class_init()函数分别用来初始化实例的数据结构和类的数据结构。这两个函数不用显式调用,而是用宏将其转换,然后为GTypeInfo结构赋值,最后由GType对象进行自动处理。22.4.3GTypeInfo结构GTypeInfo结构中定义了对象的类型信息,包括以下内容。类结构的长度:必需,本例为BabyClass结构的长度。基础初始化函数:可选,与C++构造函数作用相当。基础结束化函数:可选,与C++析构函数作用相当。类初始化函数:可选,即baby_class_init()函数,使用宏GclassInit()转换。类结束函数:可选。实例初始化函数:可选,即baby_init()函数。GType变量表:可选。22.4.3GTypeInfo结构结束GTypeInfo结构的定义后,可使用g_type_register_static()函数注册对象的类型。该函数共有4个参数,第1个参数为父对象类型,即宏G_TYPE_OBJECT(),用于表示GObject类。第2个参数为对象名,本例为Baby。第3个参数是GTypeInfo数据结构指针,本例赋值为&babyinfo。第4个参数是注册成功后返回的对象ID。创建所有基于G_OBJECT对象的子类都可以使用g_object_new()函数。第1个参数为对象ID。第2个参数为其后参数的数量,NULL为没有。第3个参数开始均为GParameter类型,它是一个结构体,定义如下:structGParameter{

constgchar*name;

GValuevalue;};其中,第2个参数为GValue类型,此类型是基础的变量容器对象,可用于封装变量的值和变量的类型。22.4.4信号机制信号机制是GObject系统中对象的行为方式。当对象的某个条件满足时,将发出一个相应的信号,该信号可被注册的回调函数获得,并执行相关代码。使用信号机制不需要用多线程去反复检查某个变量,系统开销将降低。一个对象可以有多个信号,但也可不定义信号。当对象定义了信号时,通常用枚举类型为信号命名。例如,可以用FINAL_SIGNA表示最后一个信号,而不去实现该信号的功能,这是一种程序设计惯例。本例中,Baby对象定义实现了BABY_BORN信号,该信号在对象创建时产生。信号的标识用静态的整型指针数组来保持,以便信号处理时使用。所有对象的实例均有对象的类数据结构,相关信号也需要定义在类数据中。因此,信号是所有对象的实例所共有的,所有实例都可以进行信号处理,可在类初始化函数中创建信号。g_signal_new()函数用于创建一个新信号,创建成功时,返回值为该信号的标识符,否则返回–1。g_signal_emit()函数用于向实例发射一个信号,上例的创建函数中调用g_signal_emit()发出BABY_BORN信号。然后执行baby_born()函数,在终端中输出一行“消息:一个婴儿出生了”信息。22.4.4信号机制实例的数据结构中保持着对象实例所有属性及方法,属性定义为变量或指针,方法则定义为函数指针。因此,函数必须使用static修饰符修饰为静态类型,只有此种情况才能有效地取得函数的地址对象实例所有的属性和方法一般都定义在对象的实例结构中,属性定义为变量或变量指针,而方法则定义为函数指针。因此,函数一定要定义为static类型,只有这样才能有效地为函数指针赋值。GObject对象系统十分庞大,本节并未涵盖其全部内容,只是列举了其中最常用的函数和数据类型。在GTK+部分,读者将详细了解GObject对象系统的作用,以及信号机制的实际意义。22.52D图形引擎Cairo介绍Cairo是Linux系统中重要的2D矢量图形引擎,用于在屏幕或打印机中输出2D图形。它已成为Linux系统图形领域的重要组件。GNOME、GTK+、Pango等许多软件使用了它提供的2D图形引擎。Cairo项目提供了一个开源的函数库,通过该函数库可以绘制出多种图形,并以常见的图像格式输出。另外,Cairo支持PostScript格式和PDF格式,很多阅读软件中都通过调用Cairo库实现自身的矢量图文件解码功能,使屏幕显示与打印机输出的效果接近。图形学是计算机科学的重要领域,Cairo的设计思想来自于PostScript和PDF模型,因为它们都是使用几何学矢量空间来构图的系统。矢量图是以点和线的比例构图的系统,Cairo以精确比例为目标,因此非常适合于图形界面应用程序中的各种简单图形效果实现。目前,Cairo已能做到以极少的代码输出极为复杂的图形,成为Linux系统上最为出色的绘图系统之一。Cairo使用C语言编写,全部源代码公开。所提供的函数库也是以C语言为主要版本。矢量图也具备一定限制,虽然它可以无限放大,但是太大时反而超出比例,影响图形的完整性。而太小时,图形的一些细节无法反映出来。在Linux系统中很多图形库都绑定了Cairo,使得Cairo受欢迎程度大大提高。例如,GTK+对Cairo提供了完美的支持,使Cairo能在界面上用几条简单的函数,绘制出各种想要的图形。22.52D图形引擎Cairo介绍Cairo函数库可分为3部分,最重要的是核心绘图库。核心绘图库提供了绘图的上下文,相对于画布。上下文使用cairo_t类型,该类型定义了画布的大小,并且可以保持一些绘图操作。其次是外表库,可以保存一些绘图的材质信息。然后是字体库,用于以矢量方式显示文字,对文字进行绘图操作。绘图上下文的操作并不复杂,通常只需要点、线和曲线。所有矢量图形都可以还原到这3种基本的元素中来。Cairo函数库允许裁剪,可以只保留核心绘图库。为了简化绘图的操作,Cairo提供了一些函数用于计算复杂图形的构成。例如用线组成矩形,由弧线运算为圆形或者椭圆形。特别是对于弧线,Cairo引进了坐标系统,弧线的弧度和长度作为平面坐标系中的路径。这种扩展使开发者不用了解复杂的图形学原理,而图形实现的代码也有所减少。另外,Cairo还提供了一些高级的函数,这些函数能对图形进行转换,包括对图形的缩放、旋转和剪切操作,以及对图形的复制操作。22.52D图形引擎Cairo介绍外表库包括绘图时所用的画笔信息,颜色信息,甚至是贴图纹理信息。这些信息是绘制高仿真图形的基础。例如,Cairo借用了OpenGL的外表材质机制,各种复杂的仿真电子游戏就是用这种机制进行纹理贴图实现的。模式是介于核心绘图库和材质库之间的接口,Cairo可以通过读取模式的内容,将模式作为绘图操作的蒙版,提供多图层的支持。Cairo对模式支持非常完善,从简单的实体模式到高级的逐变模式都能支持。下面首先介绍最基本的Cairo函数,可以将它们总结为5个基本函数,如表所示。函

名说

明cairo_stroke()用于实现上下文中的一次绘图操作cairo_fill()填充操作cairo_show_text()/cairo_show_glyphs()显示文本或图形操作cairo_paint()线条画笔操作cairo_mask()外表操作22.52D图形引擎Cairo介绍下面以一个简单的例子说明Cairo函数库的简单用法。(22_5.c)该程序用于绘制一个简单的图形并保存到png格式文件中。在编译该程序前首先需要安装Cairo库,通常安装GTK+时会一并安装。编译该程序只需要指定Cairo库的目录,编译指令如下:gcc-ocairo_testcairo_test.c-lcairo-I/usr/include/cairo22.6多媒体库GStreamer介绍GStreamer是GNOME桌面环境下用主流的多媒体应用的编程框架,可用于开发各种多媒体程序或数据流处理程序。目前,GStreamer已经能够处理MP3、RM、WMA、MPEG、MPEG2、AVI、Quicktime等多种格式的多媒体数据。GStreamer使用GObject对象系统建立,并将对媒体处理过程抽象为一套更为直观的对象系统。整个程序中的数据流称之为管道,数据经过管道处理后,被送到目的地。处理数据的核心部件作为插件开发,用户可以根据自身需要,开发出新的插件,或者去寻找自己所需要的插件。框架中所有具备具体功能的模块被设计为独立的组件,程序设计时可根据需要选择组件,并且这些组件都具备对象的特性。GStreamer框架中的对象有一个共同的父类,即GstElement对象。其子类是组成管道的基本组件,用来输入、输出或数据处理功能。因此,GStreamer是一个完全的面向对象系统,所有操作都是通过对象操作实现的。22.6多媒体库GStreamer介绍GstElement对象是封装过的独立个体,GstElement对象之间也只能通过接口相互访问。按照应用方向上的差异,可以将GstElement对象分为数据源元件、过滤器元件和接收器元件3类,如图所示。22.6多媒体库GStreamer介绍数据源元件(SourceElement):读取媒体数据的元件是数据源元件。该元件数据源元件只有输出端,而输入通常是与Linux系统紧密结合的。可以认为在管道中,数据源元件没有输入端。例如一个文件数据源,其本质上是通过read()系统调用读取本地文件中的内容。过滤器元件(FilterElement):过滤器元件既有输入端又有输出端。输入端获得来自数据源元件的原始数据,过滤器元件内部对数据进行处理后,通过输入端传递到接收器元件。一个典型的过滤器元件的例子是音频编码单元,它首先从外界获得音频数据,然后根据特定的压缩算法对其进行编码,最后再将编码后的结果提供给其它模块使用。接收器元件(SinkElement):接收器元件只有输入端。因为数据被通过系统调用送到了声卡或屏幕,接收器元件处于管道终端。例如一个音频输出接收器元件,该元件负责将接收到的数据写到声卡上,通常这也是音频处理过程中的最后一个环节。22.6.1过滤器过滤器用来进行数据处理的GstElement对象,通常是多媒体格式的解码器。在输入和输出端口数目上,过滤器并无任何限制。以AVI文件分离器为例,该元件的输入端为文件数据源。数据经过解码后,被分为视频流和音频流,分别输出到两个不同的接收器元件中,如图所示。22.6.1过滤器创建GstElement对象需要使用工厂对象GstElementFactory,工厂对象是GStreamer框架中用于创建对象的唯一方法。GStreamer框架包含多种工厂对象,可通过工程名称来区分。例如,下面的代码通过gst_element_factory_find()函数获得了一个名为mad的工厂对象,它之后可以用来创建与之对应的MP3解码器元件。GstElementFactory*factory;factory=gst_element_factory_find("mad");创建工厂对象后,就可使用gst_element_factory_create()函数创建特定的GstElement对象了。该函数有两个参数,第1个参数为工厂对象的实例,第2个参数是创建的元件名称。元件名称可使用查询方式获得,参数为NULL时,将使用工厂对象默认的文件名称。下列代码用于演示利通过已创建的工厂对象生成名为decoder的MP3解码器元件。GstElement*element;element=gst_element_factory_create(factory,"decoder");22.6.1过滤器当创建的GstElement不再使用的时候,还必须调用gst_element_unref()函数释放其占用的内存资源。gst_element_unref(element);GstElement属于GObject的子类,因此对象的属性可使用GObject对象系统提供的函数或宏来管理。所有GstElement对象的父类都是GstObject对象,并且从父类基础基本的属性。因为工厂对象创建函数gst_element_factory_make()和gst_element_factory_create()创建工厂对象和元件对象时会用到名称属性,可通过调用gst_object_set_name()和gst_object_get_name()函数设置和读取GstElement对象的名称属性。22.6.2衬垫衬垫(pad)是元件与管道外进行连接的通道,是GstElement对象重要的概念之一。对于元件而言,其所支持的媒体类型通过衬垫传递给其它元件。创建GstElement对象后,gst_element_get_pad()函数可获得该对象的衬垫信息。例如,下面的代码将返回element元件中名为src的衬垫。GstPad*srcpad;srcpad=gst_element_get_pad(element,"src");如果不知道衬垫名称,可通过gst_element_get_pad_list()函数列出指定元件中的所有衬垫。例如,下面的代码将输出element元件中所有衬垫的名称。GList*pads;pads=gst_element_get_pad_list(element);while(pads){

GstPad*pad=GST_PAD(pads->data);

g_print("衬垫名称为:%s\n",gst_pad_get_name(pad));

pads=g_list_next(pads);}22.6.2衬垫与元件的管理机制相似之处为可动态地设置或读取衬垫的名称,这是通过调用gst_pad_set_name()和gst_pad_get_name()函数来完成的。衬垫有两种类型,分别为输入衬垫和输出衬垫。输入衬垫只能接收数据,输出衬垫只能产生数据。gst_pad_get_direction()函数可以获得指定衬垫的类型。衬垫是GstElement对象的一部分,因此所有衬垫均在元件内存在。gst_pad_get_parent()可通过衬垫查询元件的名称,该函数的返回值为包含查询衬垫的GstElement对象指针。衬垫是元件业务功能的名片,经常被用来判断需要调入那种元件处理对应的媒体信息。GStreamer框架提供了名为_GstCaps的数据结构,该结构用于通过衬垫描述元件所具有的能力。struct_GstCaps{

gchar*name;

//名称

guint16id;

//类型ID

guintrefcount;

//数量

GstProps*properties;

//性能属性

GstCaps*next;

//指向下一个类型的指针};22.6.2衬垫以mad元件为例,其描述如下:Pads:

SINKtemplate:'sink'

Availability:Always

Capabilities:

'mad_sink':

MIMEtype:'audio/mp3':

SRCtemplate:'src'

Availability:Always

Capabilities:

'mad_src':

MIMEtype:'audio/raw':

format:String:int

endianness:Integer:1234

width:Integer:16

depth:Integer:16

channels:Integerrange:1-2

law:Integer:0

signed:Boolean:TRUE

rate:Integerrange:11025-4800022.6.2衬垫在mad元件中有两个衬垫,分别是sink和src。sink衬垫为mad元件的输入衬垫,其描述信息显示该衬垫能够接收的媒体类型为audio/mp3,即MP3音频媒体。src衬垫是该元件的输出衬垫,用于输出类型为audio/raw的音频数据,操作系统能识别该数据并控制声卡播放。其中还具有format、endianness等多种属性,用于描述输出数据流的具体格式。GStreamer框架中每个衬垫都包含多种能力的描述信息,对于判断元件的功能帮助极大。如果需要查询该衬垫的信息,可通过gst_pad_get_caps()函数。下列代码将输出名为pad的衬垫的媒体类型信息和数据格式。GstCaps*caps;caps=gst_pad_get_caps(pad);g_print("衬垫名称为:%s\n",gst_pad_get_name(pad));

//获得衬垫名称while(caps){

g_print("能力描述名称为:%s,MIME类型为:%s\n",

gst_caps_get_name(cap),

//获得能力描述名称

gst_caps_get_mime(cap));

//获得MIME类型名称

caps=caps->next;

//遍历链表中所有的结构}22.6.3箱柜箱柜(bin)是GStreamer框架比较大的一种集合容器,常被用来装入其它元件。因其自身也属于GstElement对象,所以有运行多层嵌套现象的发生。箱柜常用来将多个元件合并为一个逻辑元件,这是组件更为复杂的管道的方法之一。箱柜对于GStreamer框架还有重要意义,它会尝试对数据流进行优化。箱柜的典型结构如图所示。22.6.3箱柜GstPipeline管道是最常用的顶级容器,因此顶层箱柜必须是管道。管道外的箱柜是没有执行能力的。在使用GstThread线程机制进行视频和音频同时处理时,一般会用到箱柜来进行。工厂函数gst_element_factory_make()和箱柜创建函数gst_pipeline_new()都可用来创建箱柜对象。它们的一般形式为:GstElement*thread,*pipeline;thread=gst_element_factory_make("thread",NULL);//创建线程对象,同时为其指定唯一的名称pipeline=gst_pipeline_new("pipeline_name");//根据给出的名称,创建一个特定的管道对象22.6.3箱柜gst_bin_add()函数将已创建的元件添加到已创建的箱柜中:GstElement*element;GstElement*bin;bin=gst_bin_new("bin_name");

//创建一个新箱柜element=gst_element_factory_make("mpg123","decoder");

//创建新元件gst_bin_add(GST_BIN(bin),element);

//将元件装入箱柜gst_bin_get_by_name()函数可用于查询箱柜指定的元件,如下列代码所示:GstElement*element;element=gst_bin_get_by_name(GST_BIN(bin),"decoder");//获得名为decoder的元件由于箱柜直接可以嵌套,可通过gst_bin_get_by_name()函数以递归方式查找内部的箱柜。另外,如果需要将元件从箱柜中移除,可使用gst_bin_remove()函数实现。源代码如下:GstElement*element;gst_bin_remove(GST_BIN(bin),element);

//将元件从箱柜移出22.6.4精灵衬垫对于箱柜对象来说,并没有属于其自身的衬垫,因此无法与其它元件进行数据传递。GStreamer框架为箱柜定义了精灵衬垫对象,这种衬垫能自动为箱柜添加输入端和输出端,如图所示。22.6.4精灵衬垫在为箱柜添加精灵衬垫后,就可以将该箱柜作为普通元件来处理。所有元件和GstElement对象操作均对添加精灵衬垫后的箱柜有效。下列代码将演示为箱柜添加精灵衬垫的方法。GstElement*bin;GstElement*element;element=gst_element_factory_create("mad","decoder");bin=gst_bin_new("bin_name");gst_bin_add(GST_BIN(bin),element);gst_element_add_ghost_pad(bin,gst_element_get_pad(element,"sink"),"sink");

//添加精灵衬垫有了箱柜和精灵衬垫后,GStreamer框架的数据处理流程已十分清晰。多媒体信息通过衬垫读入到数据源元件,数据源再将数据传送到过滤器进行加工,最后过滤器将加工后的数据传送到接收器。整个处理过程是数据流的状态,因为这3个部分的所有元件都将同时处理,组成一个数据管道。22.6.4精灵衬垫最后的工作是将这些元件通过各自的衬垫连接起来,这样一个完整的数据流管道就能进行实际的处理工作了。下列代码将演示如何连接两个元件以及在不需要使用时断开元件间的连接。源代码如下:GstPad*srcpad,*sinkpad;srcpad=gst_element_get_pad(element1,"src");sinpad=gst_element_get_pad(element2,"sink");gst_pad_link(srcpad,sinkpad);

//连接元件gst_pad_unlink(srcpad,sinkpad);

//断开元件在处理音频时,可能每个元件最多只有一个输入端和一个输出端。可通过gst_element_link()函数直接建立这样的元件间的连接,或者通过gst_element_unlink()函数在适当的时候断开元件间的连接。源代码如下:gst_element_link(element1,element2);

//连接元件gst_element_unlink(element1,element2);

//断开元件22.6.4精灵衬垫整条GStreamer管道建立完毕后,所有元件都能处理流动中的数据。为了便于控制,GStreamer框架为元件定义了4种状态。NULL默认状态:元件刚被建立。READY就绪状态:元件已能够工作。PAUSED暂停状态:元件数据处理暂时停止。PLAYING播放状态:元件正在进行数据处理。所有的元件创建时都处于NULL状态,然后在NULL、READY、PAUSED、PLAYING等状态间转换。控制元件状态可通过gst_element_set_state()函数实现,如下列代码所示:GstElement*bin;gst_element_set_state(bin,GST_STATE_PLAYING);

//将箱柜设于播放状态NULL状态是元件的初始状态,对于管道来说,该状态下管道并没有建立好,所以NULL状态不可直接转换为PLAYING状态。当播放结束后,元件将再次回到NULL状态,因此相关的对象并没有被删除,而管道处于READY状态。22.6.4精灵衬垫READY状态是就绪状态。对于数据源元件,READY状态表示该元件已获得了相关资源。对于管道对象,真正的数据流处理是首次进入READY状态开始的,这时管道应已建立成功。进入PLAYING状态后,所有的元件都将处理数据。如果切换到PAUSED状态,数据的处理将暂停。只要管道中有一个元件为PAUSED状态,管道也将变成PAUSED状态,直到该元件切换回PLAYING状态,播放会重新开始。随着GNOME桌面环境的不断普及,GStreamer作为一个强大的多媒体应用开发框架,已经开始受到越来越多人的关注。Gstreamer在设计时采用了非常灵活的体系结构,并且提供了许多预定义的媒体处理模块,因此能够极大简化在Linux下开发多媒体应用的难度。安装GStreamer可在官方站点下载源代码编译,地址为/,或在终端输入下列命令:apt-getinstallgstreamer0.10GStreamer的头文件路径位于“/usr/include/gstreamer-<版本号>/gst”目录中。编译使用GStreamer函数库的程序需要加入指令“'pkg-config--cflags--libsgstreamer-base-<版本号>'”。22.7搭建GTK+开发环境GTK+是在一系列的函数库基础上建立的,所以安装GTK+函数库前,首先要将其依赖的函数库安装。如果使用DEB或者YUM软件包管理器安装,依赖包会自动被安装,并自动检查版本之间的关联性。DEB安装指令如下:apt-getinstallgnome-core-devel//安装libgtk2.0-dev、libglib2.0-dev等开发相关的函数库文件apt-getinstalldevhelp

//安装开发帮助文档查看程序apt-getinstalllibglib2.0-doclibgtk2.0-doc//安装GTK/GLib的API参考手册及其它帮助文档apt-getinstalllibgtk2.0* //安装GTK+2所需的所有文件YUM安装指令如下:yuminstalldevhelpyuminstallgtk2-devel

//安装GTK+2所需的开发文件22.7搭建GTK+开发环境如果所选择的Linux发布版没有软件包管理器,那么可以通过下载源代码编译的方式安装GTK+开发环境。安装时注意顺序和函

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论