GTK、Gobject 入门

8 篇文章 0 订阅

GTK入门

environment

To use gtk2 or gtk3 apps you don’t need to install anything. But, if you want to develop (or even just compile) apps this is what you’re looking for:
sudo apt-get install libgtk-3-dev

check

  1. dpkg -l libgtk* | grep -e '^i' | grep -e 'libgtk-*[0-9]'
    to list all the libgtk packages, including -dev ones, that are on your system. dpkg -l will list all the packages that dpkg knows about, including ones that aren’t currently installed, so I’ve used grep to list only ones that are installed (line starts with i).
  2. The follow 3 cmd will print your gtk version if installed in your device
    pkg-config --modversion gtk+
    pkg-config --modversion gtk+-2.0
    pkg-config --modversion gtk+-3.0
  3. run&build c file
    version.c
    #include <gtk/gtk.h>
    #include <glib/gprintf.h>
    
    int main(int argc, char *argv[])
    {
        /* Initialize GTK */
        gtk_init (&argc, &argv);
    
        g_printf("%d.%d.%d\n", gtk_major_version, gtk_minor_version, gtk_micro_version);//相关测试项目
        return(0);
    }   
    
    build cmd
    gcc -o version version.c `pkg-config --cflags --libs gtk+-3.0`
    run as usual./version,the version number will show below

develop in vsc

  1. Add include
    open your gtk object,the vsc may warning *.h no such file or directory
    just open C/C++ Configurations or open .vscode/c_cpp_properties.json,let it be follow

    {
    "configurations": [
        {
            "name": "Linux",
            "includePath": [
                "${workspaceFolder}/**",
                "/usr/lib/**",
                "/usr/include/**"
            ],
            "defines": [],
            "compilerPath": "/usr/bin/gcc",
            "cStandard": "gnu17",
            "cppStandard": "gnu++14",
            "intelliSenseMode": "linux-gcc-x64"
            }
        ],
        "version": 4
    }
    

    the includePath will auto check the revelent file to your project.

  2. debug of gtk in vsc
    因为编译使用了第三方库,所以在scod中创建debug的tasks.json 修改build args
    tasks.json

 "args": [
             "-g",
             "${file}",
             "-o",
             "${fileDirname}/${fileBasenameNoExtension}",
             "`",
             "pkg-config",
             "--cflags",
             "--libs",
             "gtk+-3.0",
             "`"
         ],

实际就是把build cmd 写在里面而已。

gobject

hello world.c

  #include <gtk/gtk.h>

    // callback function which is called when button is clicked
    static void on_button_clicked(GtkButton *btn, gpointer data) {
        // change button label when it's clicked
        gtk_button_set_label(btn, "Hello World");
    }
	
    // callback function which is called when application is first started
    static void on_app_activate(GApplication *app, gpointer data) {
        // create a new application window for the application
        // GtkApplication is sub-class of GApplication
        // downcast GApplication* to GtkApplication* with GTK_APPLICATION() macro
        GtkWidget *window = gtk_application_window_new(GTK_APPLICATION(app));
        // a simple push button
        GtkWidget *btn = gtk_button_new_with_label("Click Me!");
        // connect the event-handler for "clicked" signal of button
        g_signal_connect(btn, "clicked", G_CALLBACK(on_button_clicked), NULL);
        // add the button to the window
        gtk_container_add(GTK_CONTAINER(window), btn);
        // display the window
        gtk_widget_show_all(GTK_WIDGET(window));
    }

    int main(int argc, char *argv[]) {
        // create new GtkApplication with an unique application ID
        GtkApplication *app = gtk_application_new(
            "org.gtkmm.example.HelloApp", 
            G_APPLICATION_FLAGS_NONE
        );
        // connect the event-handler for "activate" signal of GApplication
        // G_CALLBACK() macro is used to cast the callback function pointer
        // to generic void pointer
        g_signal_connect(app, "activate", G_CALLBACK(on_app_activate), NULL);
        // start the application, terminate by closing the window
        // GtkApplication* is upcast to GApplication* with G_APPLICATION() macro
        int status = g_application_run(G_APPLICATION(app), argc, argv);
        // deallocate the application object
        g_object_unref(app);
        return status;
    }

这串代码只是实现了简单的gtk、gobject GUI开发的内容
[C] “Hello World” in Gtk+
build cmd gcc -o official helloworld.c `pkg-config --cflags --libs gtk+-3.0

gtk面向对象

gtk 开发gui软件貌似挺流行,但对我不是重点,显示这类的,桌面有.net就够了,游戏 unity等的引擎也足够优秀,需要学的是如何渲染,如果只是学怎么调用api,再熟练也成长不了,毕竟永远是在别人的基础上做开发,却从未去考虑基础到底是什么。anyway,闲话到此。
gtk 的gui开发,可以参考gtk学习总结:GTK从入门到放弃,三天包教包会

面向对象都熟悉,但c没有底层实现,所以有了gobject
有几个示例讲得非常好,这里引用
稍微修改了一下,增加一个设置prop的例子
boy.c

#include "boy.h"
/**用LAST_SIGNAL来表示最后一个信号(不用实现的信号)是一种非常良好的编程风格**/
enum { BOY_BORN, LAST_SIGNAL };
enum{
PROP_0,//must
PROP_B,
};
static gint boy_signals[LAST_SIGNAL] = { 0 };
static void boy_cry (void);
static void boy_born(void);
//static void boy_init(Boy *boy);
//static void boy_class_init(BoyClass *boyclass);
 
G_DEFINE_TYPE(Boy, boy, G_TYPE_OBJECT);
 
/**g_type_register_static函数用来注册对象的类型,它的第一个参数是表示此对象的父类的对象类型,我们这里是G_TYPE_OBJECT,这个宏用来表示GObject的父类;
 * 第二个参数表示此对象的名称,这里为"Boy";
 * 第三个参数是此对象的GTypeInfo结构型指针,这里赋值为&boyinfo;
 * 第四个参数是对象注册成功后返回此对象的整型ID标识。 
 * **/

//static void boy_init(Boy *boy);
//static void boy_class_init(BoyClass *boyclass);
 
//GType boy_get_type(void)
//{
//    static GType boy_type = 0;
//    if(!boy_type) {
//        static const GTypeInfo boy_info = {
//            sizeof(BoyClass),
//            NULL,NULL,
//            (GClassInitFunc)boy_class_init,
//            NULL,NULL,
//            sizeof(Boy),
//            0,
//            (GInstanceInitFunc)boy_init
//        };
//        boy_type = g_type_register_static(G_TYPE_OBJECT,"Boy",&boy_info,0);
//    }
//    return boy_type;
//}

/**
 * set property revelent
 * 
 */
void my_child_set_prop(GObject      *object,
                   guint         prop_id,
                   const GValue *value,
                   GParamSpec   *pspec){
	Boy *self=BOY(object);
	BoyPrivate *priv=BOY_GET_PRIVATE(self);
	g_print("priv addr b %p\n",priv);
	switch(prop_id){
	case PROP_B:{
		priv->prop_text=g_value_get_int(value);
		break;
	}

	default:{
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
	}
	}
}
void my_child_get_prop(GObject    *object,
                   guint       prop_id,
                   GValue     *value,
                   GParamSpec *pspec){
	Boy *self=BOY(object);
	BoyPrivate *priv=BOY_GET_PRIVATE(self);
	switch(prop_id){
	case PROP_B:{
		g_value_set_int(value,priv->prop_text);
		break;
	}
	default:{
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
	}
	}
}


/**boy_init和boy_class_init,它们分别用来初始化实例结构和类结构。它们并不被在代码中明显调用,
 * 关键是将其用宏转换为地址指针,然后赋值到GTypeInfo结构中,然后由GType系统自行处理,
 * 同时将它们定义为静态的也是非常必要的。 **/
static void boy_init(Boy *boy)
{
    boy->age = 0;
    boy->name = "none";
    boy->cry = boy_cry;
    BoyPrivate *d;
    d = boy->priv = BOY_GET_PRIVATE(boy);
    memset(d, 0, sizeof(*d));
    d->weight = 100;
}
static void boy_class_init(BoyClass *boyclass)
{
    boyclass->boy_born = boy_born;
    /**Boy对象定义了一个信号BOY_BORN,在对象创建时发出,表示Boy对象诞生。
    *同时还需要定义静态的整型指针数组来保存信号的标识
    **/
    boy_signals[BOY_BORN] = g_signal_new("boy_born",
                BOY_TYPE,
                G_SIGNAL_RUN_FIRST,
                G_STRUCT_OFFSET(BoyClass,boy_born),
                NULL,NULL,
                g_cclosure_marshal_VOID__VOID,
                G_TYPE_NONE, 0, NULL);
    g_type_class_add_private(boyclass, sizeof(BoyPrivate));

    GObjectClass *base_class = G_OBJECT_CLASS (boyclass);
    base_class->set_property=my_child_set_prop;
    base_class->get_property=my_child_get_prop;

    g_object_class_install_property (base_class, PROP_B,
                                     g_param_spec_int ("b",
                                                        "B",
                                                        "The B",
                                                        -1,
                                                        G_MAXINT,
                                                        -1,
                                                        G_PARAM_READWRITE));

}



Boy *boy_new(void)
{
    Boy *boy;
    boy = g_object_new(BOY_TYPE, NULL);
    //g_signal_emit向指定义对象的实例发射信号
    g_signal_emit(boy, boy_signals[BOY_BORN], 0);
    return boy;
}
int boy_get_age(Boy *boy)
{
    return boy->age;
}
void boy_set_age(Boy *boy, int age)
{
    boy->age = age;
}
char *boy_get_name(Boy *boy)
{
    return boy->name;
}
void boy_set_name(Boy *boy, char *name)
{
    boy->name = name;
}
Boy*  boy_new_with_name(gchar *name)
{
    Boy *boy;
    boy = boy_new();
    boy_set_name(boy, name);
    return boy;
}
Boy*  boy_new_with_age(gint age)
{
    Boy* boy;
    boy = boy_new();
    boy_set_age(boy, age);
    return boy;
}
Boy *boy_new_with_name_and_age(gchar *name, gint age)
{
    Boy *boy;
    boy = boy_new();
    boy_set_name(boy,name);
    boy_set_age(boy,age);
    return boy;
}
static void boy_cry (void)
{
    g_print("---The Boy is crying......\n");
}
static void boy_born(void)
{
    g_print("---Message : A boy was born.\n");
}
void boy_info(Boy *boy)
{
    g_print("---The Boy name is %s\n", boy->name);
    g_print("---The Boy age is %d\n", boy->age);
    g_print("---The Boy weight is %d\n", boy->priv->weight);
}

原作者在示例中写了两个boy.c,第一份讲明了初始化所必要的两个函数boy_init()&boy_class_init()并通过boy_get_type()和头文件,让创建new boy 的时候可以去初始化。之后作者同时提供了一个官方宏初始化的方式G_DEFINE_TYPE(Boy, boy, G_TYPE_OBJECT),一开始我是不理解的,后来尝试修改第二参数,build fail后才了解,这个宏,会自动将参数二构造成方式一需要的函数结构,可以理解为在编译前,宏展开的效果和方式一实际是一样的,使用宏去构造要快得多,也算是gobjec的语法糖了。
boy.h

#ifndef _BOY_H_
#define _BOY_H_
#include <glib-object.h>
/**BOY_TYPE宏封装了boy_get_type函数,可以直接取得并替代Boy对象的ID标识;
 * BOY(obj)宏是G_TYPE_CHECK_INSTANCE_CAST宏的再一次封装,目的是将一个Gobject对象强制转换为Boy对象
**/
typedef struct _Boy Boy;
typedef struct _BoyClass BoyClass;
typedef struct _BoyPrivate BoyPrivate;
 
#define BOY_TYPE (boy_get_type())
#define BOY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), BOY_TYPE, Boy))
#define BOY_GET_PRIVATE(obj)  (G_TYPE_INSTANCE_GET_PRIVATE((obj), BOY_TYPE, BoyPrivate))
 
/**结构类型_Boy是Boy对象的实例,就是说我们每创建一个Boy对象,也就同时创建了一个Boy结构。
 * Boy对象中的parent表示此对象的父类,GObject系统中所有对象的共同的根都是GObject类,所以这是必须的;
 * 其它的成员可以是公共的,这里包括表示年龄的age,表示名字的name和表示方法的函数指针cry,
 * 外部代码可以操作或引用它们。
**/
struct _Boy
{
    GObject parent;
    BoyPrivate *priv;
    gint age;
    gchar *name;
    void (*cry)(void);
};
/**结构类型_BoyClass是Boy对象的类结构,它是所有Boy对象实例所共有的。
 * BoyClass中的parent_class是GObjectClass,同GObject是所有对象的共有的根一样,GObejctClass是所有对象的类结构的根。
 * 在BoyClass中我们还定义了一个函数指针boy_born,也就是说这一函数指针也是所有Boy对象实例共有的,
 * 所有的Boy实例都可以调用它;同样,如果需要的话,你也可以在类结构中定义其它数据成员。
**/
struct _BoyClass
{
    GObjectClass parent_class;
    void (*boy_born)(void);
};
 
struct _BoyPrivate
{
    gint weight;
    gint prop_text;
};
/**其余的函数定义包括三种:一种是取得Boy对象的类型ID的函数boy_get_type,这是必须有的;
 * 另一种是创建Boy对象实例的函数boy_new和boy_new_with_*,这是非常清晰明了的创建对象的方式,当然你也可以用g_object_new函数来创建对象;
 * 第三种是设定或取得Boy对象属性成员的值的函数boy_get_*和boy_set_*。
 * 正常情况下这三种函数都是一个对象所必需的,另外一个函数boy_info用来显示此对象的当前状态**/
GType boy_get_type(void);
Boy *boy_new(void);
gint boy_get_age(Boy *boy);
void boy_set_age(Boy *boy, int age);
char *boy_get_name(Boy *boy);
void boy_set_name(Boy *boy, char *name);
Boy *boy_new_with_name(gchar *name);
Boy * boy_new_with_age(gint age);
Boy * boy_new_with_name_and_age(gchar *name, gint age);
void  boy_info(Boy *boy);
#endif // _BOY_H_

man.c

   #include "man.h"
    static void man_bye(void);
    static void man_init(Man *man);
    static void man_class_init(Man *man);
    /**关键在于定义对象时将父对象实例定义为Boy,父类设定为BoyClass,在注册此对象时将其父对象类型设为BOY_TYPE。
     **/
    GType man_get_type(void)
    {
        static GType man_type = 0;
        if(!man_type) {
            static const GTypeInfo man_info = {
                sizeof(ManClass),
                NULL, NULL,
                (GClassInitFunc)man_class_init,
                NULL, NULL,
                sizeof(Man),
                0,
                (GInstanceInitFunc)man_init
            };
            /**在注册此对象时将其父对象类型设为BOY_TYPE**/
            man_type = g_type_register_static(BOY_TYPE, "Man", &man_info, 0);
        }
        return man_type;
    }
    static void man_init(Man *man)
    {
        man->job = "none";
        man->bye = man_bye;
    }
    static void man_class_init(Man *man)
    {
    }
    Man*  man_new(void)
    {
        Man *man;
        man = g_object_new(MAN_TYPE, 0);
        return man;
    }
    gchar* man_get_job(Man *man)
    {
        return man->job;
    }
    void  man_set_job(Man *man, gchar *job)
    {
        man->job = job;
    }
    Man*  man_new_with_name_age_and_job(gchar *name, gint age, gchar *job)
    {
        Man *man;
        man = man_new();
        boy_set_name(BOY(man), name);
        boy_set_age(BOY(man), age);
        man_set_job(man, job);
        return man;
    }
    static void man_bye(void)
    {
        g_print("+++Goodbye everyone!\n");
    }
    /**在设定对象属性时如用到父对象的属性要强制转换下,如取得对象的name属性,就必须用BOY(obj)->name,
     *因为Man本身没有name属性,而其父对象Boy有,所以用BOY宏将其强制为Boy类型的对象。
    **/
    void man_info(Man *man)
    {
        /**定义对象时将父对象实例定义为Boy,父类设定为BoyClass**/
        g_print("+++the man name is %s\n", BOY(man)->name);
        g_print("+++the man age is %d\n", BOY(man)->age);
        g_print("+++the man job is %s\n", man->job);
    }

man.h

 #ifndef _MAN_H_
    #define _MAN_H_
    /**********Man继承自Boy对象,Man对象在Boy对象的基础上又增加了一个属性job和一个方法bye***********/
    #include "boy.h"
     
    #define MAN_TYPE (man_get_type())
    #define MAN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), MAN_TYPE, Man))
    typedef struct _Man Man;
    typedef struct _ManClass ManClass;
    struct _Man
    {
        Boy parent;
        gchar *job;
        void (*bye)(void);
    };
    struct _ManClass
    {
        BoyClass parent_class;
    };
     
    GType man_get_type();
    Man *man_new(void);
    gchar *man_get_job(Man *man);
    void man_set_job(Man *man, gchar *job);
    Man *man_new_with_name_age_and_job(gchar *name, gint age, gchar *job);
    void man_info(Man *man);
     
    #endif // _MAN_H_

main.c

 #include <glib.h>
    #include "boy.h"
    #include "man.h"
     
    int main(int argc, char *argv[])
    {
        Boy *tom, *peter,*rick;
        Man *green, *brown;
        // g_type_init();//注意,初始化类型系统,必需
        g_print("**********************\n");
        tom = boy_new_with_name("Tom");
        tom->cry();
        boy_info(tom);
        g_print("**********************\n");
        rick=boy_new_with_name_and_age("Rick",24);
        GValue value = {0};
        g_value_init(&value,G_TYPE_INT);
        g_value_set_int(&value, 400);
        g_object_set_property(G_OBJECT(rick), "b", &value);
        g_value_unset (&value);

        GValue val = {0};
        gint get_val=0;
        g_value_init(&val,G_TYPE_INT); 
        // g_value_init(&get_val,G_TYPE_INT);
        g_object_get_property(G_OBJECT(rick),"b",&val);
        int b=g_value_get_int(&val);
        g_value_unset (&val);
        g_print("get property (%d)\n",b);
        g_object_get(G_OBJECT(rick),"b",&get_val,NULL);
        // int c=g_value_get_int(get_val);
        g_print("get property by get fun(%d)\n",get_val);
        // g_value_unset (&get_val);

        g_print("**********************\n");
        peter = boy_new_with_name_and_age("Peter", 10);
        peter->cry();
        boy_info(peter);
        g_print("**********************\n");
        g_print("######################\n");
        green = man_new();
        /**设定Man对象的name属性用到其父对象Boy的方法**/
        boy_set_name(BOY(green), "Green");
        boy_set_age(BOY(green), 28);
        man_set_job(green, "Doctor");
        man_info(green);
        green->bye();
        g_print("######################\n");
        brown = man_new_with_name_age_and_job("Brown", 30, "Teacher");
        man_info(brown);
        brown->bye();
        g_print("######################\n");

        return TRUE;
    }

由于主要是为了了解gobject里对象属性(property)的使用,在代码示例(boy)中添加了相关函数,并在main中写入和读取测试。
首先需要构建一个枚举

enum{
PROP_0,//must
PROP_B,
};

无定义枚举相当于define,值默认从0开始增加,也就是PROP_0=0,PROP_B=1
然后在对象的class 初始化函数中,注册和定义相关的get、set prop函数

static void boy_class_init(BoyClass *boyclass)
{
    boyclass->boy_born = boy_born;
    /**Boy对象定义了一个信号BOY_BORN,在对象创建时发出,表示Boy对象诞生。
    *同时还需要定义静态的整型指针数组来保存信号的标识
    **/
    boy_signals[BOY_BORN] = g_signal_new("boy_born",
                BOY_TYPE,
                G_SIGNAL_RUN_FIRST,
                G_STRUCT_OFFSET(BoyClass,boy_born),
                NULL,NULL,
                g_cclosure_marshal_VOID__VOID,
                G_TYPE_NONE, 0, NULL);
    g_type_class_add_private(boyclass, sizeof(BoyPrivate));

    GObjectClass *base_class = G_OBJECT_CLASS (boyclass);
    base_class->set_property=my_child_set_prop;
    base_class->get_property=my_child_get_prop;

    g_object_class_install_property (base_class, PROP_B,
                                     g_param_spec_int ("b",
                                                        "B",
                                                        "The B",
                                                        -1,
                                                        G_MAXINT,
                                                        -1,
                                                        G_PARAM_READWRITE));
}

初学时我一直不理解为什么要定义枚举去注册g_object_class_install_property(),转念一想,应该是内部会把g_param_spec_int()的结果以排序的方式去定义(1->n)。get/set prop 的定义倒是好理解,按枚举量去做switch。
在main中,用两种方式做get prop

GValue val = {0};
gint get_val=0;
g_value_init(&val,G_TYPE_INT); 
g_object_get_property(G_OBJECT(rick),"b",&val);
int b=g_value_get_int(&val);
g_value_unset (&val);
g_print("[CGX]get property (%d)\n",b);
g_object_get(G_OBJECT(rick),"b",&get_val,NULL);
g_print("[CGX]get property by get fun(%d)\n",get_val);

主要是两种g_object_get()/g_object_get_property(),两者除了取值后的赋值对象不同之外没有其他差异。
从上面可以理解设计原理:get(“b”)->找到对应的枚举值,由于是枚举,大小不会重复->通过枚举量做switch->返回对应的内部数据。
这样就可以将private等特殊量通过这种方式暴露出来。也可以在get、set上做设定,判断是否可以取,或者做对应的处理。
makefile

CC = gcc
gobject_v=gtk+-3.0
all:
	# $(CC) -c boy.c `pkg-config --cflags glib-2.0 gobject-2.0`
	# $(CC) -c man.c `pkg-config --cflags glib-2.0 gobject-2.0`
	# $(CC) -c main.c `pkg-config --cflags glib-2.0 gobject-2.0`
	# $(CC) -o simple boy.o man.o main.o `pkg-config --libs glib-2.0 gobject-2.0`

	$(CC) -c boy.c `pkg-config --cflags $(gobject_v)`
	$(CC) -c man.c `pkg-config --cflags $(gobject_v)`
	$(CC) -c main.c `pkg-config --cflags $(gobject_v)`
	$(CC) -o simple boy.o man.o main.o `pkg-config --libs $(gobject_v)`
	sudo chmod 777 simple
clean:
	rm *.o
	rm simple

原作者用的是v2,我改成v3,顺手加上make clean

总结:如果只是看面向对象,原作者的boy->man的结构已经讲得很清楚了。注意创建对象时构造g_type_register_static()使用parent的类型,定义类时也不要忘了使用parent的定义BoyClass、Boy。

上面提供的代码,在v3上可以直接run。看懂结构问题应该不大。比较不好理解的还是有的,比如gobject后为什么还要GObjectClass。
看看两者的内部定义
gobject

/**
 * GObject:
 * 
 * All the fields in the GObject structure are private 
 * to the #GObject implementation and should never be accessed directly.
 */
struct  _GObject
{
  GTypeInstance  g_type_instance;
  
  /*< private >*/
  volatile guint ref_count;
  GData         *qdata;
};

gobjectclass

/**
 * GObjectClass:
 * @g_type_class: the parent class
 * @constructor: the @constructor function is called by g_object_new () to 
 *  complete the object initialization after all the construction properties are
 *  set. The first thing a @constructor implementation must do is chain up to the
 *  @constructor of the parent class. Overriding @constructor should be rarely 
 *  needed, e.g. to handle construct properties, or to implement singletons.
 * @set_property: the generic setter for all properties of this type. Should be
 *  overridden for every type with properties. If implementations of
 *  @set_property don't emit property change notification explicitly, this will
 *  be done implicitly by the type system. However, if the notify signal is
 *  emitted explicitly, the type system will not emit it a second time.
 * @get_property: the generic getter for all properties of this type. Should be
 *  overridden for every type with properties.
 * @dispose: the @dispose function is supposed to drop all references to other 
 *  objects, but keep the instance otherwise intact, so that client method 
 *  invocations still work. It may be run multiple times (due to reference 
 *  loops). Before returning, @dispose should chain up to the @dispose method 
 *  of the parent class.
 * @finalize: instance finalization function, should finish the finalization of 
 *  the instance begun in @dispose and chain up to the @finalize method of the 
 *  parent class.
 * @dispatch_properties_changed: emits property change notification for a bunch
 *  of properties. Overriding @dispatch_properties_changed should be rarely 
 *  needed.
 * @notify: the class closure for the notify signal
 * @constructed: the @constructed function is called by g_object_new() as the
 *  final step of the object creation process.  At the point of the call, all
 *  construction properties have been set on the object.  The purpose of this
 *  call is to allow for object initialisation steps that can only be performed
 *  after construction properties have been set.  @constructed implementors
 *  should chain up to the @constructed call of their parent class to allow it
 *  to complete its initialisation.
 * 
 * The class structure for the GObject type.
 * 
 * |[<!-- language="C" -->
 * // Example of implementing a singleton using a constructor.
 * static MySingleton *the_singleton = NULL;
 * 
 * static GObject*
 * my_singleton_constructor (GType                  type,
 *                           guint                  n_construct_params,
 *                           GObjectConstructParam *construct_params)
 * {
 *   GObject *object;
 *   
 *   if (!the_singleton)
 *     {
 *       object = G_OBJECT_CLASS (parent_class)->constructor (type,
 *                                                            n_construct_params,
 *                                                            construct_params);
 *       the_singleton = MY_SINGLETON (object);
 *     }
 *   else
 *     object = g_object_ref (G_OBJECT (the_singleton));
 * 
 *   return object;
 * }
 * ]|
 */
struct  _GObjectClass
{
  GTypeClass   g_type_class;

  /*< private >*/
  GSList      *construct_properties;

  /*< public >*/
  /* seldom overidden */
  GObject*   (*constructor)     (GType                  type,
                                 guint                  n_construct_properties,
                                 GObjectConstructParam *construct_properties);
  /* overridable methods */
  void       (*set_property)		(GObject        *object,
                                         guint           property_id,
                                         const GValue   *value,
                                         GParamSpec     *pspec);
  void       (*get_property)		(GObject        *object,
                                         guint           property_id,
                                         GValue         *value,
                                         GParamSpec     *pspec);
  void       (*dispose)			(GObject        *object);
  void       (*finalize)		(GObject        *object);
  /* seldom overidden */
  void       (*dispatch_properties_changed) (GObject      *object,
					     guint	   n_pspecs,
					     GParamSpec  **pspecs);
  /* signals */
  void	     (*notify)			(GObject	*object,
					 GParamSpec	*pspec);

  /* called when done constructing */
  void	     (*constructed)		(GObject	*object);

  /*< private >*/
  gsize		flags;

  /* padding */
  gpointer	pdummy[6];
};

个人认为gobject用于构建对象可继承的结构,包括但不限于类型和方法。而gobjectclass则用于构建属于对象自己的内部信息通信,包括信号量,属性,事件等的功能都在class里实现,没有定义就默认没有,比如get、set prop和boy的boy_born 函数,在run上面的示例时,可以发现man对象是不会印boy_born,man class里仅仅是声明了parent类型,在class init内也是空的。与之对比,我尝试在man中调用boy(gobject)的cry方法。发现ok

BOY(green)->cry();

与使用Boy(object)的其他类似(age,name)一样,只要把子类转为父类后就可以直接调用非private的结构、函数方法。
另一个比较热门的讨论是https://stackoverflow.com/questions/14840456/purpose-of-gvalue-gtypevaluetable-gtypeinfo-and-gparamspec
感兴趣可以看看

接口查询
官方api查询
Gtk – 3.0
GObject Reference Manual

可以的参考文章
GObject官方详细文档(译)
快速上手Gobject

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值