1 信号
每个对象都封装在Gtk程序中。而且不推荐使用全局变量,因为它们容易使程序变得复杂。因此,我们需要一些东西来在对象之间通信。有两种方法可以做到这一点。
- 函数。例如,tb = gtk_text_view_get_buffer (tv)。调用者请求tv提供tb,这是一个连接到tv的GtkTextBuffer实例。
- 信号。例如,在GApplication对象上的activate信号。当应用程序被启动时,就会发出信号。这时,连接到该信号的处理程序会被调用。
函数的调用者或连接到信号的处理程序通常位于对象之外。这两者之间的区别之一是对象是主动的或被动的。在函数中,对象被动地响应调用者。对象主动向处理程序发送信号。
GObject信号被注册、连接和发射。
- 信号注册到发出信号的对象类型。注册通常在对象类初始化时完成。
- 信号通过g_connect_signal或其族函数连接到处理程序。连接通常是在对象之外完成的。
- 在发射信号时,调用连接的处理程序。信号在对象的实例上发出。
2 信号注册
在TfeTextView中,注册了两个信号。
- “change-file”信号。当tv->file发生改变时,发出此信号。
- “open-response”信号。tfe_text_view_open函数不能返回状态,因为它使用了GtkFileChooserDialog。发出这个信号而不是函数的返回值。
一个静态变量或数组用于存储信号ID。
enum {
CHANGE_FILE,
OPEN_RESPONSE,
NUMBER_OF_SIGNALS
};
static guint tfe_text_view_signals[NUMBER_OF_SIGNALS];
信号注册在类初始化函数中。
1 static void
2 tfe_text_view_class_init (TfeTextViewClass *class) {
3 GObjectClass *object_class = G_OBJECT_CLASS (class);
4
5 object_class->dispose = tfe_text_view_dispose;
6 tfe_text_view_signals[CHANGE_FILE] = g_signal_new ("change-file",
7 G_TYPE_FROM_CLASS (class),
8 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
9 0 /* class offset */,
10 NULL /* accumulator */,
11 NULL /* accumulator data */,
12 NULL /* C marshaller */,
13 G_TYPE_NONE /* return_type */,
14 0 /* n_params */
15 );
16 tfe_text_view_signals[OPEN_RESPONSE] = g_signal_new ("open-response",
17 G_TYPE_FROM_CLASS (class),
18 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
19 0 /* class offset */,
20 NULL /* accumulator */,
21 NULL /* accumulator data */,
22 NULL /* C marshaller */,
23 G_TYPE_NONE /* return_type */,
24 1 /* n_params */,
25 G_TYPE_INT
26 );
27 }
- 6-15:注册“change-file”信号。g_signal_new函数被使用。信号“change-file”没有默认处理程序(object method handler),因此偏移量(第9行)设置为零。通常不需要默认处理程序。如果需要,请使用g_signal_new_class_handler函数而不是g_signal_new。更多信息请参见GObject API参考。
- g_signal_new的返回值是信号id。signal id的类型是guint,与unsigned int相同。它在函数g_signal_emit中使用。
- 16-26:表示“open-response”信号。这个信号有一个参数。
- 24:参数个数。“open-response”信号只有一个参数。
- 25:参数类型。G_TYPE_INT是整数类型。这些基本类型在GObject参考手册中有描述。
处理程序声明如下。
/* "change-file" signal handler */
void
user_function (TfeTextView *tv,
gpointer user_data)
/* "open-response" signal handler */
void
user_function (TfeTextView *tv,
TfeTextViewOpenResponseType response-id,
gpointer user_data)
- 因为“change-file”信号没有参数,所以处理程序的参数是一个TfeTextView实例和用户数据。
- 因为“open-response”信号只有一个参数,所以处理程序的参数是一个TfeTextView实例、信号的参数和用户数据。
- tv是发射信号的对象实例。
- user_data来自g_signal_connect的第四个参数。
- parameter来自g_signal_emit的第四个参数。
参数的值在tfetextview.h中定义,因为它们是公有的。
/* "open-response" signal response */
enum
{
TFE_OPEN_RESPONSE_SUCCESS,
TFE_OPEN_RESPONSE_CANCEL,
TFE_OPEN_RESPONSE_ERROR
};
- 当tfe_text_view_open成功打开并读取文件时,该参数设置为TFE_OPEN_RESPONSE_SUCCESS。
- 当用户取消时,该参数设置为TFE_OPEN_RESPONSE_CANCEL。
- 当发生错误时,该参数设置为TFE_OPEN_RESPONSE_ERROR。
3 信号连接
信号和处理程序通过函数g_signal_connect连接起来。还有一些类似的函数,如g_signal_connect_after、g_signal_connect_等。但g_signal_connect是最常见的。信号“change-file”连接到TfeTextView对象外的回调函数。以同样的方式,信号“open-response”连接到TfeTextView对象外的回调函数。这些回调函数由用户定义。
在程序tfe中,回调函数定义在tfenotebook.c中。它们的名字是file_changed和open_response。稍后会解释。
g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed), nb);
g_signal_connect (TFE_TEXT_VIEW (tv), "open-response", G_CALLBACK (open_response), nb);
4 信号发射
信号是在实例上发出的。该实例的类型是g_signal_new的第二个参数。信号和对象类型之间的关系在信号注册时确定。
函数g_signal_emit用于发出信号。下面几行是从tfetextview.c中提取的。每一行都来自不同的一行。
g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS);
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_CANCEL);
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);
- 第一个参数是发出信号的实例。
- 第二个参数是信号id。
- 第三个参数是信号的细节。“change-file”信号和“open-response”信号没有细节,当没有细节时参数为0。
- “change-file”信号没有参数,所以没有第四个参数。
- “open-response”信号只有一个参数。第四个参数是传向回调函数的变量。