概述
http://blog.csdn.net/cuijpus/article/details/4420033
GDK是gtk的下面一层的东西,平常都用GTK的函数,很少直接调用GDK的函数。但是GTK里面的事件、信号、回调、绘制等都和GDK有直接的关系,所以需要把GDK的绘图与事件相关的内容再理一理。
我原本打算自己去分析的,baidu了几下后,我放弃了,有分析的很好的文章有助于理解GDK的事件和绘图,下面几个是不错的分析,你们可以点击链接去看。后面我也放了几个汇总,方便些。第5部分的网友分析的非常不错。
1. GDK基础:
http://hi.baidu.com/phoenix20080808/blog/item/d2515519ec73f64043a9ada6.html
2. 图形上下文
http://blog.csdn.net/hahapro/archive/2007/07/25/1708132.aspx
3. GDK 事件类型
http://www.huihoo.org/gnu/gtk2-tut/a2711.html
4. GTK的消息流说明(X Window做后端的情况)
http://blog.csdn.net/absurd/archive/2006/03/08/619170.aspx
5. Gtk事件与信号关系
http://blog.csdn.net/c_spark/archive/2009/05/06/4155958.aspx
1. GDK基础
1 颜色表和颜色 1.1 颜色表 在X窗口系统中,像素值代表在一个颜色查找表中的入口,这是为了使系统能够具有多种显示模式(8位,16位)。例如:考虑一种八位的显示模式:八位不足以做为显示中的颜色编码,只能为很少部分的RGB值编码,显示时,系统将像素值用作索引,从颜色表中找出其对应的RGB值来显示像素。这个颜色表称为colormap。有时,我们可以修改颜色表以包含要使用的颜色(一些colormap是只读的,不能被修改)。在Gdk中,用GdkColormap来表示一个颜色表
1.2 颜色值 GDK使用GdkColor存储RGB值和像素值。红、绿、蓝值是以 1 6位无符号整数给出的,取值范围为0到65535GdkColor的定义如下:
在用一种颜色绘画时,必须保证:
例题:使用指定的颜色绘画的步骤
2 可绘区和pixmap 2.1 定义
2.2 操作
2.3 GdkPixmap的使用 GtkWidget* parent; 3 鼠标指针
在程序中改变鼠标指针的方法 |
2. 图形上下文:
一个图形上下文,或者GC(GraphicsContext),是一套在绘图时要用到的参数(比如颜色、剪裁屏蔽值、字体等等)。它是一种服务器端资 源,就像pixmap和窗口一样。GC减少了Gdk绘图函数的参数个数,也减少了每个绘图请求从客户到服务器间传递的参数的数目。
与GdkWindowAttr类似,图形上下文可以用GdkGCValues结构创建。结构中包含了图形上下文中所有的特性,还可以传递 gdk_gc_new_with_values()标志指出哪一个成员是有效的。其他的成员保留其缺省值。还可以用gdk_gc_new()函数(这种方 法通常更容易)创建一个全为缺省值的GC。创建GC之后,还有一些函数用来改变GC的设置,但是要记住,每次改变GC设置值都需要一条消息传递到X服务 器。
所有的GC都是不可以互换的,它们都与特定的深度和视件相关联。GC的深度和视 件必须与要绘图的可绘区的深度和视件相匹配。GC的深度和视件是从传递到gdk_gc_new()函数的GdkWindow*参数中获得的,所以处理这种 问题的最容易的方法就是在要绘图的窗口上创建GC。
下面是GdkGCValues结构的定义:
typedefstruct_GdkGCValuesGdkGCValues; struct_GdkGCValues { GdkColorforeground; GdkColorbackground; GdkFont*font; GdkFunctionfunction; GdkFillfill; GdkPixmap*tile; GdkPixmap*stipple; GdkPixmap*clip_mask; GdkSubwindowModesubwindow_mode; gintts_x_origin; gintts_y_origin; gintclip_x_origin; gintclip_y_origin; gintgraphics_exposures; gintline_width; GdkLineStyleline_style; GdkCapStylecap_style; GdkJoinStylejoin_style; };
前景色(foreground成员)是画线、圆或其他形状时的“画笔颜色”。背景色(background成员)的用处依赖于特定的绘画操作。这些颜色必须是用gdk_color_alloc()函数在当前颜色表中分配的。
font成员没有用到:在Xlib中绘制文本时用它指定字体。在Gdk以前的版本中,它也有同样的作用;但是新的绘制文本的Gdk程序都要求一个 GdkFont*参数。一个Xlib图形上下文只能存储无格式的字体,但是GdkFont能够代表一个字体集(用以绘制一些外语文字)。
function成员指定要画的像素点与可绘区上已有的像素点如何结合起来。有许多种可能取值,但是只有两种是最常用的:
GDK_COPY:缺省值。它忽略已存在的像素点(只是将新的像素点画在上面)。
GDK_XOR:将旧的和新的像素点一种可反转的方式结合起来。也就是,如果执行两次GDK_OR操作,头一次绘图就会被第二次操作取消。GDK_XOR通常用于“擦除”,可以恢复可绘区原来内容。
GdkGCValues的fill成员决定如何使用GdkGCValues中的tile和stipple成员。其中tile成员是一个与目的可绘区深度 相同的pixmap图片,它被反复复制到目的可绘区,将它们拼贴起来—第一次拼贴的原点是(ts_x_origin,ts_y_origin)。而 stipple成员是一个位图(深度为1的pixmap);它也是从(ts_x_origin,ts_y_origin)开始拼贴的。下面是fill的可 能取值:
GDK_SOLID:忽略tile和stipple成员。绘图形状是用前景色和背景色绘制的。
GDK_TILED:绘图形状用tile成员指定的pixmap图片绘制,而不是用前景色和背景色。用GDK_TILED模式绘画会擦除可绘区上的任何内容,显示由tile成员指定的图片的拼贴图形。
GDK_STIPPLED:用stipple中定义的位绘制图形。也就是,在stipple成员中未设置的位不会绘出。
GDK_OPAQUE_STIPPLED:用前景色绘制在stipple中设置的位,没有在stipple中设置的位用背景色绘制。 有些X服务器并没有有效实现上面所有的fill模式值,所以使用时可能会很慢。clip_mask成员是可选的,它是一个位图,只有在这个位图中设置了的 位才会画出。从clip_mask到可绘区的映射是由clip_x_origin和clip_y_origin值决定的,这些定义了与clip_mask 中(0,0)对应的可绘区的坐标。也可以设置一个剪裁矩形(最常用的、也是最有用的形式)或一个剪裁区域(区域就是在屏幕上的任意范围,典型情况是一个多 边形或矩形列表)。
用gdk_gc_set_clip_rectangle()设置剪裁矩形:
GdkRectangleclip_rect; clip_rect.x=10; clip_rect.y=20; clip_rect.width=200; clip_rect.height=100; gdk_gc_set_clip_rectangle(gc,&clip_rect);
要关闭“剪裁”,将剪裁的矩形、区域或剪裁屏蔽值设置为NULL。GC的subwindow_mode只与可绘区是否为一个窗口有关。缺省设置是 GDK_CLIP_BY_CHILDREN;这意指子窗口不会被在父窗口上的绘图影响。这会造成一个假象:子窗口在父窗口的“上面”,并且子窗口是不透明 的。GDK_INCLUDE_INFERIORS在所有在“上面”的 子窗口上绘制,改写子窗口上包含的任何图形—通常不使用这种模式。如果确实在使用GDK_INCLUDE_INFERIORS模式,可能要使用GDK_XOR作为绘图函数,因为它允许恢复子窗口原先的内容。
graphics_exposures是一个布尔值,缺省是TRUE,它决定gdk_window_copy_area()是否产生expose事件。 GC的最后四个值决定怎样画线。这些值用于画线,包括未填充多边形的边框以及弧线。line_width域决定线的宽度(以像素计)。宽度为0的线称为一 条“细线”,细线是一个像素宽的线,绘制得非常快(通常使用硬件加速),但是画的具体像素依赖于所使用的X服务器。为了一致性,最好使用宽度为1的线。
line_style域可以是下面三种值:
GDK_LINE_SOLID是缺省值;一条实线。
GDK_LINE_ON_OFF_DASH用前景色画一条虚线,将虚线的off(关闭)部分空着。
GDK_LINE_DOUBLE_DASH用前景色画一条虚线,但是虚线的off(关闭)部分用背景色绘制。虚线是gdk_gc_set_dashes()指定的;GdkGCValues中并不包括这个域。gdk_gc_set_dashes()需要三个参数:
dash_list是一个虚线长度的数组。偶数号的长度是“on”(打开)部分,它们是用前景色绘制的;奇数号的长度是“off”(关闭)部分,它们不画出,或者用背景色绘制,具体绘制方法依赖于line_style。长度值不能是0,所有的值必须是正数。
dash_offset是在虚线列表中第一个像素的索引号。也就是,如果在dash_list中指定了5个on和5个off,并且dash_offset是3,绘制的线将从第3个on虚线开始。
N是在dash_list中的元素的个数。可以设置一个古怪的虚线模式,例如:
gchardash_list[]={5,5,3,3,1,1,3,3}; gdk_gc_set_dashes(gc,0,dash_list,sizeof(dash_list));
缺省的dash_list是{4,4},偏移量是0。图16-1显示了一些用GDK_LINE_DOUBLE_DASH画的虚线。图形上下文的前景色是 黑色,背景色是亮灰色。头5根线是缺省的{4,4}虚线模式,偏移量分别是0、1、2、3和4。记住,缺省值是0。图16-2显示了这5根线的放大图。最 后的一根线就是上面提到的古怪虚线模式,它的放大图显示在图16-3。
cap_style决定X怎样画线的端点(或虚线端点)。它有4种可能取值:
GDK_CAP_BUTT:是缺省值,它意味着线的端点是正方形的。
GDK_CAP_NOT_LAST:对应一个像素宽度的线,最后一个像素忽略不画。其他与GDK_CAP_BUTT一样。
GDK_CAP_ROUND:在线的端点画一个小弧线,由线的端点向两边延伸。弧线的中心是线的端点,半径是线宽的一半。对一个像素宽的线,它没有什么效果(因为没有办法画一个像素宽的弧线)。
GDK_CAP_PROJECTING:将线延伸,越过它的终点半个线宽。它对一个像素的线没有效果。
join_style参数影响画多边形或在一个函数中画多条线时,各线之间如何连接。如果把线想象成一个细长的矩形,就很容易弄清楚线之间并不是平滑 连接的。在连接的两个端点之间有一个凹槽。对这个凹槽有三种处理方法,也就是join_style的三种可能取值:
GDK_JOIN_MITER:是缺省值,在线交叉的地方画一个尖角。
GDK_JOIN_ROUND:在交叉的凹槽处画一个弧线,画一个圆形的转角。
GDK_JOIN_BEVEL:用最小的可能的形状填充凹槽,画一个平坦的转角。函数列表:GdkGC
#include<gdk/gdk.h> GdkGC*gdk_gc_new(GdkWindow*window) GdkGC*gdk_gc_new_with_values(GdkWindow*window, GdkGCValues*values, GdkGCValuesMaskvalues_mask) voidgdk_gc_set_dashes(GdkGC*gc, gintdash_offset, gchardash_list, gintn) voidgdk_gc_unref(GdkGC*gc)
|
5. Gtk事件与信号关系
--分析中相关的源码包 gtk+2.0-2.12.9
Gtk所提供的工具库与Gtk应用程序与都是基于事件触发机制来管理。所有的Gtk应用程序都是基于事件驱动,如果没有事件发生,应用程序将处于等待状态,不会执行任何操作,一旦事件发生,将根据不同的事件做出相应的处理。在GTK+中,一个事件就是从X Server传出来的一个信息。当一个事件发生时,Gtk程序就会通过发送一个信号来通知应用程序执行相关的操作,即调用指定控件与这一信号进行绑定的回调函数,来完成一次由事件所触发的行动。
一、通过对源代码分析,解决理解过程中所产生的问题:
1、Gtk应用程序如何能接收到来至XServer端的事件:
根据以前的分析可知,GDK层所提供的事件循环对XServer端传过来的事件进行管理,并将其转化为GDK层的事件,通过对具体源代码的分析,可以知道Gtk应用程序能接收的事件,便是来自GDK层进行加工过的Gdk事件。
在程序中可以得知,当有事件需要处理时,Gdk层对XServer端的事件以队列的形式进行管理(_gdk_events_queue),并将X事件通过(gdk_event_translate)转换为GDK事件,将转换后的事件放入队列中,每次从队列中取出队首的事件(_gdk_event_unqueue),如果事件不为空,这时程序中的处理是将GDK事件GdkEvent交给了_gdk_event_func函数进行处理,到这我们可以知道,由XServer端产生的事件经过GDK层后将事件由一个函数_gdk_event_func来处理。
进一步分析知道,_gdk_event_func这一函数指针做为一个GDK提供给外部的一个接口,外部程序如果实现这一接口,便可以接收来自GDK层转换XServer端产生的事件,在对Gtk的分析过程中,该接口是由Gtk来实现(gtk_main_do_event),也就是Gtk程序通过这一函数来处理事件。
当程序在调用gtk_init时对_gdk_event_func进行设置。
2、Gtk应用程序接收到事件后如何对事件进行处理:
在进一步的分析中,Gtk应用程序采用了信号的机制,通过信号的方式,通知Gtk其它的控件做出相应的动作。每一个控件都有自己定义的信号,每一个信号都可以绑定到一个指定的回调函数上。
从gtk_main_do_event函数中,通过调用内部函数(如:gtk_widget_event_internal)将GDK事件关联到Gtk自身定义的信号上,并将信号发出,绑定这一信号的控件便可以接收到,这样根据信号的定义执行相应的回调函数。
二、以对GtkButton控件的点击事件(press-release-click)为例,进行分析:
当鼠标点击在GtkButton上时,系统做出了以下的反应:
由于XServer实时接收着来至输入设备的操作,当鼠标点击后,XServer立即生成一个XEvent结构,里面描述着所点击的控件,XEvent产生的事件类型等具体信息,这时将这一事件传给GDK层,GDK层通过将XEvent事件转化为GdkEvent,将转化后的事件交给函数接口(_gdk_event_func)处理,即gtk_main_do_event。经过几层函数调用的处理,在函数gtk_widget_event中调用gtk_widget_event_internal,将事件转化成为Gtk中的信号类型,并通过g_signal_emit将GtkButton的信号BUTTON_PRESS_EVENT发送出去。
在gtkwindow.c文件中,对GtkWidgetClass创建了BUTTON_PRESS_EVENT、BUTTON_RELEASE_EVENT等类型信号,BUTTON_PRESS_EVENT与 函数button_press_event关联,GtkButton类型继承了父类GtkWidget的信号,并将函数button_press_event进行重载为gtk_button_button_press。在GtkButton结构中定义自己的信号"PRESSED",关联函数为gtk_real_button_pressed。
在GtkButton中定义了与单击按钮时关联的信号类型为: PRESSED,RELEASED,CLICKED。
通过分析源码,信号的流程是在gtk_widget_event_internal函数中所发出的信号由GtkWidget所接收(所有widget的父类,如GtkButton), 再由GtkWidget中对这一信号所关联的回调函数(gtk_button_button_press),来发出由GtkWidget子类所定义的信号,最终的操作在于GtkWidget子类中信号的定义。即最终信号的发出是gtk_button_pressed调用g_signal_emit将“PRESSED”信号发出,调用回调函数gtk_real_button_pressed修改GtkButton的属性。
以同样的方式,处理鼠标对按钮的释放(release)事件,当RELEASED信号发出后,调用与RELEASED信号相关联的回调函数,根据当前GtkButton的属性进行判断如果当前按钮满足一定的条件,如当前按钮是否处于按下(down)状态等,则由gtk_button_clicked,发出一个"CLICKED"信号,调用与"CLICKED"关联的回调函数执行click事件的操作。
三、参考资料
http://library.gnome.org/devel/gtk-tutorial/stable/ No.Starch.Press.The.Official.GNOME.2.Developers.Guide.eBook-LiB.pdf
|
借花献佛,我做个总结:
1. 程序初始化时,放个钩子下去,钓鱼
2. 有按键后,由Xserver/或者DirectFB Master层传到GDK层
3. GDK层把X/DFB事件转换为GDK事件
4. GTK层把GDK事件转换为Gtk信号,并发出去
5. 然后GObject会调用Gtk信号对应的回调函数,完成对事件的响应
6. 在回调函数里面,有可能做些界面更新的操作
最后
以上就是现代唇膏为你收集整理的GDK事件与GTK signal的前世今生的全部内容,希望文章能够帮你解决GDK事件与GTK signal的前世今生所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复