GTK接口定义和实现
一、接口定义
GObject接口如何工作的理论在“非实例化类类型:接口”一节中给出。 本节介绍如何定义和实现一个接口。
第一步是取一个正确的名称。 该接口定义了两种方法:
/* * Copyright/Licensing information. */ #ifndef __VIEWER_EDITABLE_H__ #define __VIEWER_EDITABLE_H__ #include <glib-object.h> G_BEGIN_DECLS #define VIEWER_TYPE_EDITABLE viewer_editable_get_type () G_DECLARE_INTERFACE (ViewerEditable, viewer_editable, VIEWER, EDITABLE, GObject) struct _ViewerEditableInterface { GTypeInterface parent_iface; void (*save) (ViewerEditable *self, GError **error); void (*undo) (ViewerEditable *self, guint n_steps); void (*redo) (ViewerEditable *self, guint n_steps); }; void viewer_editable_save (ViewerEditable *self, GError **error); void viewer_editable_undo (ViewerEditable *self, guint n_steps); void viewer_editable_redo (ViewerEditable *self, guint n_steps); G_END_DECLS #endif /* __VIEWER_EDITABLE_H__ */
该代码与继承自GObject的普通GType的代码相同,除了几个细节:
1、_GET_CLASS函数称为_GET_IFACE(由G_DECLARE_INTERFACE定义)。
2、实例类型ViewerEditable没有被完全定义:它仅被用作一个抽象类型,它表示实现该接口的任何对象的一个实例。
3、ViewerEditableInterface的父级是GTypeInterface,而不是GObjectClass。
ViewerEditable类型本身的实现是微不足道的:
1、G_DEFINE_INTERFACE创建一个viewer_editable_get_type函数,该函数在类型系统中注册类型。第三个参数用于定义一个先决条件接口(稍后我们再讨论一下)。当接口没有先决条件时,只需为此参数传递0。
2、viewer_editable_default_init预计会注册接口的信号,如果有的话(稍后会看到如何使用它们)。
3、接口方法viewer_editable_save,viewer_editable_undo和viewer_editable_redo取消引用接口结构以访问其关联的接口函数并调用它。
G_DEFINE_INTERFACE (ViewerEditable, viewer_editable, G_TYPE_OBJECT); static void viewer_editable_default_init (ViewerEditableInterface *iface) { /* add properties and signals to the interface here */ } void viewer_editable_save (ViewerEditable *self, GError **error) { ViewerEditableInterface *iface; g_return_if_fail (VIEWER_IS_EDITABLE (self)); g_return_if_fail (error == NULL || *error == NULL); iface = VIEWER_EDITABLE_GET_IFACE (self); g_return_if_fail (iface->save != NULL); iface->save (self, error); } void viewer_editable_undo (ViewerEditable *self, guint n_steps) { ViewerEditableInterface *iface; g_return_if_fail (VIEWER_IS_EDITABLE (self)); iface = VIEWER_EDITABLE_GET_IFACE (self); g_return_if_fail (iface->undo != NULL); iface->undo (self, n_steps); } void viewer_editable_redo (ViewerEditable *self, guint n_steps) { ViewerEditableInterface *iface; g_return_if_fail (VIEWER_IS_EDITABLE (self)); iface = VIEWER_EDITABLE_GET_IFACE (self); g_return_if_fail (iface->redo != NULL); iface->redo (self, n_steps); }
二、实现
一旦界面被定义,实现它是相当微不足道的。
第一步是定义一个正常的最终GObject类,正如在“Boilerplate标题代码”一节中所述。
第二步是通过使用G_DEFINE_TYPE_WITH_CODE和G_IMPLEMENT_INTERFACE而不是G_DEFINE_TYPE定义来实现ViewerFile:
static void viewer_file_editable_interface_init (ViewerEditableInterface *iface); G_DEFINE_TYPE_WITH_CODE (ViewerFile, viewer_file, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (VIEWER_TYPE_EDITABLE, viewer_file_editable_interface_init))
类可以通过在对G_DEFINE_TYPE_WITH_CODE的调用中使用多次调用G_IMPLEMENT_INTERFACE来实现多个接口。
viewer_file_editable_interface_init,接口初始化函数:它里面的接口的每个虚拟方法必须分配给它的实现
static void viewer_file_editable_save (ViewerFile *self, GError **error) { g_print ("File implementation of editable interface save method: %s.\n", self->filename); } static void viewer_file_editable_undo (ViewerFile *self, guint n_steps) { g_print ("File implementation of editable interface undo method: %s.\n", self->filename); } static void viewer_file_editable_redo (ViewerFile *self, guint n_steps) { g_print ("File implementation of editable interface redo method: %s.\n", self->filename); } static void viewer_file_editable_interface_init (ViewerEditableInterface *iface) { iface->save = viewer_file_editable_save; iface->undo = viewer_file_editable_undo; iface->redo = viewer_file_editable_redo; } static void viewer_file_init (ViewerFile *self) { /* Instance variable initialisation code. */ }