创建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还是很容易的。