Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5789
  • 博文数量: 2
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2021-03-17 09:18
文章分类
文章存档

2021年(2)

我的朋友
最近访客

分类: Android平台

2021-03-17 09:39:04

原文地址:GTK 中自定义信号 作者:iibull


点击(此处)折叠或打开

  1. #include "xconfig.h"

  2. static gchar buffer[256];
  3. static GtkWidget *window;
  4. static GtkWidget *darea;
  5. static guint id_signal;

  6. static gboolean time_handler(GtkWidget *widget)
  7. {    
  8.     time_t curtime;
  9.     struct tm *loctime;
  10.     curtime = time(NULL);
  11.     loctime = localtime(&curtime);
  12.     strftime(buffer, 256, "%T", loctime);
  13.     //发射信号    
  14.     g_signal_emit_by_name (window, "custom-signal", buffer);
  15.     return TRUE;
  16. }

  17. static void gtk_custom_signal_callback(gpointer instance, gpointer arg1, gpointer user_data)
  18. {    
  19.     gtk_window_set_title(GTK_WINDOW(window), (gchar *)arg1);    
  20.     g_printf("in gtk_custom_signal_callback.\n");
  21. }

  22. int main(int argc, char **argv)
  23. {
  24.     gtk_init(&argc, &argv);
  25.     
  26.     window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  27.     darea = gtk_drawing_area_new();
  28.     gtk_container_add(GTK_CONTAINER (window), darea);

  29.     g_signal_connect(window, "destroy",G_CALLBACK(gtk_main_quit), NULL);
  30.     gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  31.     gtk_window_set_default_size(GTK_WINDOW(window), 170, 100);
  32.     gtk_window_set_title(GTK_WINDOW(window), "timer");

  33.     g_timeout_add(1000, (GSourceFunc) time_handler, (gpointer) window); //建立定时器
  34. #if 1    
  35.     //建立信号
  36.     id_signal = g_signal_new ("custom-signal", //最好用 - 分隔符
  37.                   G_TYPE_OBJECT,
  38.                   G_SIGNAL_RUN_LAST,
  39.                   0,
  40.                   NULL, //callback 成功后 收尾的处理. 可以为 NULL, NULL
  41.                   NULL, //callback 的 user_data
  42.                   g_cclosure_marshal_VOID__POINTER, //g_cclosure_marshal_VOID__POINTER----------typedef void (*callback) (gpointer instance, gpointer arg1, gpointer user_data);
  43.                   G_TYPE_NONE, //返回类型
  44.                   1, //参数个数
  45.                   G_TYPE_POINTER//参数类型
  46.                   );
  47. #else
  48.     id_signal = g_signal_new ("custom-signal",
  49.                   G_TYPE_OBJECT,
  50.                   G_SIGNAL_RUN_LAST,
  51.                   0,
  52.                   NULL, NULL,
  53.                   g_cclosure_marshal_VOID__VOID, //g_closure_marshal_VOID_VOID说明该signal的回调函数为以下的callback类型:typedef void (*callback) (gpointer instance, gpointer user_data);
  54.                   G_TYPE_NONE, //返回类型
  55.                   0 //参数个数
  56.                   //参数类型
  57.                   );
  58. #endif    
  59.     //绑定信号
  60.     gulong id_handler = g_signal_connect_object(window, "custom-signal",G_CALLBACK(gtk_custom_signal_callback), window, G_CONNECT_AFTER);
  61.     g_printf("bind signal return. %ld.\n", id_handler);
  62.     
  63.     gtk_widget_show_all(window);
  64.     time_handler(window);
  65.     gtk_main();
  66.     return 0;
  67. }


点击(此处)折叠或打开

  1. Target=demo

  2. SrcDir= .
  3. ExtList= .cpp

  4. Source= $(foreach ext,$(ExtList), \
  5.     $(foreach sd, $(SrcDir), $(wildcard $(addprefix $(sd)/*,$(ext)))))    

  6. Objs= $(foreach ext, $(ExtList), \
  7.     $(patsubst %$(ext), %.o, $(filter %$(ext),$(Source))))\
  8.     #resources.o

  9. CFLAGS := -Wall -Wno-unused-function -g -rdynamic

  10. LDFlags := -lrt -rdynamic
  11. LDFlags += `pkg-config --libs gtk+-3.0`
  12. LDFlags += `pkg-config --libs opencv`
  13. LDFlags += `pkg-config --libs x265` #sudo apt install libx265-{doc/dev}
  14. LDFlags += `pkg-config --libs libavcodec libavutil libavformat`
  15. LDFlags += `pkg-config --libs libswscale`

  16. INCFLAGS := -I .
  17. INCFLAGS += `pkg-config --cflags gtk+-3.0`
  18. INCFLAGS += `pkg-config --cflags opencv`
  19. INCFLAGS += `pkg-config --cflags x265`
  20. INCFLAGS += `pkg-config --cflags libavcodec libavutil libavformat`
  21. INCFLAGS += `pkg-config --cflags libswscale`

  22. CC=g++
  23. CPP=g++
  24. GLIB_COMPILE_RESOURCES=glib-compile-resources

  25. %.o : %.c
  26.     $(CC) $(CFLAGS) -c $(@D)/$(<F) -o $(@D)/$(@F) $(INCFLAGS)
  27. %.o : %.cpp
  28.     $(CPP) $(CFLAGS) -c $(@D)/$(<F) -o $(@D)/$(@F) $(INCFLAGS)

  29. INSTALL_PROGRAM = ${INSTALL} -c -s -m 555

  30. $(Target): $(Objs)
  31.     $(CC) $(CFLAGS) -o $(Target) $(Objs) $(LDFlags)

  32. all: $(Target)

  33. objs: $(Objs)

  34. resources.cpp: demo.gresource.xml
  35.     $(GLIB_COMPILE_RESOURCES) demo.gresource.xml --target=$@ --sourcedir=. --generate-source

  36. RM= rm -rf

  37. clean:
  38.     @$(RM) $(foreach sd, $(SrcDir), $(wildcard $(sd)/*.o))
  39.     @$(RM) $(Target)
  40.     @$(RM) resources.cpp

  41. cleanall:clean
  42.     @$(RM) $(Target)

  43. rebuild: cleanall




对于glib,我们经常用到信号通知机制来触发事件,在GObject中,可以自己定义signal信号,用于事件触发中。

       一般在class_init时,由g_signal_new配合一些参数生成一个新信号句柄,然后使用g_signal_connect连接对象和处理方式(回调函数),最后由g_signal_emit发出信号触发。

 

一,其中最关键的是就g_signal_new,在这个过程中,我们应该先了解下相关的参数,其一般模型如下:

guint

g_signal_new (const gchar     *signal_name,

             GType          itype,

             GSignalFlags        signal_flags,

             guint               class_offset,

             GSignalAccumulator  accumulator,

             gpointer               accu_data,

             GSignalCMarshaller  c_marshaller,

             GType          return_type,

             guint            n_params,

             ...)

 

通过上面的函数模型可以看出,其参数个数是可变的。而这些参数有些是必须的,有些可设为NULL,下面可以结合一个基于clutter的实例来解释一下各个参数的含义(SECTION 12是从clutter源代码中提取出来的定义示例)

1,            const gchar *signal_name: 该参数是定义信号的名字,它由分隔符以及ASCII码中的字母和数字构成,且第一个字符必须是字母,实例中定义的名字为"myself-signal"。(分隔符可以是"-""_"——事实上,系统会先调用g_strdelimit"_"转化为"-"再存储signal_name。因此,在调用g_singal_emit_by_name时,detailed_signal参数中的分隔符必须是"-");

2,            GType itype:该参数是signal所依附的类的在GType类型系统中注册时得到的ID,也就是*_get_type()函数的返回值。而在clutter工程中,class_init时可使用G_OBJECT_CLASS_TYPE(klass)来获得,实例中使用CLUTTER_TYPE_ACTOR获取。(clutter _actor应该才是stagetexturetext的基类);

3,           GSignalFlags signal_flags:该参数是信号的属性标记,共有七种,其中有提到"per-object handler",将在下面class_offset中介绍:

· G_SIGNAL_RUN_FIRST:调用回调函数时,"per-object handler"对应的回调函数将第一个调用;

· G_SIGNAL_RUN_LAST:调用回调函数时,"per-object handler"对应的回调函数将在用户用g_signal_connect连接的回调函数之后调用,并在用户用g_signal_connect _after连接的回调函数之前调用;

· G_SIGNAL_RUN_CLEANUP:调用回调函数时,"per-object handler"对应的回调函数将最后一个调用;

信号发射时,如果信号的上次发射还没有结束,那么本次信号发射将不再进行,而只是使上次的信号发射重新开始。[wyj1] 

· G_SIGNAL_DETAILED:信号名字可以使用"signal_name::detailed"的形式。

· G_SIGNAL_ACTION:程序员可以在代码中自由地调用g_signal_emit族的函数来发射信号,而不需要把g_signal_emit族的函数放在一段代码中再来调用。

· G_SIGNAL_NO_HOOKS:信号发射过程中不支持钩子函数。

4,            guint class_offset:该参数是itype对应的类的class结构中的一个函数指针相对于class结构的实例的首地址的偏移量。该函数指针所对应的函数常被称为"per-object handler","default (signal) handler""object methond handler",并将在信号发出后被调用(如调用g_signal_emit_by_name)。常配合宏G_STRUCT_OFFSET使用(该宏能够返回结构体变量的成员相对于该结构体的变量的首地址的偏移量)。如SECTION1中,G_STRUCT_OFFSET (ClutterActorClass, paint),就是指在clutterActorClass类中,"per-object handler” paint的函数的偏移地址。而如果将该参数设为0,则表示该类没有"per-object handler"。实例中由于在类中没有定义相应的"per-object handler”,故设为0

5,            GSignalAccumulator accumulator:该参数是一个函数指针,其对应的函数将在该信号的每个handler执行完以后执行。其函数模型可参见SECTION 3。该函数的返回类型为gboolean如果其返回值为FASLE,则signal发射过程就会被中止(即不再调用后面的hander),否则会继续下去。事实上,"delete-event"等带有event后缀的signal就是利用了这一点——这些信号的某个回调函数如果返回了TRUE,则以后的回调函数就不会被调用。我的理解是,此函数的功能就是决定信号还要不要继续往下走?(注意,如果该signalaccumulator,则回调函数类型(由c_marshaller反映)必须有返回值,否则该signal不能有accumulator,也即在调用g_signal_new时以NULL作为该形参的实参)

6,           gpointer accu_data:该参数将作为用户自定义参数传入accumulator所指向的函数中。

7,            GSignalCMarshaller c_marshaller:该参数是一个GSignalCMarshall类型的函数指针,其值反映了回调函数的返回值类型和额外参数类型(所谓额外参数,即指除回调函数中instanceuser_data以外的参数)。

l             例如,g_closure_marshal_VOID_VOID说明该signal的回调函数为以下的callback类型:typedef void (*callback)  (gpointer instance, gpointer user_data);

l             g_closure_marshal_VOID_POINTER----------typedef void (*callback)  (gpointer instance, gpointer arg1, gpointer user_data);

l             g_cclosure_marshal_VOID__CHAR ()---------void (*callback) (gpointer instance, gchar arg1, gpointer user_data)

l             g_cclosure_marshal_VOID__INT ()-----------void (*callback) (gpointer instance, gint arg1, gpointer user_data)

l             以前列举了部分,可参考http://developer.gnome.org/gobject/unstable/gobject-Closures.html。如果默认提供的GClosureMarshall中没有你需要的,你可以用glib-genmarshall生成它,具体可参见devhelp中有关glib-genmarshall的说明。如为信号生成一个参数转换函数为custom_marshal_VOID__OBJECT

[socol@localhost ~]$:cat marshal.list   

VOID:OBJECT     

[socol@localhost ~]$:glib-genmarshal --header --prefix=custom_marshal marshal.list > custom_marshal.h   

[socol@localhost ~]$:glib-genmarshal --header --prefix=custom_marshal marshal.list > custom_marshal.c 

8,            GType return_type:该参数的值应为回调函数的返回值在GType类型系统中的IDG_TYPE_NONE表示没有返回值,G_TYPE_BOOLEAN表示返回值为gboolean

9,            guint n_params:该参数的值应为回调函数的额外参数的个数,如SECTION 10,则表示后面没有参数,SECTION 21,表示后面还有一个参数。

 

二,定义好一个signal后,就是要将该信号与对象object和回调函数callback关联起来,使用g_signal_connect或者g_signal_connect_after函数。

/* * g_signal_connect:

 * @instance: the instance to connect to.

 * @detailed_signal: a string of the form "signal-name::detail".

 * @c_handler: the #GCallback to connect.

 * @data: data to pass to @c_handler calls.

* Connects a #GCallback function to a signal for a particular object.

* The handler will be called before the default handler of the signal.

* Returns: the handler id

 */

#define g_signal_connect(instance, detailed_signal, c_handler, data) \

g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, (GConnectFlags) 0)

 

/**

* The handler will be called after the default handler of the signal.

* Returns: the handler id

 */

#define g_signal_connect_after(instance, detailed_signal, c_handler, data) \

    g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, G_CONNECT_AFTER)

 

实例中如下:

       g_signal_connect (stage, "key-press-event", G_CALLBACK (_keyboard_cb),  (gpointer)text);

       g_signal_connect (texture, "myself-signal", G_CALLBACK (on_paint),  NULL);

 

 

三,最后就是发射相关信号触发事件。

       两种信号发射方式:

1,使用signal name: g_signal_emit_by_name(texture, "myself-signal");

2,使用signal handle: g_signal_emit(texture, mysignal, 0);

 

 

clutter实例:

 

#include

 

ClutterActor *stage, *texture;

gint mysignal;

 

static void

on_paint (ClutterActor *actor, gconstpointer *data)

{

  printf("on paint\n");

}

 

 

static void

_keyboard_cb(ClutterActor *actor, ClutterEvent * event, gpointer *data)

{

       printf("keyboard cb\n");

       g_signal_emit_by_name(texture, "myself-signal");

       // g_signal_emit(texture1, mysignal, 0);

             

}

 

 

gint main(gint argc, gchar *argv[])

{

clutter_init (&argc, &argv);

       stage = clutter_stage_get_default ();

       clutter_actor_set_size (stage, 1280, 720);

       g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);

 

       GType type = CLUTTER_TYPE_ACTOR;

       printf("type=%d\n", type);

 

       texture = clutter_texture_new_from_file("/res/play.png",NULL);

       clutter_actor_set_position(texture,100,230);

 

       texture1 = clutter_texture_new_from_file("/res/play.png",NULL);

       clutter_actor_set_position(texture1,100,330);

 

       text = clutter_text_new_with_text ("sans 22",   "hello");

       clutter_actor_set_position(text,100,130);

       clutter_text_set_color(CLUTTER_TEXT(text), &text_color);

      

       ClutterActorClass *klass;

       GObjectClass *object_class;

       klass = CLUTTER_ACTOR_GET_CLASS (…);

object_class = G_OBJECT_CLASS (klass);

 

mysignal = g_signal_new ("myself-signal",

               CLUTTER_TYPE_ACTOR, //G_TYPE_FROM_CLASS (object_class),

                G_SIGNAL_RUN_LAST,

                0,

                NULL, NULL,

                g_cclosure_marshal_VOID__VOID,

                G_TYPE_NONE, 0

                );

      

       g_signal_connect (stage, "key-press-event", G_CALLBACK (_keyboard_cb),  text);

       g_signal_connect (texture, "myself-signal", G_CALLBACK (on_paint),  NULL);

 

       clutter_container_add (CLUTTER_CONTAINER (stage), texture,   NULL);

}

 

 

 

SECTION 1

/**

   * ClutterActor::paint:

   * @actor: the #ClutterActor that received the signal

   * The ::paint signal is emitted each time an actor is being painted.

   * Subclasses of #ClutterActor should override the class signal handler

   * and paint themselves in that function.

   * It is possible to connect a handler to the ::paint signal in order

   * to set up some custom aspect of a paint.

   * Since: 0.8

   */

actor_signals[PAINT] =

    g_signal_new (I_("paint"),

                  G_TYPE_FROM_CLASS (object_class),

                  G_SIGNAL_RUN_LAST,

                  G_STRUCT_OFFSET (ClutterActorClass, paint),

                  NULL, NULL,

                  _clutter_marshal_VOID__VOID,

                  G_TYPE_NONE, 0);

 

 

SECTION 2:

  /**

   * ClutterActor::key-press-event:

   * @actor: the actor which received the event

   * @event: a #ClutterKeyEvent

   *

   * The ::key-press-event signal is emitted each time a keyboard button

   * is pressed while @actor has key focus (see clutter_stage_set_key_focus()).

   *

   * Return value: %TRUE if the event has been handled by the actor,

   *   or %FALSE to continue the emission.

   *

   * Since: 0.6

   */

  actor_signals[KEY_PRESS_EVENT] =

    g_signal_new (I_("key-press-event"),

                     G_TYPE_FROM_CLASS (object_class),

                     G_SIGNAL_RUN_LAST,

                     G_STRUCT_OFFSET (ClutterActorClass, key_press_event),

                     _clutter_boolean_handled_accumulator, NULL,

                     _clutter_marshal_BOOLEAN__BOXED,

                     G_TYPE_BOOLEAN, 1,

                     CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);

 

SECTION 3

 

gboolean

_clutter_boolean_handled_accumulator (GSignalInvocationHint *ihint,

                                      GValue                *return_accu,

                                      const GValue          *handler_return,

                                      gpointer               dummy)

{

  gboolean continue_emission;

  gboolean signal_handled;

 

  signal_handled = g_value_get_boolean (handler_return);

  g_value_set_boolean (return_accu, signal_handled);

  continue_emission = !signal_handled;

 

  return continue_emission;

}

阅读(1369) | 评论(0) | 转发(0) |
0

上一篇:Gtk事件与信号关系

下一篇:没有了

给主人留下些什么吧!~~