GDK事件与GTK signal的前世今生

发布时间:2017-1-19 22:54:21 编辑:www.fx114.net 分享查询网我要评论
本篇文章主要介绍了"GDK事件与GTK signal的前世今生",主要涉及到GDK事件与GTK signal的前世今生方面的内容,对于GDK事件与GTK signal的前世今生感兴趣的同学可以参考一下。

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来表示一个颜色表 Gdk_widget_get_colormap:获取指定构件的颜色表Gdk_colormap_get_system:获取系统的缺省颜色表 1.2 颜色值    GDK使用GdkColor存储RGB值和像素值。红、绿、蓝值是以 1 6位无符号整数给出的,取值范围为0到65535GdkColor的定义如下: struct GdkColor {    gulong pixel; 像素值    gushort red;    gushort green;    gushort blue; }; 在用一种颜色绘画时,必须保证: 保证像素值包含合适的值保证颜色值在要使用的可绘区的颜色表中存在 例题:使用指定的颜色绘画的步骤 GdkColor color; color.red = 65535; color.green = 0; color.blue = 0; if (gdk_colormap_alloc_color(colormap, &color, FALSE, TRUE)) //填充GdkColor的pixel分量  { /* 成功!*/ } 其中,colormap为要绘画的可绘区的颜色表,此后,就可以使用该GtkColor颜色来绘画了 gdk_colormap_alloc_color:根据GdkColor结构中的红、绿、蓝分量的值来查找颜色表,从中找出该红、绿、蓝分量对应的像素值并填充GdkColor的pixel成员。使用完一种颜色后,应该用 gdk_colormap_free_colors()函数将它从颜色表中删除gdk_color_parse:从颜色的名字来获取颜色对应的RGB值并填充指定的GdkColor变量 2 可绘区和pixmap 2.1 定义 pixmap指的是内存中的一块图片缓冲区,可以在里面绘图,然后将该pixmap中的数据复制到显存中,这样做可以快速更新屏幕。因此,pixmap一般用来存储从磁盘加载的图像数据。在Gdk中,使用GdkPixmap来表示一个pixmap位图指的是深度为1的pixmap,在Gdk中,使用GdkBitmap来表示一个位图可绘区指的是可以用来绘图的区域,在Gdk中,使用GdkDrawable来表示一个可绘区。可绘区包括GdkWindow、Gdkpixmap以及GdkBitmap(深度为1的pixmap) 2.2 操作 gdk_pixmap_new(GdkDrawable*drawable,gint width,gint height,gint depth):创建一个pixmap 创建pixmap时,应该使pixmap的色深与它要绘制到的目标窗口的色深相同。 如果在其drawable参数中指定一个窗口,则色深将与指定窗口的色深相同,此时应该将depth参数设置为-1。 如果drawable参数为NULL,则必须在其depth参数中指定色深 ================================================================================================gdk_pixmap_unref:销毁一个pixmapgtk_pixmap_new:使用指定的GdkPixmap来创建构件 2.3 GdkPixmap的使用 GtkWidget* parent; gchar *xpm_filename; GdkPixmap *pixmap; GdkBitmap *mask; GtkStyle *style; 创建GtkPixmap要复制到的目标构件。注意:必须实现该构件才能使用它的window属性 parent=gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_widget_realize(parent); style=gtk_widget_get_style(parent); pixmap=gdk_pixmap_create_from_xpm(parent->window,&mask,&style->bg[GTK_STATE_NORMAL],xpm_filename); 3 鼠标指针 gdk_window_get_pointer用来获取鼠标指针的坐标gdk_pointer_grab函数用来让一个窗口独占指针gdk_pointer_is_grabbed函数用来判断鼠标是否被独占了gdk_pointer_ungrab用来取消对鼠标指针的独占 ========================================================== 在程序中改变鼠标指针的方法 GdkCursor* cursor; cursor = gdk_cursor_new(GDK_CLOCK); gdk_window_set_cursor(window, cursor); gdk_cursor_destroy(cursor); 注意   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. 在回调函数里面,有可能做些界面更新的操作

上一篇:android:tag与android:id的区别
下一篇:用JDOM包实现生成XML文件的简单示例

相关文章

相关评论