GTK2.0教程,Hello World详解

这是一个标准的 GTK Hello World, 只有一个按钮构件的程序

#include <gtk/gtk.h>

/* 这是一个回调函数。data 参数在本示例中被忽略。
 * 后面有更多的回调函数示例。*/
void hello( GtkWidget *widget,
            gpointer   data )
{
    g_print ("Hello World\n");
}

gint delete_event( GtkWidget *widget,
                   GdkEvent  *event,
     gpointer   data )
{
    /* 如果你的 "delete_event" 信号处理函数返回 FALSE,GTK 会发出 "destroy" 信号。
     * 返回 TRUE,你不希望关闭窗口。
     * 当你想弹出“你确定要退出吗?”对话框时它很有用。*/

    g_print ("delete event occurred\n");

    /* 改 TRUE 为 FALSE 程序会关闭。*/

    return TRUE;
}

/* 另一个回调函数 */
void destroy( GtkWidget *widget,
              gpointer   data )
{
    gtk_main_quit ();
}

int main( int   argc,
          char *argv[] )
{
    /* GtkWidget 是构件的存储类型 */
    GtkWidget *window;
    GtkWidget *button;
    
    /* 这个函数在所有的 GTK 程序都要调用。参数由命令行中解析出来并且送到该程序中*/
    gtk_init (&argc, &argv);
    
    /* 创建一个新窗口 */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    
    /* 当窗口收到 "delete_event" 信号 (这个信号由窗口管理器发出,通常是“关闭”
     * 选项或是标题栏上的关闭按钮发出的),我们让它调用在前面定义的 delete_event() 函数。
     * 传给回调函数的 data 参数值是 NULL,它会被回调函数忽略。*/
    g_signal_connect (G_OBJECT (window), "delete_event",
        G_CALLBACK (delete_event), NULL);
    
    /* 在这里我们连接 "destroy" 事件到一个信号处理函数。  
     * 对这个窗口调用 gtk_widget_destroy() 函数或在 "delete_event" 回调函数中返回 FALSE 值
     * 都会触发这个事件。*/
    g_signal_connect (G_OBJECT (window), "destroy",
        G_CALLBACK (destroy), NULL);
    
    /* 设置窗口边框的宽度。*/
    gtk_container_set_border_width (GTK_CONTAINER (window), 10);
    
    /* 创建一个标签为 "Hello World" 的新按钮。*/
    button = gtk_button_new_with_label ("Hello World");
    
    /* 当按钮收到 "clicked" 信号时会调用 hello() 函数,并将NULL传给
     * 它作为参数。hello() 函数在前面定义了。*/
    g_signal_connect (G_OBJECT (button), "clicked",
        G_CALLBACK (hello), NULL);
    
    /* 当点击按钮时,会通过调用 gtk_widget_destroy(window) 来关闭窗口。
     * "destroy" 信号会从这里或从窗口管理器发出。*/
    g_signal_connect_swapped (G_OBJECT (button), "clicked",
         G_CALLBACK (gtk_widget_destroy),
                              window);
    
    /* 把按钮放入窗口 (一个 gtk 容器) 中。*/
    gtk_container_add (GTK_CONTAINER (window), button);
    
    /* 最后一步是显示新创建的按钮和窗口 */
    gtk_widget_show (button);
    gtk_widget_show (window);
    
    /* 所有的 GTK 程序必须有一个 gtk_main() 函数。程序运行停在这里
     * 等待事件 (如键盘事件或鼠标事件) 的发生。*/
    gtk_main ();
    
    return 0;
}

编译 Hello World 程序www.freedesktop.org 得到。这个程序读取 GTK 附带的 .pc 文件来决定编译 GTK 程序需要的编译选项。pkg-config --cflags gtk+-2.0 列出 include 目录,pkg-config --libs gtk+-2.0 列出编译连接库,也可以合在一起,像这样:pkg-config --cflags --libs gtk+-2.0

编译命令是:

gcc -Wall -g helloworld.c -o helloworld `pkg-config --cflags gtk+-2.0` \
`pkg-config --libs gtk+-2.0`

这里使用了程序 pkg-config,可以从

注意上面编译命令中使用的单引号类型是很重要的。是tab键上那个键。

信号和回调函数的原理事件

Hello World 详解设置构件属性这一章还有其它类似函数。

现在我们知道基本理论了,让我们来详细分析helloworld示例程序。

这是按钮被点击时要调用的回调函数。在这个示例中我们忽略了参数 widget 和 data,但是使用这些参数也不难。下一个示例会使用 data 参数来告诉我们哪个按钮被按下了。

void hello( GtkWidget *widget,
            gpointer   data )
{
    g_print ("Hello World\n");
}

接下来的一个回调函数有点特殊。"delete_event" 在窗口管理器发送这个事件给应用程序时发生。我们在这里可以选择对这些事件做什么。可以忽略它们,可以做一点响应,或是简单地退出程序。

这个回调函数返回的值让 GTK 知道该如何去做。返回 TRUE,让它知道我们不想让 "destroy" 信号被发出,保持程序继续运行。返回 FALSE,我们让 "destroy" 信号发出,这接着会调用 "destroy" 信号处理函数。

gint delete_event( GtkWidget *widget,
                   GdkEvent  *event,
     gpointer   data )
{
    g_print ("delete event occurred\n");

    return TRUE; 
}

这里是另一个回调函数,它通过调用 gtk_main_quit() 来退出程序。这个函数告诉 GTK 当控制权返回给它时就从 gtk_main 退出。

void destroy( GtkWidget *widget,
              gpointer   data )
{
    gtk_main_quit ();
}

我假设你知道 main() 函数...是的,像其它程序一样,所有的 GTK 程序有一个 main() 函数。

int main( int   argc,
          char *argv[] )
{

接下来声明两个指向 GtkWidget 类型的结构的指针。它们被用于创建一个窗口和一个按钮。

GtkWidget *window;
    GtkWidget *button;

这里又是 gtk_init()。跟前面一样,这个初始化工具包,分析命令行里的参数。它从参数列表中删除任何可以识别的参数,并且修改 argc 和 argv,使这些被删除的参数好像从来就不存在一样,而允许你的程序分析剩余的参数。

gtk_init (&argc, &argv);

创建一个新窗口。这个很直观。它为 GtkWidget *window 结构分配了内存,这样 window 现在指向了一个有效的结构。它建立了一个新窗口,但是这个窗口直到在程序后面部分我们调用 gtk_widget_show(window) 后才会显示出来。

window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

这有两个连接一个信号处理函数到一个对象 (本例中,就是 window ) 的示例。这里,"delete_event" 和 "destroy" 信号被捕获。当我们用窗口管理器去关闭窗口或调用函数 gtk_widget_destroy() 并将 window 构件作为对象传给它来销毁时,"delete_event" 信号发出。当我们在 "delete_event" 信号处理函数中返回 FALSE 值时,"destroy" 信号发出。G_OBJECTG_CALLBACK 是宏,为我们执行类型转换和检测,同时也增加了代码的可读性。

g_signal_connect (G_OBJECT (window), "delete_event",
                      G_CALLBACK (delete_event), NULL);
    g_signal_connect (G_OBJECT (window), "destroy",
                      G_CALLBACK (destroy), NULL);

接下来这个函数用于设置容器对象的属性。设置窗口边框宽度为10个象素。在

再次,GTK_CONTAINER也是一个执行类型转换的宏。

gtk_container_set_border_width (GTK_CONTAINER (window), 10);

这个函数调用创建一个新按钮。在内存中分配空间给一个新的 GtkWidget 结构,初始化它,并使 button 指针指向它。它显示后上面会有个 "Hello World" 标签。

button = gtk_button_new_with_label ("Hello World");

在这,我们让这个按钮做一些有用的事。我们给按钮设置信号处理函数,因此当按钮发出 "clicked" 信号时,hello() 函数被调用。我们忽略了 data 参数,简单地传送 NULL 给 hello() 回调函数。显而易见,当我们用鼠标点击按钮时,信号 "clicked" 被发出。

g_signal_connect (G_OBJECT (button), "clicked",
                      G_CALLBACK (hello), NULL);

我们也要使用这个按钮退出程序。这将演示 "destroy" 信号怎样由窗口管理器引发,或由我们的程序引发。当我们按下按钮时,和上面一样,它首先调用 hello() 回调函数,然后是这个函数,这依赖于它们被设置连接的顺序。你可以拥有许多回调函数,所有的回调按你设置连接的顺序依次执行。因为 gtk_widget_destroy() 函数只接受 GtkWidget *widget 作为唯一的参数,我们这里用 g_signal_connect_swapped() 函数代替正统的 g_signal_connect()。

g_signal_connect_swapped (G_OBJECT (button), "clicked",
                              G_CALLBACK (gtk_widget_destroy),
                              G_OBJECT (window));

这是一个组装调用,在组装构件这一章将作深入讲解。不过它相当容易理解。它简单地告诉 GTK 要把按钮放在窗口里,也就是它显示的地方。注意一个 GTK 容器只能包含一个构件。还有其它的构件,在后面介绍,设计为用来以各种方法布局多个构件。

gtk_container_add (GTK_CONTAINER (window), button);

一切准备就绪。所有信号处理函数连接好了,按钮也放进了窗口,我们让 GTK 在屏幕上“显示”这些构件。窗口构件最后显示,这样整个窗口会一下弹出,而不是先见到窗口弹出后再见到按钮。虽然这个简单的示例中,你不会注意到。

gtk_widget_show (button);

    gtk_widget_show (window);

接着,当然,我们调用 gtk_main() 函数来等待来自 X 服务器的事件,当这些事件到来时,调用构件发出信号。

gtk_main ();

最后返回,调用函数 gtk_quit() 后控制权返回到这里。

return 0;

现在,当我们用鼠标点击一个 GTK 按钮,构件发出一个 "clicked" 信号。为了让我们利用这个信息,程序设置了一个信号处理器来捕获那个信号,它按我们的选择依次调用函数。在我们的示例中,当按下按钮时,以 NULL 作为参数调用函数 hello(),然后调用该信号的下一个处理函数,该函数调用 gtk_widget_destroy() 函数,把窗口构件作为参数传递给它,销毁窗口构件。这导致窗口发出 "destroy" 信号,它被捕获,并且调用我们的 destroy() 回调函数,简单地退出 GTK。

如果用窗口管理器去关闭窗口,它会引发 "delete_event"。这会调用我们的 "delete_event" 处理函数。如果我们在函数中返回 TRUE,窗口还是留在那里,什么事也不发生。返回 FALSE,会使 GTK 发出 "destroy" 信号,它当然会调用 "destroy" 回调,退出 GTK。

数据类型

你或许发现前述示例中有几个地方需要解释。gint、gchar 等等。你看到的这些被分别定义类型别名(typedefs)到 int 和 char,它们是 GLib 系统的一部分。这用来避免在计算时对简单数据类型的大小(size)的依赖。

一个好的示例是,"gint32" 被定义为任何平台的32位整数,无论是64位的 alpha 还是32位的 i386。该类型定义非常直观。它们都在 glib/glib.h 里定义 (这个文件被gtk.h包含了 )。

你也将注意到 GTK 有在函数要一个 GtkObject 作为参数时传入 GtkWidget 的能力。GTK 的设计是面向对象的,一个构件是一个对象。

在 2.0 版,信号系统已从 GTK 移到 GLib,因此在函数和类型的说明中有前缀 "g_" 而不是 "gtk_"。我们不打算介绍 GLib 2.0 信号系统相对 GTK 1.2 信号系统扩展的细节。

在我们详细分析 helloworld 程序之前,我们会讨论信号和回调函数。GTK 是一个事件驱动的工具包,意味着它会等在 gtk_main() 那里,直到下一个事件发生,才把控制权传给适当的函数。

控制权的传递是使用“信号”的办法来完成的。(注意这里的信号并不等同于 Unix 系统里的信号,并且也不是用它们实现的,虽然使用的术语是一样的。) 当一个事件发生时,如按一下鼠标键,所按的构件会“发出”适当的信号。这就是 GTK 的工作机制。有所有构件都继承的信号,如 "destroy",有构件专有的信号,如开关 (toggle) 按钮发出的 "toggled" 信号。

要使一个按钮执行一个动作,我们需设置信号和信号处理函数之间的连接。可以这样使用函数来设置连接:

gulong g_signal_connect( gpointer      *object,
                         const gchar   *name,
                         GCallback     func,
                         gpointer      func_data );

第一个参数是要发出信号的构件,第二个参数是你想要连接的信号的名称,第三个参数是信号被捕获时所要调用的函数,第四个参数是你想传递给这个函数的数据。

第三个参数指定的函数叫做回调函数,一般为下面的形式:

void callback_func( GtkWidget *widget,
                    gpointer   callback_data );

第一个参数是一个指向发出信号的构件的指针,第二个参数是一个指向数据的指针,就是上面 g_signal_connect() 函数的最后一个参数传进来的数据。

注意上面回调函数的声明只是一般的形式,有些构件的特殊信号会用不同的调用参数。

另一个在 helloworld 示例中使用的调用,是:

gulong g_signal_connect_swapped( gpointer     *object,
                                 const gchar  *name,
                                 GCallback    func,
                                 gpointer     *slot_object );

g_signal_connect_swapped() 和 g_signal_connect() 相同,只是回调函数只用一个参数,一个指向 GTK 对象的指针。所以当使用这个函数连接信号时,回调函数应该是这样的形式

void callback_func( GtkObject *object );

这个对象通常是一个构件。然而我们一般不用函数 g_signal_connect_swapped() 设置回调。它们常用来调用一个只接受一个单独的构件或者对象作为参数的 GTK 函数,如同我们的 helloworld 示例中那样。

拥有两个函数来设置信号连接的目的只是为了允许回调函数有不同数目的参数。GTK 库中许多函数仅接受一个单独的构件指针作为其参数,所以对于这些函数你要用 g_signal_connect_swapped(),然而对你自己定义的函数,你可能需要附加的数据提供给你的回调函数。

除有前面描述的信号机制外,还有一套 events 反映 X 事件机制。回调函数可以与这些事件连接。这些事件是:

  • event
  • button_press_event
  • button_release_event
  • scroll_event
  • motion_notify_event
  • delete_event
  • destroy_event
  • expose_event
  • key_press_event
  • key_release_event
  • enter_notify_event
  • leave_notify_event
  • configure_event
  • focus_in_event
  • focus_out_event
  • map_event
  • unmap_event
  • property_notify_event
  • selection_clear_event
  • selection_request_event
  • selection_notify_event
  • proximity_in_event
  • proximity_out_event
  • visibility_notify_event
  • client_event
  • no_expose_event
  • window_state_event

为了连接一个回调函数到这些事件之一,你使用函数 g_signal_connect(),像前面介绍的一样,用上面事件名之一作为 name 参数。事件的回调函数与信号的回调函数有一点点不同:

gint callback_func( GtkWidget *widget,
                    GdkEvent  *event,
                    gpointer   callback_data );

GdkEvent 是一个 C 联合结构,它的类型依赖于上述事件中的哪个事件发生了。为了让我们得知发生了哪个事件,每个可能的类型都有一个 type 成员来反映发生的事件。事件结构的其它部分将依赖于这个事件的类型。类型的可能的值有:

GDK_NOTHING
  GDK_DELETE
  GDK_DESTROY
  GDK_EXPOSE
  GDK_MOTION_NOTIFY
  GDK_BUTTON_PRESS
  GDK_2BUTTON_PRESS
  GDK_3BUTTON_PRESS
  GDK_BUTTON_RELEASE
  GDK_KEY_PRESS
  GDK_KEY_RELEASE
  GDK_ENTER_NOTIFY
  GDK_LEAVE_NOTIFY
  GDK_FOCUS_CHANGE
  GDK_CONFIGURE
  GDK_MAP
  GDK_UNMAP
  GDK_PROPERTY_NOTIFY
  GDK_SELECTION_CLEAR
  GDK_SELECTION_REQUEST
  GDK_SELECTION_NOTIFY
  GDK_PROXIMITY_IN
  GDK_PROXIMITY_OUT
  GDK_DRAG_ENTER
  GDK_DRAG_LEAVE
  GDK_DRAG_MOTION
  GDK_DRAG_STATUS
  GDK_DROP_START
  GDK_DROP_FINISHED
  GDK_CLIENT_EVENT
  GDK_VISIBILITY_NOTIFY
  GDK_NO_EXPOSE
  GDK_SCROLL
  GDK_WINDOW_STATE
  GDK_SETTING

所以,连接一个回调函数到这些事件之一,我们会这样用:

g_signal_connect (G_OBJECT (button), "button_press_event",
                  G_CALLBACK (button_press_callback), NULL);

这里假定 button 是一个按钮构件。现在,当鼠标位于按钮上并按一下鼠标时,函数 button_press_callback() 会被调用。这个函数应该声明为:

static gint button_press_callback( GtkWidget      *widget, 
                                   GdkEventButton *event,
                                   gpointer        data );

注意,我们可以把第二个参数类型声明为 GdkEventButton,因为我们知道哪个类型的事件会发生。

这个函数的返回值指示这个事件是否应该由 GTK 事件处理机制做进一步的传播。返回 TRUE 指示这个事件已经处理了,且不应该做进一步传播。返回 FALSE 继续正常的事件处理。

转载于:https://www.cnblogs.com/daviyang/archive/2008/08/23/1859296.html

GTK+ 2.0 教程 译者: huzheng,konghui,ferry,carton,yang_yi,ddd,tingle 版本号: V_0.1.0 2002年 6 月25 日 本文是有关通过 C 语言接口使用 GTK (the GIMP Toolkit) 的教程。 Table of Contents 中文版说明 简介 从这里开始 用 GTK 来写 Hello World 编译 Hello World 程序 信号和回调函数的原理 事件 Hello World 详解 继续 数据类型 深入探索信号处理函数 改进了的 Hello World 组装构件 组装盒的原理 盒的细节 组装示范程序 用表组装 表组装示例 构件概述 类型转换 构件的组织 无窗口构件 按钮构件 一般按钮 Normal Buttons 开关按钮 Toggle Buttons 复选按钮 Check Buttons 单选按钮 Radio Buttons 调整对象 Adjustments 创建一个调整对象 轻松使用调整对象 “调整对象”的内部机制 范围构件 Range Widgets 滚动条构件 Scrollbar Widgets 比例构件 Scale Widgets 创建一个比例构件 函数和信号(至少讲了函数) 常用的范围函数 设置更新方式 获得和设置调整对象 键盘和鼠标绑定 示例 杂项构件 标签 Labels 箭头 Arrows 工具提示对象 The Tooltips Object 进度条 Progress Bars 对话框 Dialogs 标尺 Rulers 状态栏 Statusbars 文本输入构件 Text Entries 微调按钮 Spin Buttons 组合框 Combo Box 日历 Calendar 颜色选择 Color Selection 文件选择 File Selections 容器构件 Container Widgets 事件盒 The EventBox 对齐构件 The Alignment widget 固定容器 Fixed Container 布局容器 Layout Container 框架 Frames 比例框架 Aspect Frames 分栏窗口构件 Paned Window Widgets 视角 Viewports 滚动窗口 Scrolled Windows 按钮盒 Button Boxes 工具栏 Toolbar 笔记本 Notebooks 菜单构件 手工创建菜单 手工菜单示例 使用套件 套件示例 无文档构件 快捷标签 Accel Label 选项菜单 Option Menu 菜单项 Menu Items 复选菜单项 Check Menu Item 单选菜单项 Radio Menu Item 分隔菜单项 Separator Menu Item 分离菜单项 Tearoff Menu Item 曲线图 Curves 绘图区 Drawing Area 字体选择对话框 Font Selection Dialog 消息对话框 Message Dialog Gamma 曲线图 图像 Image 插头和插座 Plugs and Sockets 树视区 Tree View 文本视区 Text View 设置构件的属性 超时、IO 和 Idle 函数 超时 Timeouts 监控IO Idle 函数 高级事件和信号处理 信号函数 连接和断开信号处理函数 阻塞和反阻塞信号处理函数 发出和停止信号 信号的发射和传播 操作选中区 概述 获取选中区信息 提供选中区 拖放 概述 属性 函数 设置源构件 源构件上的信号 设置目的构件 目的构件上的信号 GLib 定义 双向链表 单向链表 存储管理 计时器 字符串处理 实用程序和错误处理函数 GTK 的 rc 文件 rc 文件的功能 GTK rc 文件的格式 rc 文件示例 编写你自己的构件 概述 一个构件的剖析 创建一个复合构件 介绍 选择一个父类 头文件 _get_type() 函数 _class_init() 函数 _init() 函数 其余的... 从头创建构件 介绍 在屏幕上显示构件 表盘构件的原形 主体 gtk_dial_realize() 大小磋商 gtk_dial_expose() 事件处理 可能的增强 深入的学习 涂鸦板,一个简单的绘图程序 概述 事件处理 绘图区构件和绘图 添加XInput支持 允许扩展设备信息 使用扩展设备信息 得到更多关于设备的信息 进一步的讲解 编写 GTK 应用程序的技巧 投稿 鸣谢 教程的版权和许可声明
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值