这里除了信号的定义等函数外,还有一些函数需要定义,以进一步完善控件的功能。首先是信号发射的时机,我们这里设定当用户按下回车键后即发射此信号,所以要为单行文本录入控件的key_release_event信号加回调函数,来判断键入的信息,如果是回车键则发射我们定义的信号。
在发射信号后,我们需要显示一个简短的信息以证明信号已经发射,就是下面代码中定义的enter_ok函数,它的功能就是显示信息,表明信号成功发射。至于实例初始函数our_item_init和类初始化函数our_item_class_init,他们的实现和Gobject中的对象的实现是相同的。
此外创建控件的方法our_item_new和our_item_new_with_label都很简单,改变标签文字内容的方法our_item_set_label也只有几行代码,相信读者定会一目了然的。
以下为ouritem.c的完整代码:
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include "ouritem.h"
//定义枚举类型,说明信号的名称和次序
enum {
OURITEM_OK_SIGNAL,
LAST_SIGNAL
};
static gint ouritem_signals[LAST_SIGNAL] = { 0 };
static void our_item_init(OurItem *ouritem);
static void our_item_class_init(OurItemClass *ouritemclass);
static void enter_ok(void);
void on_key_release(GtkWidget *entry, GdkEventKey *event, gpointer data);
//注册自定义控件
GtkType our_item_get_type(void)
{
static GtkType our_item_type = 0;
if(!our_item_type)
{
GtkTypeInfo our_item_info = {
"OurItem", //控件名
sizeof(OurItem), //控件实例的尺寸
sizeof(OurItemClass), //控件类的尺寸
(GtkClassInitFunc)our_item_class_init, //控件类初始化函数
(cour_item_init, //控件实例初始化函数
NULL, //
NULL //
};
our_item_type = gtk_type_unique(GTK_TYPE_HBOX, &our_item_info);//注册此控件
}
return our_item_type;
}
//初始化实例结构
static void our_item_init(OurItem *ouritem)
{
ouritem->label = gtk_label_new(NULL);
gtk_box_pack_start(GTK_BOX(ouritem),ouritem->label,FALSE,FALSE,2);
ouritem->entry = gtk_entry_new();
g_signal_connect(G_OBJECT(ouritem->entry),"key_release_event",
G_CALLBACK(on_key_release),ouritem);
gtk_box_pack_start(GTK_BOX(ouritem),ouritem->entry,TRUE,TRUE,2);
}
//初始化类结构
static void our_item_class_init(OurItemClass *ouritemclass)
{
GtkObjectClass *object_class;
object_class = (GtkObjectClass*)ouritemclass;
//下面函数创建一个新的信号
ouritem_signals[OURITEM_OK_SIGNAL] = g_signal_new("ouritem_ok",
G_TYPE_FROM_CLASS(object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET(OurItemClass, enter_ok),
NULL,NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0, NULL);
ouritemclass->enter_ok = enter_ok;//此函数在下面定义
}
//创建新的自定义控件
GtkWidget* our_item_new(void)
{
return GTK_WIDGET(g_object_new(TYPE_OUR_ITEM,0));
}
//设定自定义控件前面的静态文本
void our_item_set_label(GtkWidget* item, gchar* label)
{
gtk_label_set_text(GTK_LABEL(OUR_ITEM(item)->label),label);
}
//带参数创建自定义控件
GtkWidget* our_item_new_with_label(gchar* label)
{
GtkWidget* item;
item = our_item_new();
our_item_set_label(item,label);
return item;
}
//此函数只是简单的在终端上提示你已经按了一次回车键
static void enter_ok(void)
{
g_print("OK! Enter key was clicked! /n");
}
//以下函数捕获键盘输入消息
void on_key_release(GtkWidget *entry, GdkEventKey *event, gpointer data)
{
if(event->keyval == GDK_Return) //当按下回车键后发射自定义的信号
{
g_signal_emit(G_OBJECT(data),ouritem_signals[OURITEM_OK_SIGNAL],0);
}
}
(5)编译与测试
可以编写一个小程序来测试一下这个自定义控件,测试的前提是先将实例结构和类结构的定义头文件ouritem.h包含到测试文件中来,先用 our_item_new_with_label来创建一个我们自定义的控件,然后为控件的ouritem_ok信号加一个回调函数 on_item_ok,函数的功能是向另一个单行录入控件中加入文本,最后将我们自定义的控件加入到窗口的纵向盒状容器中来。测试代码如下:
//main.c
#include <gtk/gtk.h>
#include "ouritem.h"
static GtkWidget *entry = NULL;
//以下函数为自定义控件ouritem的"ouritem_ok"信号的回调函数
void on_item_ok(GtkWidget *widget, gpointer data)
{
gtk_entry_set_text(GTK_ENTRY(entry),"OK!项目一录入结束");
}
int main(int argc, char* argv[])
{
GtkWidget *window, *vbox, *item1, *button;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window),"自定义控件测试");
g_signal_connect(G_OBJECT(window),"delete_event",
G_CALLBACK(gtk_main_quit),NULL);
gtk_container_set_border_width(GTK_CONTAINER(window),10);
vbox = gtk_vbox_new(FALSE,0);
gtk_container_add(GTK_CONTAINER(window),vbox);
//创建自定义控件
item1 = our_item_new_with_label("项目一:");
//为自定义控件的"ouritem_ok"信号连接函数
g_signal_connect(G_OBJECT(item1),"ouritem_ok",
G_CALLBACK(on_item_ok),NULL);
gtk_box_pack_start(GTK_BOX(vbox),item1,FALSE,FALSE,5);
entry = gtk_entry_new();
gtk_box_pack_start(GTK_BOX(vbox),entry,FALSE,FALSE,5);
button = gtk_button_new_with_label("退出");
gtk_box_pack_start(GTK_BOX(vbox),button,FALSE,FALSE,5);
g_signal_connect(G_OBJECT(button),"clicked",
G_CALLBACK(gtk_main_quit),NULL);
gtk_widget_show_all(window);
gtk_main();
return FALSE;
}
由于编译此测试程序的同时还要编译自定义控件的代码,所以需要写一个Makefile文件,然后用make来处理,Makefile内容如下所示:
CC = gcc
test:main.o ouritem.o
$(CC) main.o ouritem.o -o test `pkg-config --libs gtk -2.0`
main.o:main.c ouritem.h
$(CC) -c main.c -o main.o `pkg-config --cflags gtk -2.0`
ouritem.o:ouritem.c ouritem.h
$(CC) -c ouritem.c -o ouritem.o `pkg-config --cflags gtk -2.0`
运行此程序后,在项目一后面输入内容,点击回车键,会在下面的单行录入控件中显示"OK!项目一录入结束",同时在终端上也会显示"OK! Enter key was clicked!",这表明信号定义成功,并已经连接到了相应的回调函数。
至此我们初步完成了简单的自定义控件的定义、创建和测试过程。有兴趣的读者可以自行扩展和修改此控件的功能。
GTK 的入门向导中有两个完整的自定义控件的例程,对初学者来说可能过于复杂,相信看这个例程后会加深读者对它们的理解。
要编写功能更强大的控件会涉及到X Window底层的很多知识,如将X事件封装为GTK 的信号,对画布进行的画图操作等,这还有待于喜欢GTK 的朋友们进一步去研究和发掘。