热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

GTK+Widget的内部结构与工作流程(转)

GtkWidget的基本结构是这样的:typedefstruct{GtkStyle*GSEAL(style);GtkRequisitionGSEAL(requisit

GtkWidget的基本结构是这样的:

typedef struct {GtkStyle *GSEAL (style);GtkRequisition GSEAL (requisition);GtkAllocation GSEAL (allocation);GdkWindow *GSEAL (window);GtkWidget *GSEAL (parent);
} 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需要做几件事。
        1)将分配的大小记录在自己的allocation中。
        2)如果自己的window已经创建了,那么要改变自己所拥有的window的大小,使之符合所分配的大小。
        3)如果widget是一个容器(container),那么对其所有的子widget也要相应地计算它们的大小并重新给它们分配大小。
        4)分配大小可能发生在实例化之前,也可能在实例化后因为所属容器的大小、位置发生变化而被重新分配,因此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中的典型代码:


创建:

GtkWidget* gtk_entry_new (void)
{/* 返回类型为GTK_TYPE_ENTRY的对象(Gobject的工作) */return g_object_new (GTK_TYPE_ENTRY, NULL);
}/* 初始化函数,在g_object_new时自动调用 */
static void gtk_entry_init (GtkEntry *entry)
{GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry);/* 设置widget标识 */GTK_WIDGET_SET_FLAGS (entry, GTK_CAN_FOCUS);/* 初始化各字段 */entry->text_size = MIN_SIZE;entry->text = g_malloc (entry->text_size);entry->text[0] = '\0';/* …… *//* 设置拖放 */gtk_drag_dest_set (GTK_WIDGET (entry),GTK_DEST_DEFAULT_HIGHLIGHT,NULL, 0,GDK_ACTION_COPY | GDK_ACTION_MOVE);gtk_drag_dest_add_text_targets (GTK_WIDGET (entry));/* 输入法context */entry->im_context = gtk_im_multicontext_new ();/* 信号 */g_signal_connect (entry->im_context, "commit",G_CALLBACK (gtk_entry_commit_cb), entry);/* …… */
}


大小分配:

static void gtk_entry_size_allocate (GtkWidget *widget,GtkAllocation *allocation)
{GtkEntry *entry = GTK_ENTRY (widget);/* 保存到allocation中 */ widget->allocation = *allocation;/* 判断是否实例化 */if (GTK_WIDGET_REALIZED (widget)){/* 计算窗口大小…… *//* 改变窗口大小 */gdk_window_move_resize (widget->window, x, y, width, height);/* …… */}
}


大小请求:

static voidgtk_entry_size_request (GtkWidget *widget, GtkRequisition *requisition)
{/* 计算所需大小…… *//* 设置所城大小 */if (entry->width_chars <0)requisition->width &#61; MIN_ENTRY_WIDTH &#43; xborder * 2 &#43; inner_border.left &#43; inner_border.right;else{/* …… */ requisition->width &#61; char_pixels * entry->width_chars &#43; xborder * 2 &#43; inner_border.left &#43; inner_border.right;}requisition->height &#61; PANGO_PIXELS (entry->ascent &#43; entry->descent) &#43; yborder * 2 &#43; inner_border.top &#43; inner_border.bottom;/* …… */
}


实例化&#xff1a;

static void gtk_entry_realize (GtkWidget *widget)
{/* …… *//* 设置标志 */GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);/* …… *//* 创建window */widget->window &#61; gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);gdk_window_set_user_data (widget->window, entry);/* …… */
}


映射&#xff1a;

static void gtk_entry_map (GtkWidget *widget)
{/* …… *//* 判断是否可以且需要显示 */if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_MAPPED (widget)){/* 调用父类的map函数&#xff0c;也就是GtkWidget的&#xff0c;这样就不用自己设置GTK_MAPPED和显示widget->window了 */GTK_WIDGET_CLASS (gtk_entry_parent_class)->map (widget);/* …… *//* 显示需要显示的window */gdk_window_show (icon_info->window);/* …… */}
}


反映射&#xff1a;

static void gtk_entry_unmap (GtkWidget *widget)
{/* …… *//* 判断是否需要隐藏 */if (GTK_WIDGET_MAPPED (widget)){/* …… *//* 隐藏需要显示的window */gdk_window_hide (icon_info->window);/* …… *//* 调用父类的unmap函数&#xff0c;也就是GtkWidget的&#xff0c;这样就不用自己取消GTK_MAPPED和隐藏widget->window了 */GTK_WIDGET_CLASS (gtk_entry_parent_class)->unmap (widget);}
}


反实例化&#xff1a; 

static void gtk_entry_unrealize (GtkWidget *widget)
{/* …… *//* 调用父类的unrealize函数来销毁widget->window和取消GTK_REALIZED标识 */GTK_WIDGET_CLASS (gtk_entry_parent_class)->unrealize (widget);/* …… */
}


销毁&#xff1a;

static voidgtk_entry_destroy (GtkObject *object)
{/* 销毁为成员分配的空间…… *//* 用父类的object销毁函数自动调用gobject来销毁 */GTK_OBJECT_CLASS (gtk_entry_parent_class)->destroy (object);
}



推荐阅读
  • 带添加按钮的GridView,item的删除事件
    先上图片效果;gridView无数据时显示添加按钮,有数据时,第一格显示添加按钮,后面显示数据:布局文件:addr_manage.xml<?xmlve ... [详细]
  • WPF之Binding初探
      初学wpf,经常被Binding搞晕,以下记录写Binding的基础。首先,盗用张图。这图形象的说明了Binding的机理。对于Binding,意思是数据绑定,基本用法是:1、 ... [详细]
  • TerraformVersionTerraformv0.9.11AffectedResource(s)Pleas ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • FeatureRequestIsyourfeaturerequestrelatedtoaproblem?Please ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • 在Oracle11g以前版本中的的DataGuard物理备用数据库,可以以只读的方式打开数据库,但此时MediaRecovery利用日志进行数据同步的过 ... [详细]
  • AFNetwork框架(零)使用NSURLSession进行网络请求
    本文介绍了AFNetwork框架中使用NSURLSession进行网络请求的方法,包括NSURLSession的配置、请求的创建和执行等步骤。同时还介绍了NSURLSessionDelegate和NSURLSessionConfiguration的相关内容。通过本文可以了解到AFNetwork框架中使用NSURLSession进行网络请求的基本流程和注意事项。 ... [详细]
  • Jboss的EJB部署描述符standardjaws.xml配置步骤详解
    本文详细介绍了Jboss的EJB部署描述符standardjaws.xml的配置步骤,包括映射CMP实体EJB、数据源连接池的获取以及数据库配置等内容。 ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了10分钟了解Android的事件分发相关的知识,希望对你有一定的参考价值。什么是事件分发?大家 ... [详细]
  • React提供三种方式创建Refs:字符串Refs(将被废弃)回调函数RefsReact.createRef(从React16.3开始)第一种方式不推荐使用,原因在此,并且可能会在之后的版本移除。classMyComponentextendsReact.Component{constructor(props){sup ... [详细]
author-avatar
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有