Gobject C语言库 VI 信号通信

GObject库有一个重要的功能,信号通信。 面向语言的基础功能是没有类似功能的,GUI库才会看到消息通知的功能。
在对象与对象通信的方式中,通常有这么几种:

  • A对象在自己的方法中调用 B对象的方法
    这种方式A和B通过方法紧密的关联着。
  • A 对象在方法里调用一个或多个传递的回调函数,回调函数中包含B或其它对象的信息。
    这种方式虽然有一定隔离性,但是也会受到回调函数个数的限制,并且使用起来不是很直观。
  • A 在执行的方法中发送一个消息给系统,系统根据注册的消息处理器,传递给消息处理器执行。

GObject 正是第三种方法,这样A不需要知道B,它仅需要在需要发消息的地方,将消息传送出去。 消息的处理器通过注册函数来完成,所以也没有个数的限制。使用Gobject的信号通信机制通常包含4个步骤:

  • 注册信号, 一个信号来源于一个对象,信号的注册应该在类的初始化函数中完成。
  • 编写信号处理器。 当信号发生后,一个信号处理器可以处理这个消息。
  • 连接信号和信号处理器。 这样处系统就知道某个信号应该被哪个处理器处理了。
  • 发射信号。 系统会处理信后的接受,并调用相应的一个或多个处理器进行处理。

信号的注册
我们通常使用g_signal_newv(), g_signal_new_valist() or g_signal_new() 三个函数来注册处理器,它们需要在产生信号的类型的类初始化方法中完成:

guint
g_signal_newv (const gchar        *signal_name,   //一个用于唯一标志这个信号的字符串
               GType               itype,                            // 可以产生这个消息的实例类型
               GSignalFlags        signal_flags,           //用于定义各种处理器被执行顺序的标志符
               GClosure           *class_closure,          //default 处理器,一个对象可以提供一个默认处理器,它回合用户定义处理器一起被执行
               GSignalAccumulator  accumulator,    // 所有处理器被激活后调用的函数
               gpointer            accu_data,                // 传给accumulator的数据
               GSignalCMarshaller  c_marshaller,   //  默认的C marshaller
               GType               return_type,             //  信号的返回类型
               guint               n_params,                  // 信号传递的参数的个数
               GType              *param_types);        //  一个Gtype数组,用来描述每一个参数的类型

从上面的定义来看,消息的注册实际就是描述一个消息和消息处理器应该遵从的要求。
我们在VedioMedia里创建一个Signal : FORMAT_CHANGED 通知感兴趣的处理器VedioMedia的媒体格式变更了,现在我们需要做以下的事情:

  1. 定义这个信号: 信号的名称 返回类型 传递的参数及类型
    我们声明一个字符串常量作为信号的名称(#define FORMAT_CHANGED "format_changed");为了简便,我们定义返回类型为void, 传递参数为0.
  2. 在class_init函数中注册这个信号
static guint format_change;

static void
vcam_vediomedia_class_init(VcamVedioMediaClass * kclass) {
    //注册私有属性
    g_type_class_add_private(kclass, sizeof(VcamVedioMediaPriv));

    /* override base object methods */
    GObjectClass* base_class = G_OBJECT_CLASS(kclass);
    base_class->set_property = vcam_vediomedia_set_property;
    base_class->get_property = vcam_vediomedia_get_property;
    /* install properties */
    g_object_class_install_property(base_class, VCAM_MEDIA_FORMAT,
        g_param_spec_object("format", "vedio format", "desscribe the vedio format", VCAM_TYPE_VEDIOMEDIA,G_PARAM_READWRITE));

    format_change = g_signal_new(FORMAT_CHANGED,
                                          G_TYPE_FROM_CLASS(kclass),
                                          G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
                                          0 /* class offset.Subclass cannot override the class handler (default handler). */,
                                          NULL /* accumulator */,
                                          NULL /* accumulator data */,
                                          NULL /* C marshaller. g_cclosure_marshal_generic() will be used */,
                                          G_TYPE_NONE /* return_type */,
                                          0     /* n_params */
                                          );
}

这里我们换用了g_signal_new函数,g_signal_new的第三个函数为默认处理器在类型中的offset, 我们设置为0即可。

发射信号
当我们在VcamVedioMedia类型中注册了一个信号,他的名字是“format_changed”, 标识档期媒体实例的格式信息发生了变化。通常我们希望在发生变化后通知感兴趣的处理器来处理,所以这个消息将在引起格式信息变化的函数里被发送出去:
我们在VcamVedioMediaClass中新定义一个setfmt的函数指针,以及一个帮助方法:

#ifndef VCAM_VEDIOMEDIA_H
#define VCAM_VEDIOMEDIA_H

#include <glib-object.h>

#define VCAM_TYPE_VEDIOMEDIA (vcam_vediomedia_get_type())
#define FORMAT_CHANGED  "format_changed"
G_DECLARE_DERIVABLE_TYPE(VcamVedioMedia, vcam_vediomedia, VCAM, VEDIOMEDIA, GObject)

typedef struct _VcamVedioMediaPriv VcamVedioMediaPriv;
struct  _VcamVedioMedia {
    GObject parent_instance;
    VcamVedioMediaPriv priv;
    void (*setfmt)(Format *fmtptr)
};


struct _VcamVedioMediaClass {
    GObjectClass parent_class;


};

void vcam_vediomedia_setfmt(VcamVedioMedia* self, Format* fmtptr);
#endif /* __VCAM_VEDIOMEDIA_H__ */

同时在.c文件中绑定setfmt的实际执行方法,实现vcam_vediomedia_setfmt逻辑,并在该逻辑中发送格式改变的信号:

//默认的修改格式方法
void setfmt_default(Format* fmtptr) {
  //set format
}
//修改格式后,发送信号
void vcam_vediomedia_setfmt(VcamVedioMedia* self, Format* fmtptr) {

    VcamVedioMediaClass* klass;

    g_return_if_fail(VCAM_IS_VEDIOMEDIA(self));

    klass = VCAM_SOURCE_GET_CLASS(self);
    g_return_if_fail(klass->setfmt != NULL);

    klass->setfmt(self, fmtptr);
    //发送信号
    g_signal_emit(self, format_change, 0);

}

static void
vcam_vediomedia_class_init(VcamVedioMediaClass * kclass) {
    //注册私有属性
    g_type_class_add_private(kclass, sizeof(VcamVedioMediaPriv));
    kclass->setfmt = setfmt_default;
    /* override base object methods */
    GObjectClass* base_class = G_OBJECT_CLASS(kclass);
    base_class->set_property = vcam_vediomedia_set_property;
    base_class->get_property = vcam_vediomedia_get_property;
    /* install properties */
    g_object_class_install_property(base_class, VCAM_MEDIA_FORMAT,
        g_param_spec_object("format", "vedio format", "desscribe the vedio format", VCAM_TYPE_VEDIOMEDIA,G_PARAM_READWRITE));

    format_change = g_signal_new(FORMAT_CHANGED,
                                          G_TYPE_FROM_CLASS(kclass),
                                          G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
                                          0 /* class offset.Subclass cannot override the class handler (default handler). */,
                                          NULL /* accumulator */,
                                          NULL /* accumulator data */,
                                          NULL /* C marshaller. g_cclosure_marshal_generic() will be used */,
                                          G_TYPE_NONE /* return_type */,
                                          0     /* n_params */
                                          );


}

在发送消息中我们使用了g_signal_emit,他又三个参数:
第一个参数是发送当前信号的实例
第二个参数是信号的id,当前信号把存在format_change变量里(static guint format_change;

另外一个使用g_signal_emit_by_name等发送信号。

信号处理器
我们有一个类型叫做VcamSource, 它拥有一个VcamVedioMedia的属性,当VcamVedioMedia的格式放生变更时,VcamSource需要做一些事情。
信号处理器一般有两个参数:

  • 第一个参数标识发送信号的对象
  • 第二参数是一个指向用户数据(注册信号时传递的)的指针。
    如果信号本身有回传参数,这些参数需要在第一个和第二个参数之间,例如下面的处理器response_id就是信号传给处理器的参数。
    void user_function (GtkDialog *dialog, int response_id, gpointer user_data);
    我们在VcamVedioMedia的.c文件中定义这个处理器:
  void
     div_by_zero_cb(VcamVedioMedia * d, gpointer user_data) {
     g_print("\nthis is a signal handler\n\n");
 }

我们在连接的时候如果不传用户数据,第二个参数就可以省略掉

信号连接:
在实例化VcamVedioMedia的时候,我们将创建Format 对象,所以在哪个是偶,我会将这个format对象的消息与处理器连接:

 static void
vcam_source_init(VcamSource *d) {
      vedio = g_object_new(VCAM_TYPE_VEDIOMEDIA,NULL);
      g_signal_connect(vedio, FORMAT_CHANGED, G_CALLBACK(media_format_changed), NULL);

}

g_signal_connect有四个参数:
vedio 时发送信号的实例,也就是这个连接的源头.
FORMAT_CHANGED 时vediomedia.h文件中定义的字符串,标识信号的名称。
第三个参数media_format_changed就是信号处理器,注册时需要被映射为G_CALLBACK.
第四个参数是用户数据,可以传递给信号,null标识不需要用户数据。

这样利用信号通信来响应vediomedia格式变化的功能就实现了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容