你的位置:首页 > 信息动态 > 新闻中心
信息动态
联系我们

GTK入门2 signal

2021-11-22 2:57:07

GTK of signal

测试项目

base.c

#include "Base.h"
#include <string.h>
 
enum
{
	HELLO_BASE_SIGNAL = 0,
	LAST_BASE_SIGNAL
};
 
static int basesignals[LAST_BASE_SIGNAL];
 
G_DEFINE_TYPE(TESTBase,test_base,G_TYPE_OBJECT);
 
static void test_base_init(TESTBase *pBase)
{
	memcpy(pBase->szName,"baseJob",sizeof(pBase->szName));
	printf("test_base_init.\n");
	
}
 
static void test_base_class_destroy(GObject *object)
{
	TESTBase *pTESTBase = NULL;
 
	pTESTBase = TEST_BASE(object);
	
	printf("test_base_class_destroy.\n");
}
 
static void test_base_class_finalize(GObject *object)
{
	TESTBase *pTESTBase = NULL;
 
	pTESTBase = TEST_BASE(object);
	
	printf("test_base_class_finalize.\n");
}
 
static void test_base_hello_signal(TESTBase *pBase,int i)
{
	
	printf("test_base_hello_signal[%p] [%d].\n",pBase,i);
}
 
static void test_base_class_init(TESTBaseClass *pBaseClass)
{
	pBaseClass->iAction = 1;
	
	//GTK_OBJECT_CLASS(pBaseClass)->destroy = test_base_class_destroy;
	G_OBJECT_CLASS(pBaseClass)->finalize = test_base_class_finalize;
	G_OBJECT_CLASS(pBaseClass)->dispose = test_base_class_destroy;
	pBaseClass->basehello  = test_base_hello_signal;
	basesignals[HELLO_BASE_SIGNAL] = g_signal_new ("basehello",
                      G_TYPE_FROM_CLASS (pBaseClass),
                      G_SIGNAL_RUN_LAST,
					  G_STRUCT_OFFSET (TESTBaseClass, basehello),
                      NULL,
                      NULL,
                      g_cclosure_marshal_VOID__INT,
                      G_TYPE_NONE, 1, G_TYPE_INT);
						//在base中创建一个信号,只要有继承,则可以使用该信号机制(这样可以实现一个信号类,专门应用于处理消息,需要用到的只要继承base即可)
	printf("test_base_class_init.\n");
}
void send_basehello_signal(TESTBase *pBase,int i)
{
	g_signal_emit (pBase, basesignals[HELLO_BASE_SIGNAL], 0,i);
}

base.h

#ifndef _BASE_H_
#define _BASE_H_
//#include <gtk/gtk.h>
 
#include <glib-object.h>
#include <stdio.h>
 
typedef struct _TESTBase 	 TESTBase;
typedef struct _TESTBaseClass TESTBaseClass;
 
#define TEST_TYPE_BASE    (test_base_get_type())
#define TEST_BASE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_BASE, TESTBase))
#define TEST_IS_BASE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_BASE))
#define TEST_BASE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_BASE, TESTBaseClass))
#define TEST_IS_BASE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_TYPE_BASE))
#define TEST_BASE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_BASE, TESTBaseClass))
 
 
struct _TESTBase
{
	GObject parent;
	char 	szName[32];
};
 
struct _TESTBaseClass
{
	GObjectClass classparent;
	int 		 iAction;
	void (*basehello)(TESTBase *obj,int i);
};
 
 
//GType   test_base_get_type(void);
void send_basehello_signal(TESTBase *pBase,int i);
GType test_base_get_type(void);

#endif

Child1.h

#include "Base.h"
 
typedef struct _TESTChild1 	 TESTChild1;
typedef struct _TESTChild1Class TESTChild1Class;
 
#define TEST_TYPE_CHILD1    (test_child1_get_type())
#define TEST_CHILD1(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_CHILD1, TESTChild1))
#define TEST_IS_CHILD1(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_CHILD1))
#define TEST_CHILD1_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_CHILD1, TESTChild1Class))
#define TEST_IS_CHILD1_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_TYPE_CHILD1))
#define TEST_CHILD1_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_CHILD1, TESTChild1Class))
 
struct _TESTChild1
{
	TESTBase parent;
	char 	 szChild1Name[32];
};
 
struct _TESTChild1Class
{
	TESTBaseClass classparent;
	int 		  iChild1Action;
	void		  (*helloCb)(TESTChild1 *pBase,int i);
};
 
 
GType   test_child1_get_type(void);
void send_hello_signal(TESTChild1 *pBase,int i);
//void test_hello_callback_connect(TESTChild1 *pBase,int i);
 

Child1.c

#include "Child1.h"
 
 
G_DEFINE_TYPE(TESTChild1,test_child1,TEST_TYPE_BASE);
 
enum
{
	HELLO_SIGNAL = 0,
	LAST_SIGNAL
};
 
static int signals[LAST_SIGNAL];//必须有,且用于索引信号数据
 
static void test_child1_class_destroy(GObject *object)
{
	TESTBase *pTESTBase = NULL;
 
	pTESTBase = TEST_BASE(object);
	
	printf("test_child1_class_destroy.\n");
	
	G_OBJECT_CLASS(test_child1_parent_class)->dispose(object);
}
 
static void test_child1_class_finalize(GObject *object)
{
	TESTChild1 *pTESTChild1 = NULL;
 
	pTESTChild1 = TEST_CHILD1(object);
	
	printf("test_child1_class_finalize.\n");
	G_OBJECT_CLASS(test_child1_parent_class)->finalize(object);
	
    //GTK_OBJECT_CLASS (test_child1_parent_class)->destroy (object);
}
 
 
static void test_child1_init(TESTChild1 *pBase)
{
	printf("test_child1_init [%s].\n",pBase->parent.szName);
}
static void test_hello_callback(TESTChild1 *pBase,int i)
{
	printf("TESTChild1[%p] recv hello signal[%d].\n",pBase,i);
}
 
static void test_child1_class_init(TESTChild1Class *pBaseClass)
{
	printf("test_child1_class_init [%d].\n",pBaseClass->classparent.iAction);
	
	//GTK_OBJECT_CLASS(pBaseClass)->destroy = test_child1_class_destroy;
	G_OBJECT_CLASS(pBaseClass)->dispose = test_child1_class_destroy;
	G_OBJECT_CLASS(pBaseClass)->finalize = test_child1_class_finalize;
	pBaseClass->helloCb = test_hello_callback;
 
	signals[HELLO_SIGNAL] = g_signal_new ("hello",
                      G_TYPE_FROM_CLASS (pBaseClass),
                      G_SIGNAL_RUN_FIRST,
                      G_STRUCT_OFFSET (TESTChild1Class, helloCb),
                      NULL,
                      NULL,
                      g_cclosure_marshal_VOID__INT,
                      G_TYPE_NONE, 1, G_TYPE_INT);//class 中必须包含对hello消息的定义。但如果只是想要有信号传递机制,
					//在base parent 有实现一个消息定义,此结构就可以不用定义。相当于创建一个base class去做信息传递的工作
		
}
 
void send_hello_signal(TESTChild1 *pBase,int i)
{
	g_signal_emit (pBase, signals[HELLO_SIGNAL], 0,i);
}

main.c

#include "Child1.h"  
#include <unistd.h>
 
static void test_hello_callback_connect(TESTChild1 *pBase,int i,gpointer user_data)
{
	int j = 0;
	printf("TESTChild1[%p] recv hello signal connect[%d].\n",pBase,i);
	sleep(3);
	printf("TESTChild1[%p] recv hello signal connect[%d] end.\n",pBase,i);
}
 
static void test_hello_callback_connect1(TESTChild1 *pBase,int i,gpointer user_data)
{
	int j = 0;
	printf("TESTChild1[%p] recv hello signal connect1[%d].\n",pBase,i);
	sleep(3);
	printf("TESTChild1[%p] recv hello signal connect1[%d] end.\n",pBase,i);
}
 
int main (void)  
{  
    // g_type_init ();  
    int i;  
    TESTChild1 *P,*P1,*P2;  
#if 0
    P = g_object_new (TEST_TYPE_CHILD1, NULL);  
    P1 = g_object_new (TEST_TYPE_CHILD1, NULL);  
	P2 = g_object_new (TEST_TYPE_CHILD1, NULL);
	printf("g_object_new P[%p] P1[%p] P2[%p].\n",P,P1,P2);
	g_signal_connect(G_OBJECT(P), "hello",
                             G_CALLBACK (test_hello_callback_connect1),NULL);
	g_signal_connect(G_OBJECT(P), "hello",
                             G_CALLBACK (test_hello_callback_connect),NULL);
	
	printf("start send signal.\n");
    send_hello_signal(P,1);
    send_hello_signal(P1,2);
    send_hello_signal(P2,3);
	send_basehello_signal(P1,4);
	printf("end send signal.\n");
	g_object_unref (P);  
	g_object_unref(P1);
	g_object_unref(P2);
#else
	P = g_object_new (TEST_TYPE_CHILD1, NULL);  
	// g_signal_connect(G_OBJECT(P), "hello",
    //                          G_CALLBACK (test_hello_callback_connect1),NULL);
	//在创建对象后,可以在任意位置使用connect来连接对象和信号。并定义callback
	// g_signal_emit_by_name(P,"hello",1);//当有两个定义的信号描述相同时,两个信号的callback都会触发,但运行时也会报wrong: signal "hello" already exists 
	
    g_object_unref (P); 
	printf("new second object.\n");
	P = g_object_new (TEST_TYPE_CHILD1, NULL);  
    g_object_unref (P); 
#endif
    return 0;  
}  

makefile

CC = gcc
gobject_v=gtk+-3.0
all:
	$(CC) -c Base.c `pkg-config --cflags $(gobject_v)`
	$(CC) -c Child1.c `pkg-config --cflags $(gobject_v)`
	$(CC) -c main.c `pkg-config --cflags $(gobject_v)`
	$(CC) -o simple Base.o Child1.o main.o `pkg-config --libs $(gobject_v)`
	sudo chmod 777 simple
clean:
	rm *.o
	rm simple

示例来源
原文的代码有些问题,稍微修改后ok,本人使用的gtk3,增加makefile和emit by name 的测试。可以尝试在main中修改宏测试看看。

信号量定义

信号量的定义,主要在于
信号变量数组

enum
{
	HELLO_BASE_SIGNAL = 0,
	LAST_BASE_SIGNAL
};
static int basesignals[LAST_BASE_SIGNAL];

class 中注册

basesignals[HELLO_BASE_SIGNAL] = g_signal_new ("basehello",
                    G_TYPE_FROM_CLASS (pBaseClass),
                    G_SIGNAL_RUN_LAST,
                    G_STRUCT_OFFSET (TESTBaseClass, basehello),
                    NULL,
                    NULL,
                    g_cclosure_marshal_VOID__INT,
                    G_TYPE_NONE, 1, G_TYPE_INT);
                    //在base中创建一个信号,只要有继承,则可以使用该信号机制(这样可以实现一个信号类,专门应用于处理消息,需要用到的只要继承base即可)

其中G_STRUCT_OFFSET (TESTBaseClass, basehello)描述的是在.h中定义的信号callback函数,相当于暴露callback函数的地址。
设定callback
设定,只要在call init 的时候顺带实现一下即可。pBaseClass->basehello = test_base_hello_signal;
callback函数,个人认为实际上是空实现也问题不大。

信号连接

  1. 初始化的时候就设定好callback
    如定义中使用的,在class init中定义好callback,就做好信号的连接
  2. 使用g_signal_connect()动态连接。将特定的信号量(基类已经初始化或者对象初始化本就创建的)和callback函数连接
P = g_object_new (TEST_TYPE_CHILD1, NULL);  
g_signal_connect(G_OBJECT(P), "hello",
                        G_CALLBACK (test_hello_callback_connect1),NULL);

如上是将P(object)的信号量和test_hello_callback_connect1()函数连接在一起,当P收到hello时,会自动去run callback 函数。也就是这点,只要在创建对象的时候,绑定一个callback 就可以实现信号通信。这样方式一就不是必要的
信号继承
当基类创建了一个信号,并做好了初始化(class init),则继承的函数可以直接使用该信号。这样即使自类不显式表达信号,实际上已经有一套信号可以使用了,这样可以实现一个信号类,专门应用于处理消息,需要用到的只要继承base即可。

运行顺序
对于方式一,如前描述作连接的时候使用

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,
              ...);

其中
signal_flags
a combination of GSignalFlags specifying detail of when the default handler is to be invoked. You should at least specify G_SIGNAL_RUN_FIRST or G_SIGNAL_RUN_LAST.

可选参数比想象要多,包含

G_SIGNAL_RUN_FIRST

Invoke the object method handler in the first emission stage.

G_SIGNAL_RUN_LAST

Invoke the object method handler in the third emission stage.

G_SIGNAL_RUN_CLEANUP

Invoke the object method handler in the last emission stage.

G_SIGNAL_NO_RECURSE

Signals being emitted for an object while currently being in emission for this very object will not be emitted recursively, but instead cause the first emission to be restarted.

G_SIGNAL_DETAILED

This signal supports “::detail” appendices to the signal name upon handler connections and emissions.

G_SIGNAL_ACTION

Action signals are signals that may freely be emitted on alive objects from user code via g_signal_emit() and friends, without the need of being embedded into extra code that performs pre or post emission adjustments on the object. They can also be thought of as object methods which can be called generically by third-party code.

G_SIGNAL_NO_HOOKS

No emissions hooks are supported for this signal.

G_SIGNAL_MUST_COLLECT

Varargs signal emission will always collect the arguments, even if there are no signal handlers connected. Since 2.30.

G_SIGNAL_DEPRECATED

The signal is deprecated and will be removed in a future version. A warning will be generated if it is connected while running with G_ENABLE_DIAGNOSTIC=1. Since 2.32.

G_SIGNAL_ACCUMULATOR_FIRST_RUN

Only used in GSignalAccumulator accumulator functions for the “run_type” field to mark the first call to the accumulator function for a signal emission. Since 2.68.

参数的选则决定了方式一&二的callback函数运行的次序。
eg:G_SIGNAL_RUN_FIRST->先运行方式一,再运行方式二,使用G_SIGNAL_RUN_LAST则相反

发射信号

  1. g_signal_emit_by_name ()
void
g_signal_emit_by_name (gpointer instance,
                       const gchar *detailed_signal,
                       ...);    

Parameters
instance

the instance the signal is being emitted on.
[type GObject.Object]
detailed_signal
a string of the form “signal-name::detail”.

parameters to be passed to the signal, followed by a location for the return value. If the return type of the signal is G_TYPE_NONE, the return value location can be omitted.

eg:g_signal_emit_by_name(P,"hello",1);
P(object)继承或在自己实现了hello这个信号,callback函数处理时会收到数据为第三个参数。
**由于是byname,当有多个object实现使用了同一个信号名"hello",特别在基类和子类中使用了同一个名称,运行时会触发两次,同时有warng警告:GLib-GObject-WARNING : 01:33:21.445: …/…/…/gobject/gsignal.c:1743: signal “hello” already exists in the ‘TESTBase’ class ancestry

  1. g_signal_emit ()
void
g_signal_emit (gpointer instance,
               guint signal_id,
               GQuark detail,
               ...);

Emits a signal.
Note that g_signal_emit() resets the return value to the default if no handlers are connected, in contrast to g_signal_emitv().
Parameters
instance

the instance the signal is being emitted on.
[type GObject.Object]
signal_id
the signal id
detail
the detail

parameters to be passed to the signal, followed by a location for the return value. If the return type of the signal is G_TYPE_NONE, the return value location can be omitted.
eg:g_signal_emit (pBase, signals[HELLO_SIGNAL], 0,i);
signal_id实际就是创建信号量时的数组位置signals[HELLO_SIGNAL],在数组共享的情况下,使用起来还是比较方便的,但可能会导致代码可读性下降。因为信号名称和数组最显式的关联在class int时signals[HELLO_SIGNAL] = g_signal_new ("hello",.....