Chinaunix首页 | 论坛 | 博客
  • 博客访问: 198892
  • 博文数量: 60
  • 博客积分: 3269
  • 博客等级: 中校
  • 技术积分: 648
  • 用 户 组: 普通用户
  • 注册时间: 2005-09-21 10:48
文章存档

2012年(6)

2011年(6)

2010年(30)

2009年(8)

2007年(6)

2005年(4)

我的朋友

分类: C/C++

2010-11-29 01:32:14

创建GTK新类型Widget原理笔记
 
    有笔记的习惯真好,不过翻笔记真麻烦,整理一下贴出来共同讨论了。
 
    假设将要生成的Widget名称是lgxxx。
 
头文件:

#ifndef __GTKLGXXX_H__
#define __GTKLGXXX_H__
 
#ifdef __cplusplus
  extern "C" {
#endif
 
#include
 
#define GTK_LGXXX(obj)  GTK_CHECK_CAST(obj, gtk_lgxxx_get_type (), GtkLgxxx)
#define GTK_LGXXX_CLASS(klass) GTK_CHECK_CLASS_CAST(klass, gtk_lgxxx_get_type (), GtkLgxxxClass)
#define GTK_IS_LGXXX(obj) GTK_CHECK_TYPE(obj, gtk_lgxxx_get_type ())
 
typedef struct _GtkLgxxx GtkLgxxx;
typedef struct _GtkLgxxxClass GtkLgxxxClass;
 
struct _GtkLgxxx
{
  GtkWidget  parent;
  // user define members
};
 
struct _GtkLgxxxClass
{
  GtkWidgetClass  parent_class;
};
 
guint  gtk_lgxxx_get_type         (void);
GtkWidget* gtk_lgxxx_new          (void);
 
#ifdef __cplusplus
  }
#endif
 
#endif /* __GTKLGXXX_H__ */

主要的函数:
guint  gtk_lgxxx_get_type         (void);
GtkWidget* gtk_lgxxx_new          (void);
 
static GtkLgxxx'parentType *parent_class = NULL;
    如果了解GTK编程的话,应该很熟悉gtk_lgxxx_new(),那就先看看GTK是如何New出一个Widget实体的。

GtkWidget * gtk_lgxxx_new(void)
{
  GtkWidget *lgxxx;
  lgxxx = GTK_WIDGET( gtk_type_new ( gtk_lgxxx_get_type()));
  // user do something.
  return lgxxx;
}
 
    关键的一步是gtk_lgxxx_get_type(),这个函数是必须要自己写的,各Widget的代码基本类似,可复制使用。
    它实现登记lgxxx类型并获得一个实体的ID,过程如下:

guint gtk_lgxxx_get_type (void)
{
  // 每种“类型”只会被创建一次,因此这里定义static形式的变量
  // 这一变量在此就是赋值为0,如果未被创建,在本函数中会进行赋值
  static guint gtk_lgxxx_type = 0;
  if (!gtk_lgxxx_type)
    {
      // 如果该“类型”仍没有被创建,那么这里创建
      // 注意:GtkTypeInfo变量定义也是static的,因为每种“类型”只会对应一份GtkTypeInfo

      static const GtkTypeInfo gtk_lgxxx_info =
      {
        "GtkLgxxx",
        sizeof (GtkLgxxx),
        sizeof (GtkLgxxxClass),
        (GtkClassInitFunc) gtk_lgxxx_class_init,
        (GtkObjectInitFunc) gtk_lgxxx_init,
        /* reserved_1 */ NULL,
        /* reserved_2 */ NULL,
        (GtkClassInitFunc) NULL,
      };
      // 注意下面这句中的gtk_lgxxx'parent_get_type (),需要你明确lgxxx的parent是何类型,比如lgxxx'parent=box。
      // 这里利用了一种递归的方法来实现lgxxx类型的所有父类型都依次会被创建加载
      gtk_lgxxx_type = gtk_type_unique (gtk_lgxxx'parent_get_type (), >k_lgxxx_info);
    }
  return gtk_lgxxx_type;
}
 
    核心是gtk_type_unique ()

GtkType gtk_type_unique (GtkType parent_type, const GtkTypeInfo *type_info)
{
  GtkType new_type;
  gchar *type_name;
 
  g_return_val_if_fail (type_info != NULL, 0);
  g_return_val_if_fail (type_info->type_name != NULL, 0);
 
  if (!parent_type && n_ftype_nodes >= GTK_TYPE_FUNDAMENTAL_MAX)
    {
      g_warning ("gtk_type_unique(): maximum amount of fundamental types reached, "
   "try increasing GTK_TYPE_FUNDAMENTAL_MAX");
      return 0;
    }
  type_name = g_strdup (type_info->type_name);
 
  /* relookup pointers afterwards.*/
  // 关键一步: 动态生成一份_GtkTypeNode数据,并计算“类ID”,也即GtkType
  new_type = gtk_type_create (parent_type, type_name, type_info);
 
  if (!new_type)
    g_free (type_name);
  return new_type;
}
 
/* 动态生成一份_GtkTypeNode数据,加入到系统的“类继承树”中,并计算“类ID”返回(也即GtkType) */
static GtkType gtk_type_create (GtkType parent_type, gchar *type_name, const GtkTypeInfo *type_info)
{
  GtkTypeNode *new_node;
  GtkTypeNode *parent;
  guint i;
 
  if (g_hash_table_lookup (type_name_2_type_ht, type_name))
    {
      g_warning ("gtk_type_create(): type `%s' already exists.", type_name);
      return 0;
    }
 
  if (parent_type)
    {
      GtkTypeNode *tmp_node;
     
      LOOKUP_TYPE_NODE (tmp_node, parent_type);
      if (!tmp_node)
      {
        g_warning ("gtk_type_create(): unknown parent type `%u'.", parent_type);
        return 0;
      }
    }
 
  /* relookup pointers afterwards.*/
  new_node = gtk_type_node_next_and_invalidate (parent_type);
 
  if (parent_type)
    {
      g_assert (GTK_TYPE_SEQNO (new_node->type) > GTK_TYPE_FUNDAMENTAL_MAX);
      LOOKUP_TYPE_NODE (parent, parent_type);
    }
  else
    {
      g_assert (new_node->type <= GTK_TYPE_FUNDAMENTAL_MAX);
      parent = NULL;
    }
 
  /* 这里对新“类型”的结点(GtkTypeNode)进行赋值,重点注意几个字段 */
 
  // _GtkTypeInfo数据
  new_node->type_info = *type_info; 
  new_node->type_info.type_name = type_name;
  /* new_node->type_info.reserved_1 = NULL; */
  new_node->type_info.reserved_2 = NULL;
 
  // 全部父类的数量
  new_node->n_supers = parent ? parent->n_supers + 1 : 0;
  new_node->chunk_alloc_locked = FALSE;
 
  // 全部父“类”的ID,一个GtkType[]数组
  new_node->supers = g_new0 (GtkType, new_node->n_supers + 1);
 
  // 直系的父“类”的ID,通过它,所有的“类”串联起来形成一个链表
  new_node->parent_type = parent_type;
 
  // 这里指向从_GtkObjectClass派生出的“类”结构的klass字段暂时置为NULL
  new_node->klass = NULL;
  new_node->children_types = NULL;
  new_node->mem_chunk = NULL;
 
  if (parent)
    parent->children_types = g_list_append (parent->children_types, GUINT_TO_POINTER (new_node->type));
 
  parent = new_node;
  for (i = 0; i < new_node->n_supers + 1; i++)
    {
      new_node->supers[i] = parent->type;
      LOOKUP_TYPE_NODE (parent, parent->parent_type);
    }
 
  g_hash_table_insert (type_name_2_type_ht, new_node->type_info.type_name, GUINT_TO_POINTER (new_node->type));
 
  return new_node->type;
}
 
    GTK使用自己的一套管理Widgets的方法,每一种运行态的类实体形成管理结构,然后New出的widget实体加入到类节点下链表中,再通过HASH表
管理。
    现在应该明白guint gtk_lgxxx_get_type (void)的作用了吧,当然不仅如此。在这里用大段的代码讲述主要是为了明确gtk_lgxxx_get_type()函数的作用和重要性。
 
    distroy这个实体比较简单,distroy函数在下面讲述的gtk_lgxxx_class_init函数中将被赋值到distroy回调函数指针。

static void gtk_lgxxx_destroy (GtkObject *object)
{
  // user do something.
  parent_class->destroy(object);  // parent_class是静态的全局变量
}
 
    在gtk_lgxxx_get_type (void)中设定gtk_lgxxx_info时设置了两个init函数:
    static void gtk_lgxxx_class_init(GtkLgxxxClass *klass)
    static void gtk_lgxxx_init(GtkLgxxx *lgxxx)
 
    gtk_lgxxx_class_init(GtkLgxxxClass *klass)一般实现以下工作:
    1、为全局变量parent_class赋值,为了方便吧。
    2、登记新类型定义的信号。
    3、登记新类型定义的属性。
    4、将我们写好的那些GTK widget常见的可迭代调用的回调函数登记到相应的指针。
 
static void gtk_lgxxx_class_init (GtkLgxxxClass *klass)
{
  // 在其他GTK widget代码此函数中经常看到一串的这种定义,直到// ========
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;
  object_class = (GtkObjectClass*) klass;
  widget_class = (GtkWidgetClass*) klass;
  // lgxxx新类型是由其父类继承过来的,GTK的类结构还是很有特点的,很好的结构用法.
  // 对于其每一层父类都可以通过类似方式来进行对他们的成员操作。
  // ====================================
 
  // destroy函数赋值就是一个好例子,注意他是赋值于object_class的
  object_class->destroy = gtk_lgxxx_destroy;
 
  // 赋值parent_class,我知道有两种方法,更倾向第二种
  // parent_class = gtk_type_class (gtk_lgxxx'parent_get_type());
  parent_class = g_type_class_peek_parent (klass);
 
  /* 信号和属性登记是另一个问题 */
 
  // 如果有以下代码就写上
  widget_class->realize = gtk_lgxxx_realize;
  widget_class->size_request = gtk_lgxxx_size_request;
  // ... ...
 
  // user do something.
}
 
static void gtk_lgxxx_init(GtkLgxxx *lgxxx)
{
  // 对struct _GtkLgxxx中的成员进行初始化。
}
 
    到此应该对头文件中两个struct定义也有个清晰地认识了。
    其实在前人的基础上自己写一些简单的Widget还是很容易的。
 
 
 
 
阅读(880) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-11-30 14:51:35

很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com