GTK Widget中内部结构与工作流程

移动开发
GTK Widget中内部结构与工作流程是本文要介绍的内容,主要是来了解并学习GTK Widget的流程,具体内容的实现来看本文详解。

GTK Widget中内部结构与工作流程是本文要介绍的内容,主要是来了解并学习GTK Widget的流程,具体内容的实现来看本文详解。

Gtk Widget的基本结构是这样的:

  1. typedef struct {  
  2. GtkStyle *GSEAL (style);  
  3. GtkRequisition GSEAL (requisition);  
  4. GtkAllocation GSEAL (allocation);  
  5. GdkWindow *GSEAL (window);  
  6. GtkWidget *GSEAL (parent);  
  7. } GtkWidget 

;

其中最重要的是它的window属性,每个GtkWidget都必须有一个window。Widget是围绕着window转的,只有有了window,Widget的存在才有意义。

要注意这里的window是一个GdkWindow,而不是GtkWindow。GdkWindow是对X的window的封装,大致上是屏幕上的一块矩形区域,可以在上面画画,可以接收事件。

一个Widget从创建、显示到销毁,大致要经过这么几个过程:

1、创建(new)

这是调用gtk_xxx_new时所触发的。它干的活很简单,用gobject的对象系统创建一个相应widget的实例。
当创建实例时,gobject会自动调用指定的初始化(init)函数(在get_type时指定),init函数负责把widget的各字段都初始化(把标题文字什么的设为NULL之类的)。

注意此时window并没有被创建,其实只是有了个widget的架子而已。

创建之后就可以对widget进行各种属性的设置了。

2、实例化(realize)

实例化的过程,就是将window创建出来的过程。这其中包括几个阶段:

询问大小请求(size_request)

GTK在实例化一个widget之前,会询问这个widget希望的大小是多大。widget可以根据自己的情况(例如属性什么的),计算出自己所需要的大小,也可以返回一个默认值,反正就是widget自己定啦:)。

分配大小(size_allocate):

GTK获得大小请求后就会给widget分配一个大小。要注意的是分配的大小不一定和请求的大小相同。一般来说,在分配大小时widget需要做几件事。

将分配的大小记录在自己的allocation中。

如果自己的window已经创建了,那么要改变自己所拥有的window的大小,使之符合所分配的大小。

如果widget是一个容器(container),那么对其所有的子widget也要相应地计算它们的大小并重新给它们分配大小。

分配大小可能发生在实例化之前,也可能在实例化后因为所属容器的大小、位置发生变化而被重新分配,因此widget的window可能已经被创建,也可能是NULL,需要进行判断。

实例化

这才是真正的实例化阶段。实例化所需要做的事只有一个:用gdk_window_new创建window。创建好window后需要用 GTK_WIDGET_SET_FLAGS来给widget设置GTK_REALIZED标志。设置之后用GTK_REALIZED宏检查widget是 否已经被实例化时会返回TRUE,表示该widget已经被实例化了。
可以用gtk_widget_realize手动实例化一个widget

3、映射(map)

所谓映射,就是将已经创建好的window映射(显示)到屏幕上。需要做的事是用gdk_window_show将window给显示出来。和实例化时类似,需要用GTK_WIDGET_SET_FLAGS设置GTK_MAPPED标志,表示已经映射好了。

要注意的是map时需要判断widget是否已经实例化(用GTK_REALIZED),如果没有,应该首先实例化widget,这样才能显示window。
同样可以用gtk_widget_map手动映射一个widget。

用gtk_widget_show来显示一个widget的本质,就是将widget实例化,并将其映射。当然每一步都要判断是否已经做过,重复实例化和映射会造成资源泄漏(window被多次创建)和其他问题。

以上就是一个widget从创建到显示的过程。当然其中还有其父widget的流程。一个widget当且仅当其父widget被实例化后才能实例化,映射亦然(放心,这个流程是GTK+自动判断的)

接下来就是销毁一个widget时要做的事了。

4、反映射(unmap)

当隐藏一个widget时,其实就是取消这个widget的映射。具体做法是用gtk_window_hide来隐藏window,并用GTK_WIDGET_UNSET_FLAGS来取消(GTK_MAPPED)。

5、反实例化(unrealize)

销毁一个widget之前会自动要求将其反实例化。反实例化就是将window给销毁(记得把window指针设回NULL),并取消(GTK_REALIZED)标志。
有时可能会需要用gtk_widget_unrealize来手动反实例化一个widget。

6、销毁(destroy)

和new对应,把剩下的资源释放,最后用gobject的相应函数释放整个widget

下面是取自GtkEntry中的典型代码:

创建:

  1. GtkWidget*  
  2. gtk_entry_new (void)  
  3. {  
  4. /* 返回类型为GTK_TYPE_ENTRY的对象(Gobject的工作) */  
  5. return g_object_new (GTK_TYPE_ENTRY, NULL);  
  6. }  
  7.  
  8. /* 初始化函数,在g_object_new时自动调用 */  
  9. static void  
  10. gtk_entry_init (GtkEntry *entry)  
  11. {  
  12. GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry);  
  13. /* 设置widget标识 */  
  14. GTK_WIDGET_SET_FLAGS (entry, GTK_CAN_FOCUS);  
  15. /* 初始化各字段 */  
  16. entry->text_size = MIN_SIZE;  
  17. entry->text = g_malloc (entry->text_size);  
  18. entry->text[0] = '\0';  
  19. /* …… */  
  20. /* 设置拖放 */  
  21. gtk_drag_dest_set (GTK_WIDGET (entry),  
  22. GTK_DEST_DEFAULT_HIGHLIGHT,  
  23. NULL, 0,  
  24. GDK_ACTION_COPY | GDK_ACTION_MOVE);  
  25. gtk_drag_dest_add_text_targets (GTK_WIDGET (entry));  
  26. /* 输入法context */  
  27. entry->im_context = gtk_im_multicontext_new ();  
  28. /* 信号 */  
  29. g_signal_connect (entry->im_context, "commit",  
  30. G_CALLBACK (gtk_entry_commit_cb), entry);  
  31. /* …… */  

大小分配:

  1. static void  
  2. gtk_entry_size_allocate (GtkWidget     *widget,  
  3. GtkAllocation *allocation)  
  4. {  
  5. GtkEntry *entry = GTK_ENTRY (widget);  
  6. /* 保存到allocation中 */   
  7. widget->allocation = *allocation;  
  8. /* 判断是否实例化 */  
  9. if (GTK_WIDGET_REALIZED (widget))  
  10. {  
  11. /* 计算窗口大小…… */  
  12. /* 改变窗口大小 */  
  13. gdk_window_move_resize (widget->window, x, y, width, height);  
  14. /* …… */  
  15. }  

大小请求:

  1. static void  
  2. gtk_entry_size_request (GtkWidget      *widget,  
  3. GtkRequisition *requisition)  
  4. {  
  5. /* 计算所需大小…… */  
  6. /* 设置所城大小 */  
  7. if (entry->width_chars < 0)  
  8. requisition->width = MIN_ENTRY_WIDTH + xborder * 2 + inner_border.left + inner_border.right;  
  9. else  
  10. {  
  11. /* …… */       
  12. requisition->width = char_pixels * entry->width_chars + xborder * 2 + inner_border.left + inner_border.right;  
  13. }  
  14. requisition->height = PANGO_PIXELS (entry->ascent + entry->descent) + yborder * 2 + inner_border.top + inner_border.bottom;  
  15. /* …… */  

实例化:

  1. static void  
  2. gtk_entry_realize (GtkWidget *widget)  
  3. {  
  4. /* …… */  
  5. /* 设置标志 */  
  6. GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);  
  7. /* …… */  
  8. /* 创建window */  
  9. widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);  
  10. gdk_window_set_user_data (widget->window, entry);  
  11. /* …… */  

映射:

  1. static void  
  2. gtk_entry_map (GtkWidget *widget)  
  3. {  
  4. /* …… */  
  5. /* 判断是否可以且需要显示 */  
  6. if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_MAPPED (widget))  
  7. {  
  8. /* 调用父类的map函数,也就是GtkWidget的,这样就不用自己设置GTK_MAPPED和显示widget->window了 */  
  9. GTK_WIDGET_CLASS (gtk_entry_parent_class)->map (widget);  
  10. /* …… */  
  11. /* 显示需要显示的window */  
  12. gdk_window_show (icon_info->window);  
  13. /* …… */  

反映射:

  1. static void  
  2. gtk_entry_unmap (GtkWidget *widget)  
  3. {  
  4. /* …… */  
  5. /* 判断是否需要隐藏 */  
  6. if (GTK_WIDGET_MAPPED (widget))  
  7. {  
  8. /* …… */  
  9. /* 隐藏需要显示的window */  
  10. gdk_window_hide (icon_info->window);  
  11. /* …… */  
  12. /* 调用父类的unmap函数,也就是GtkWidget的,这样就不用自己取消GTK_MAPPED和隐藏widget->window了 */  
  13. GTK_WIDGET_CLASS (gtk_entry_parent_class)->unmap (widget);  
  14. }  

反实例化:

  1. static void  
  2. gtk_entry_unrealize (GtkWidget *widget)  
  3. {  
  4. /* …… */  
  5. /* 调用父类的unrealize函数来销毁widget->window和取消GTK_REALIZED标识 */  
  6. GTK_WIDGET_CLASS (gtk_entry_parent_class)->unrealize (widget);  
  7. /* …… */  

销毁:

  1. static void  
  2. gtk_entry_destroy (GtkObject *object)  
  3. {  
  4. /* 销毁为成员分配的空间…… */  
  5. /* 用父类的object销毁函数自动调用gobject来销毁 */  
  6. GTK_OBJECT_CLASS (gtk_entry_parent_class)->destroy (object);  

小结:GTK Widget中内部结构与工作流程的内容介绍完了,希望通过GTK Widget内容的学习能对你有所帮助!

责任编辑:zhaolei 来源: 博客园
相关推荐

2020-05-12 23:20:50

Tomcat内部结构

2011-09-09 20:02:19

GTK Widget

2009-06-05 10:26:05

struts工作流程

2010-09-27 10:19:09

DHCP工作流程

2022-11-02 15:11:44

LightHouseChrome插件

2011-03-31 10:54:01

Cacti工作流程

2010-07-28 17:19:28

ICMP协议

2010-06-24 16:40:16

Bittorrent协

2010-07-13 16:21:22

FIX协议

2010-06-12 17:44:19

ARP协议

2009-07-27 14:13:15

2011-03-29 09:30:12

Cacti

2011-08-30 13:26:18

Mysql ProxyLua

2010-04-12 15:17:40

dump Oracle

2010-09-25 15:59:54

JVM虚拟机

2011-09-09 19:39:57

GTK Widget

2010-06-23 14:46:54

DHCP协议

2010-08-30 09:07:12

DHCP工作流程

2023-06-05 08:14:17

RabbitMQ兔子MQ开源

2009-08-07 11:10:40

Netbeans ID
点赞
收藏

51CTO技术栈公众号