47、基于 GTK+ 的 GNOME 编程入门

基于GTK+的GNOME编程入门指南

基于 GTK+ 的 GNOME 编程入门

在图形用户界面(GUI)编程中,GTK+ 和 GNOME 库为开发者提供了强大的工具集,用于创建功能丰富、美观易用的应用程序。本文将详细介绍如何使用 GTK+ 和 GNOME 库进行编程,包括创建窗口、菜单和对话框等常见 GUI 元素。

1. GTK+ 与 GNOME 概述

GTK+ 是一个跨平台的 GUI 工具包,设计为桌面中立,可轻松移植到 Windows 或其他窗口系统。然而,GTK+ 缺乏将程序与桌面集成的功能,如保存程序配置、显示帮助文件或编写小程序。GNOME 库则包含了扩展 GTK+ 的 GNOME 小部件,用更易用的小部件替换了部分 GTK+ 小部件。

在使用 GNOME 库之前,需要在程序开始时进行初始化,就像使用 GTK+ 时调用 gtk_init 一样。可以使用 gnome_program_init 函数进行初始化:

GnomeProgram* gnome_program_init (const char *app_id, const char *app_version,
const GnomeModuleInfo *module_info,
int argc, char **argv,
const char *first_property_name,
...);

该函数接受应用程序 ID、版本号、模块信息、命令行参数和应用程序属性等参数。可选的属性列表允许设置一些选项,如查找位图图形的目录。

2. 创建 GNOME 窗口

下面是一个简单的 GNOME 程序示例,创建一个 GnomeApp 窗口:

#include <gnome.h>
int main (int argc, char *argv[])
{
    GtkWidget *app;
    gnome_program_init (“gnome1”, “1.0”, MODULE, argc, argv, NULL);
    app = gnome_app_new (“gnome1”, “The Window Title”);
    gtk_widget_show(app);
    gtk_main ();
    return 0;
}

编译时需要包含 GNOME 头文件,并通过 pkg-config 传递 libgnomeui libgnome

$ gcc gnome1.c –o gnome1 `pkg-config --cflags --libs libgnome-2.0 libgnomeui-2.0`

GnomeApp 小部件扩展了 GtkWindow ,方便添加菜单、工具栏和底部状态栏。由于它继承自 GtkWindow ,可以使用任何 GtkWindow 函数。

3. 创建 GNOME 菜单

在 GNOME 中创建下拉菜单非常简单。每个菜单由 GNOMEUIInfo 结构体数组表示,数组中的每个元素对应一个菜单项。例如,有文件、编辑和视图菜单时,将有三个数组描述每个菜单的内容。定义完每个单独的菜单后,通过在另一个 GNOMEUIInfo 结构体数组中引用这些数组来创建菜单栏。

GNOMEUIInfo 结构体定义如下:

typedef struct {
    GnomeUIInfoType type;
    gchar const *label;
    gchar const *hint;
    gpointer moreinfo;
    gpointer user_data;
    gpointer unused_data;
    GnomeUIPixmapType pixmap_type;
    gconstpointer pixmap_info;
    guint accelerator_key;
    GdkModifierType ac_mods;
    GtkWidget *widget;
} GnomeUIInfo;

其中, type 定义了后续菜单项的类型,有 10 种 GnomeUIInfoType 可供选择,如下表所示:
| GnomeUIInfoType | 描述 |
| — | — |
| GNOME_APP_UI_ENDOFINFO | 表示这是数组中的最后一个菜单项 |
| GNOME_APP_UI_ITEM | 普通菜单项或单选按钮(如果前面有 GNOME_APP_UI_RADIOITEMS 条目) |
| GNOME_APP_UI_TOGGLEITEM | 切换按钮或复选框菜单项 |
| GNOME_APP_UI_RADIOITEMS | 单选按钮组 |
| GNOME_APP_UI_SUBTREE | 表示此元素是子菜单。将 moreinfo 设置为指向子菜单数组 |
| GNOME_APP_UI_SEPARATOR | 在菜单中插入分隔线 |
| GNOME_APP_UI_HELP | 创建用于帮助菜单的帮助主题列表 |
| GNOME_APP_UI_BUILDER_DATA | 指定后续条目的构建器数据 |
| GNOME_APP_UI_ITEM_CONFIGURABLE | 可配置的菜单项 |
| GNOME_APP_UI_SUBTREE_STOCK | 与 GNOME_APP_UI_SUBTREE 相同,只是标签文本应在 gnome-libs 目录中查找 |
| GNOME_APP_UI_INCLUDE | 与 GNOME_APP_UI_SUBTREE 相同,只是菜单项包含在当前菜单中,而不是作为子菜单 |

下面是一个创建菜单的示例代码:

#include <gnome.h>
void closeApp ( GtkWidget *window, gpointer data)
{
    gtk_main_quit();
}
void item_clicked(GtkWidget *widget, gpointer user_data)
{
    printf("Item Clicked!\n");
}
static GnomeUIInfo submenu[] = {
    {GNOME_APP_UI_ITEM, "SubMenu", "SubMenu Hint", 
    GTK_SIGNAL_FUNC(item_clicked), NULL, NULL, 0, NULL, 0, 0, NULL},
    {GNOME_APP_UI_ENDOFINFO, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0, 0, NULL}
};
static GnomeUIInfo menu[] = {
    {GNOME_APP_UI_ITEM, "Menu Item 1", "Menu Hint", 
    NULL, NULL, NULL, 0, NULL, 0, 0, NULL},
    {GNOME_APP_UI_SUBTREE, "Menu Item 2", "Menu Hint", submenu, 
    NULL, NULL, 0, NULL, 0, 0, NULL},
    {GNOME_APP_UI_ENDOFINFO, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0, 0, NULL}
};
static GnomeUIInfo menubar[] = {
    {GNOME_APP_UI_SUBTREE, "Toplevel Item", NULL, menu, NULL, 
    NULL, 0, NULL, 0, 0, NULL},
    {GNOME_APP_UI_ENDOFINFO, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0, 0, NULL} 
};
int main (int argc, char *argv[])
{
    GtkWidget *app;
    gnome_program_init ("gnome1", "0.1", LIBGNOMEUI_MODULE,
    argc, argv,
    GNOME_PARAM_NONE);
    app = gnome_app_new("gnome1", "Menus, menus, menus");
    gtk_window_set_default_size ( GTK_WINDOW(app), 300, 200);
    g_signal_connect ( GTK_OBJECT (app), "destroy",
    GTK_SIGNAL_FUNC ( closeApp), NULL);
    gnome_app_create_menus ( GNOME_APP(app), menubar);
    gtk_widget_show(app);
    gtk_main();
    return 0;
}

运行该程序可以看到菜单栏、子菜单和回调函数的效果。

由于 GnomeUIInfo 结构体包含 11 个条目,大多数情况下为 NULL 或零值,编写时容易出错。为了简化操作,GNOME 定义了宏来自动生成这些结构体,并添加图标和键盘快捷键。有两组宏,一组用于定义单个菜单项,另一组用于顶层定义:

#include <libgnomeui/libgnomeui.h>
#define     GNOMEUIINFO_MENU_OPEN_ITEM      (cb, data)
#define     GNOMEUIINFO_MENU_SAVE_ITEM      (cb, data)
#define     GNOMEUIINFO_MENU_SAVE_AS_ITEM   (cb, data)
#define     GNOMEUIINFO_MENU_PRINT_ITEM     (cb, data)
#define     GNOMEUIINFO_MENU_PRINT_SETUP_ITEM(cb, data)
#define     GNOMEUIINFO_MENU_CLOSE_ITEM     (cb, data)
#define     GNOMEUIINFO_MENU_EXIT_ITEM      (cb, data)
#define     GNOMEUIINFO_MENU_QUIT_ITEM      (cb, data)
#define     GNOMEUIINFO_MENU_CUT_ITEM       (cb, data)
#define     GNOMEUIINFO_MENU_COPY_ITEM      (cb, data)
#define     GNOMEUIINFO_MENU_PASTE_ITEM     (cb, data)
#define     GNOMEUIINFO_MENU_SELECT_ALL_ITEM(cb, data)
#define     GNOMEUIINFO_MENU_FILE_TREE      (tree)
#define     GNOMEUIINFO_MENU_EDIT_TREE      (tree)
#define     GNOMEUIINFO_MENU_VIEW_TREE      (tree)
#define     GNOMEUIINFO_MENU_SETTINGS_TREE  (tree)
#define     GNOMEUIINFO_MENU_FILES_TREE     (tree)
#define     GNOMEUIINFO_MENU_WINDOWS_TREE   (tree)
#define     GNOMEUIINFO_MENU_HELP_TREE      (tree)
#define     GNOMEUIINFO_MENU_GAME_TREE      (tree)

下面是使用宏创建菜单的示例:

#include <gnome.h>
static GnomeUIInfo filemenu[] = {
    GNOMEUIINFO_MENU_NEW_ITEM ("New", "Menu Hint", NULL, NULL ),
    GNOMEUIINFO_MENU_OPEN_ITEM (NULL, NULL),
    GNOMEUIINFO_MENU_SAVE_AS_ITEM (NULL, NULL),
    GNOMEUIINFO_SEPARATOR,
    GNOMEUIINFO_MENU_EXIT_ITEM (NULL, NULL),
    GNOMEUIINFO_END
};
static GnomeUIInfo editmenu[] = {
    GNOMEUIINFO_MENU_FIND_ITEM (NULL, NULL),
    GNOMEUIINFO_END
};
static GnomeUIInfo menubar[] = {
    GNOMEUIINFO_MENU_FILE_TREE (filemenu),
    GNOMEUIINFO_MENU_EDIT_TREE (editmenu),
    GNOMEUIINFO_END
};
int main (int argc, char *argv[])
{
    GtkWidget *app, *toolbar;
    gnome_program_init ("gnome1", "0.1", LIBGNOMEUI_MODULE,
    argc, argv,
    GNOME_PARAM_NONE);
    app = gnome_app_new("gnome1", "Menus, menus, menus");
    gtk_window_set_default_size ( GTK_WINDOW(app), 300, 200);
    gnome_app_create_menus ( GNOME_APP(app), menubar);
    gtk_widget_show(app);
    gtk_main();
    return 0;
}

使用宏可以显著减少代码量,使菜单代码更易读。

4. 创建对话框

对话框是 GUI 应用程序中与用户交互和通知重要事件的重要部分。GTK+ 提供了从 GtkWindow 派生的特殊对话框小部件,使编程更加方便。

4.1 GtkDialog

GtkDialog GtkWindow 的子类,继承了其所有功能和属性。它将窗口分为两部分,一部分用于小部件内容,另一部分用于底部的按钮。可以使用 gtk_dialog_new_with_buttons 函数创建对话框:

GtkWidget* gtk_dialog_new_with_buttons (const gchar *title,
GtkWindow *parent,
GtkDialogFlags flags,
const gchar *first_button_text,
...);

该函数创建一个带有标题和按钮的对话框窗口。 parent 参数应指向应用程序的主窗口,以确保对话框与主窗口关联,并在主窗口最小化时一同最小化。 flags 参数决定了对话框的属性组合,可使用按位或运算符进行组合,例如 (GTK_DIALOG_MODAL|GTK_DIALOG_NO_SEPARATOR) 表示既是模态对话框又没有分隔线。

下面是一个创建带有 OK 和 Cancel 按钮的对话框示例:

GtkWidget *dialog = gtk_dialog_new_with_buttons ("Important question",
parent_window,
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
NULL);

创建对话框后,需要向其中添加内容。 GtkDialog 包含一个现成的 GtkVBox 用于填充小部件,可以通过 GTK_DIALOG(dialog)->vbox 获取指针。

4.2 模态对话框

模态对话框会阻止用户在做出响应之前进行其他操作,适用于用户即将进行有严重后果的操作或报告错误和警告消息的情况。可以通过设置 GTK_DIALOG_MODAL 标志并调用 gtk_widget_show 使对话框成为模态对话框,但更好的方法是使用 gtk_dialog_run 函数:

typedef enum
{
    GTK_RESPONSE_NONE = -1,
    GTK_RESPONSE_REJECT = -2,
    GTK_RESPONSE_ACCEPT = -3,
    GTK_RESPONSE_DELETE_EVENT = -4,
    GTK_RESPONSE_OK     = -5,
    GTK_RESPONSE_CANCEL = -6,
    GTK_RESPONSE_CLOSE  = -7,
    GTK_RESPONSE_YES    = -8,
    GTK_RESPONSE_NO     = -9,
    GTK_RESPONSE_APPLY  = -10,
    GTK_RESPONSE_HELP   = -11
} GtkResponseType;
GtkWidget *dialog = create_dialog();
int result = gtk_dialog_run(GTK_DIALOG(dialog));
switch (result)
{
    case GTK_RESPONSE_ACCEPT:
        delete_file();
        break;
    case GTK_RESPONSE_REJECT:
        do_nothing();
        break;
    default:
        dialog_was_cancelled ();
        break;
}
gtk_widget_destroy (dialog);

gtk_dialog_run 函数会停止程序执行,直到用户按下按钮或对话框被销毁,并返回一个整数结果,表示用户按下的按钮。可以使用 switch 语句根据结果执行相应的逻辑。

4.3 非模态对话框

非模态对话框的工作方式略有不同。创建方法与模态对话框相同,但不使用 gtk_dialog_run ,而是连接回调函数到 GtkDialog 的 “response” 信号:

void dialog_button_clicked (GtkWidget *dialog, gint response, gpointer user_data)
{
    switch (response)
    {
        case GTK_RESPONSE_ACCEPT:
            do_stuff();
            break;
        case GTK_RESPONSE_REJECT:
            do_nothing();
            break;
        default:
            dialog_was_cancelled ();
            break;
    }
    gtk_widget_destroy(dialog);
} 
int main()
{
    ...
    GtkWidget *dialog = create_dialog();
    g_signal_connect ( GTK_OBJECT (dialog), "response", 
    GTK_SIGNAL_FUNC (dialog_button_clicked), user_data );
    gtk_widget_show(dialog);
    ... 
}

非模态对话框可能会出现一些问题,因为用户不必立即响应,可能会最小化并忘记对话框。需要考虑用户在关闭第一个实例之前尝试再次打开对话框的情况,可以检查对话框指针是否为 NULL,如果不为 NULL 则调用 gtk_window_present 重新显示现有对话框。

4.4 GtkMessageDialog

对于非常简单的对话框, GtkDialog 可能过于复杂。 GtkMessageDialog 可以在一行代码中创建消息对话框:

GtkWidget* gtk_message_dialog_new (GtkWindow *parent, GtkDialogFlags flags,
GtkMessageType type,
GtkButtonsType buttons,
const gchar *message_format,
...);

该函数创建一个带有图标、标题和可配置按钮的对话框。 type 参数根据对话框的用途设置库存图标和标题,有四种常见类型:
- GTK_MESSAGE_INFO
- GTK_MESSAGE_WARNING
- GTK_MESSAGE_QUESTION
- GTK_MESSAGE_ERROR
还可以选择 GTK_MESSAGE_OTHER 值。 buttons 参数可以传递 GtkButtonsType ,而不是单独列出每个按钮,常见的 GtkButtonsType 如下表所示:
| GtkButtonsType | 描述 |
| — | — |
| GTK_BUTTONS_OK | 一个 OK 按钮 |
| GTK_BUTTONS_CLOSE | 一个 Close 按钮 |
| GTK_BUTTONS_CANCEL | 一个 Cancel 按钮 |
| GTK_BUTTONS_YES_NO | Yes 和 No 按钮 |
| GTK_BUTTONS_OK_CANCEL | OK 和 Cancel 按钮 |
| GTK_BUTTONS_NONE | 没有按钮 |

通过以上介绍,你可以使用 GTK+ 和 GNOME 库创建功能丰富的 GUI 应用程序,包括窗口、菜单和对话框等常见元素。在实际开发中,可以根据需求选择合适的小部件和方法,提高开发效率和用户体验。

基于 GTK+ 的 GNOME 编程入门

5. 总结与实践建议

在前面的内容中,我们详细介绍了如何使用 GTK+ 和 GNOME 库进行 GUI 编程,包括创建窗口、菜单和对话框等重要元素。下面为大家总结一些关键要点,并给出实践建议。

5.1 关键要点总结
  • GTK+ 与 GNOME 特性 :GTK+ 是跨平台的 GUI 工具包,具有桌面中立性,但缺乏与桌面集成的功能;GNOME 库扩展了 GTK+,提供了更易用的小部件和方便的菜单、对话框创建方式。
  • 窗口创建 :使用 GnomeApp 可以轻松创建带有菜单、工具栏和状态栏的窗口,初始化时需要调用 gnome_program_init 函数。
  • 菜单创建 :可以使用 GnomeUIInfo 结构体手动创建菜单,也可以使用 GNOME 提供的宏来简化操作,使代码更易读和维护。
  • 对话框创建 :GTK+ 提供了 GtkDialog GtkMessageDialog 用于创建不同类型的对话框,包括模态和非模态对话框。模态对话框使用 gtk_dialog_run 函数,非模态对话框则通过连接回调函数到 “response” 信号来处理用户响应。
5.2 实践建议
  • 代码复用 :在创建菜单和对话框时,尽量使用 GNOME 提供的宏和函数,避免手动编写复杂的结构体和代码,提高代码的复用性和可维护性。
  • 错误处理 :在使用对话框时,要考虑各种可能的用户响应,使用 switch 语句进行全面的错误处理,确保程序的健壮性。
  • 用户体验 :合理使用模态和非模态对话框,根据不同的场景选择合适的对话框类型,以提高用户体验。例如,在用户进行重要操作时使用模态对话框,避免用户误操作。
6. 流程图与操作步骤梳理

为了帮助大家更好地理解和应用前面介绍的知识,下面为大家提供创建菜单和对话框的流程图以及详细操作步骤。

6.1 创建菜单的流程图
graph TD;
    A[初始化 GNOME 库] --> B[定义菜单项结构体数组];
    B --> C[定义菜单栏结构体数组];
    C --> D[创建 GnomeApp 窗口];
    D --> E[设置窗口大小和关闭回调];
    E --> F[使用 gnome_app_create_menus 创建菜单];
    F --> G[显示窗口并进入主循环];
6.2 创建菜单的操作步骤
  1. 初始化 GNOME 库 :调用 gnome_program_init 函数进行初始化。
  2. 定义菜单项结构体数组 :可以使用 GnomeUIInfo 结构体手动定义,也可以使用 GNOME 提供的宏。
  3. 定义菜单栏结构体数组 :引用菜单项结构体数组来定义菜单栏。
  4. 创建 GnomeApp 窗口 :使用 gnome_app_new 函数创建窗口。
  5. 设置窗口大小和关闭回调 :使用 gtk_window_set_default_size 设置窗口大小,使用 g_signal_connect 连接关闭回调函数。
  6. 创建菜单 :使用 gnome_app_create_menus 函数创建菜单。
  7. 显示窗口并进入主循环 :使用 gtk_widget_show 显示窗口,使用 gtk_main 进入主循环。
6.3 创建对话框的流程图
graph TD;
    A[创建对话框] --> B{模态对话框?};
    B -- 是 --> C[使用 gtk_dialog_run 等待用户响应];
    B -- 否 --> D[连接回调函数到“response”信号];
    C --> E[根据响应结果执行相应逻辑];
    D --> E;
    E --> F[销毁对话框];
6.4 创建对话框的操作步骤
  1. 创建对话框 :使用 gtk_dialog_new_with_buttons gtk_message_dialog_new 函数创建对话框。
  2. 判断对话框类型 :如果是模态对话框,使用 gtk_dialog_run 函数等待用户响应;如果是非模态对话框,连接回调函数到 “response” 信号。
  3. 处理用户响应 :根据用户按下的按钮,使用 switch 语句执行相应的逻辑。
  4. 销毁对话框 :使用 gtk_widget_destroy 函数销毁对话框。

通过以上的总结、建议和流程图,相信大家对使用 GTK+ 和 GNOME 库进行 GUI 编程有了更清晰的认识。在实际开发中,不断实践和探索,将这些知识运用到项目中,提高自己的编程能力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值