48、使用GNOME/GTK+为CD数据库创建GUI应用

使用GNOME/GTK+为CD数据库创建GUI应用

1. 引言

在开发CD数据库应用时,为其添加一个图形用户界面(GUI)可以极大地提升用户体验。本文将介绍如何使用GNOME/GTK+库为CD数据库创建一个功能丰富的GUI应用,包括登录数据库、搜索CD、显示CD和曲目信息、添加CD到数据库、创建关于窗口以及确认用户退出等功能。

2. 准备工作

在开始之前,需要确保已经安装了MySQL数据库和MySQL开发者库。示例程序将使用之前开发的CD数据库以及相关的接口函数。

3. 代码结构

代码将分为三个源文件,并共享一个公共头文件 cdapp_gnome.h 。源文件将创建窗口和对话框的函数(界面生成函数)与回调函数分开。

3.1 cdapp_gnome.h

该头文件包含了程序所需的各种函数声明和枚举定义。

#include <gnome.h>
#include "app_mysql.h"

enum {
    COLUMN_TITLE,
    COLUMN_ARTIST,
    COLUMN_CATALOGUE,
    N_COLUMNS
};

GtkWidget *create_main_window();
GtkWidget *create_login_dialog();
GtkWidget *create_addcd_dialog();

/* Callback to quit application */
void quit_app( GtkWidget * window, gpointer data);
/* Callback useful for confirming exit before quitting */
gboolean delete_event_handler ( GtkWidget *window, GdkEvent *event, gpointer data);
/* Callback connected to ‘response’ signal of addcd dialog */
void addcd_dialog_button_clicked (GtkDialog * dialog, gint response, 
gpointer userdata);
/* Callback for menu and toolbar ‘Add CD’ button */
void on_addcd_activate (GtkWidget *widget, gpointer user_data);
/* Callback for menu ‘About’ button */
void on_about_activate (GtkWidget  *widget, gpointer user_data);
/* Callback for search button */
void on_search_button_clicked (GtkWidget *widget, gpointer userdata);
3.2 主要功能概述
  • 登录数据库 :用户需要通过登录对话框输入用户名和密码来登录数据库。
  • 搜索CD :用户可以在搜索框中输入关键词,点击搜索按钮来查找匹配的CD。
  • 显示CD和曲目信息 :使用 GtkTreeView 控件显示搜索结果和CD的详细信息。
  • 添加CD到数据库 :通过添加CD对话框,用户可以输入CD的艺术家、标题和目录信息,并将其添加到数据库中。
  • 创建关于窗口 :点击“关于”按钮,将弹出一个标准的GNOME关于框。
  • 确认用户退出 :在用户尝试退出应用程序时,会弹出确认对话框。
4. 界面生成函数(interface.c)

该文件定义了应用程序中使用的窗口和对话框。

4.1 全局变量和辅助函数
#include "app_gnome.h"
GtkWidget *treeview;
GtkWidget *appbar;
GtkWidget *artist_entry;
GtkWidget *title_entry;
GtkWidget *catalogue_entry;
GtkWidget *username_entry;
GtkWidget *password_entry;
static GtkWidget *app;

void add_widget_with_label ( GtkContainer *box, gchar *caption, GtkWidget *widget)
{
    GtkWidget *label = gtk_label_new (caption);
    GtkWidget *hbox = gtk_hbox_new (TRUE, 4);
    gtk_container_add(GTK_CONTAINER (hbox), label);
    gtk_container_add(GTK_CONTAINER (hbox), widget);
    gtk_container_add(box, hbox);
}
4.2 菜单栏定义
static GnomeUIInfo filemenu[] =
{
    GNOMEUIINFO_MENU_NEW_ITEM ("_New CD", NULL, on_addcd_activate, NULL),
    GNOMEUIINFO_SEPARATOR,
    GNOMEUIINFO_MENU_EXIT_ITEM (close_app, NULL),
    GNOMEUIINFO_END
};

static GnomeUIInfo helpmenu[] =
{
    GNOMEUIINFO_MENU_ABOUT_ITEM (on_about_activate, NULL),
    GNOMEUIINFO_END
};

static GnomeUIInfo menubar[] =
{
    GNOMEUIINFO_MENU_FILE_TREE (filemenu),
    GNOMEUIINFO_MENU_HELP_TREE (helpmenu),
    GNOMEUIINFO_END
};
4.3 创建主窗口
GtkWidget * create_main_window()
{
    GtkWidget *toolbar;
    GtkWidget *vbox;
    GtkWidget *hbox;
    GtkWidget *label;
    GtkWidget *entry;
    GtkWidget *search_button;
    GtkWidget *scrolledwindow;
    GtkCellRenderer *renderer;

    app = gnome_app_new ("GnomeCD", "CD Database");
    gtk_window_set_position ( GTK_WINDOW( app), GTK_WIN_POS_CENTER);
    gtk_window_set_default_size ( GTK_WINDOW( app ), 540, 480);
    gnome_app_create_menus (GNOME_APP (app), menubar);

    toolbar = gtk_toolbar_new ();
    gnome_app_add_toolbar (GNOME_APP (app), GTK_TOOLBAR (toolbar), "toolbar",
    BONOBO_DOCK_ITEM_BEH_EXCLUSIVE,
    BONOBO_DOCK_TOP, 1, 0, 0);
    gtk_container_set_border_width (GTK_CONTAINER (toolbar), 1);
    gtk_toolbar_insert_stock (GTK_TOOLBAR (toolbar),
    "gtk-add",
    "Add new CD",
    NULL, GTK_SIGNAL_FUNC (on_addcd_activate), 
    NULL, -1);
    gtk_toolbar_insert_space (GTK_TOOLBAR (toolbar), 1);
    gtk_toolbar_insert_stock (GTK_TOOLBAR (toolbar),
    "gtk-quit",
    "Quit the Application",
    NULL, GTK_SIGNAL_FUNC (on_quit_activate), 
    NULL, -1);

    label = gtk_label_new("Search String:");
    entry = gtk_entry_new ();
    search_button = gtk_button_new_with_label("Search");

    scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
    GTK_POLICY_AUTOMATIC,
    GTK_POLICY_AUTOMATIC);

    vbox = gtk_vbox_new (FALSE, 0);
    hbox = gtk_hbox_new (FALSE, 0);
    gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 5);  
    gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 5); 
    gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 6);  
    gtk_box_pack_start (GTK_BOX (hbox), search_button, FALSE, FALSE, 5); 
    gtk_box_pack_start (GTK_BOX (vbox), scrolledwindow, TRUE, TRUE, 0);

    treeview = gtk_tree_view_new();
    renderer = gtk_cell_renderer_text_new ();
    gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(treeview),
    COLUMN_TITLE,
    "Title", renderer,
    "text", COLUMN_TITLE,
    NULL);
    gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(treeview),
    COLUMN_ARTIST,
    "Artist", renderer,
    "text", COLUMN_ARTIST,
    NULL);
    gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(treeview),
    COLUMN_CATALOGUE,
    "Catalogue", renderer,
    "text", COLUMN_CATALOGUE,
    NULL);
    gtk_tree_view_set_search_column (GTK_TREE_VIEW (treeview),
    COLUMN_TITLE);
    gtk_container_add (GTK_CONTAINER (scrolledwindow), treeview);

    gnome_app_set_contents (GNOME_APP (app), vbox);
    appbar = gnome_appbar_new (FALSE, TRUE, GNOME_PREFERENCES_NEVER);
    gnome_app_set_statusbar (GNOME_APP (app), appbar);
    gnome_app_install_menu_hints (GNOME_APP (app), menubar);
    g_signal_connect (GTK_OBJECT (search_button), "clicked",
    GTK_SIGNAL_FUNC (on_search_button_clicked),
    entry);
    g_signal_connect (GTK_OBJECT(app), "delete_event",
    GTK_SIGNAL_FUNC ( delete_event_handler ),
    NULL);
    g_signal_connect (GTK_OBJECT(app), "destroy",
    GTK_SIGNAL_FUNC ( quit_app ), NULL);

    return app;
}
4.4 创建添加CD对话框
GtkWidget *create_addcd_dialog()
{
    artist_entry = gtk_entry_new();
    title_entry = gtk_entry_new();
    catalogue_entry = gtk_entry_new();

    GtkWidget *dialog =  gtk_dialog_new_with_buttons ("Add CD",
    app,
    GTK_DIALOG_DESTROY_WITH_PARENT,
    GTK_STOCK_OK,
    GTK_RESPONSE_ACCEPT,
    GTK_STOCK_CANCEL,
    GTK_RESPONSE_REJECT,
    NULL);

    add_widget_with_label ( GTK_CONTAINER (GTK_DIALOG (dialog)->vbox),
    "Artist", artist_entry);
    add_widget_with_label ( GTK_CONTAINER (GTK_DIALOG (dialog)->vbox),
    "Title", title_entry);
    add_widget_with_label ( GTK_CONTAINER (GTK_DIALOG (dialog)->vbox),
    "Catalogue", catalogue_entry);

    g_signal_connect ( GTK_OBJECT (dialog), "response",
    GTK_SIGNAL_FUNC (addcd_dialog_button_clicked), NULL);

    return dialog;
}
4.5 创建登录对话框
GtkWidget *create_login_dialog()
{
    GtkWidget *dialog = gtk_dialog_new_with_buttons ("Database Login",
    app,
    GTK_DIALOG_MODAL,
    GTK_STOCK_OK,
    GTK_RESPONSE_ACCEPT,
    GTK_STOCK_CANCEL,
    GTK_RESPONSE_REJECT,
    NULL);

    username_entry = gtk_entry_new();
    password_entry = gtk_entry_new();
    gtk_entry_set_visibility(GTK_ENTRY (password_entry), FALSE);

    add_widget_with_label ( GTK_CONTAINER (GTK_DIALOG (dialog)->vbox) , "Username",
    username_entry);
    add_widget_with_label ( GTK_CONTAINER (GTK_DIALOG (dialog)->vbox) , "Password",
    password_entry);

    gtk_widget_show_all(GTK_WIDGET (GTK_DIALOG (dialog)->vbox));

    return dialog;
}
5. 回调函数(callbacks.c)

该文件包含了UI控件的回调函数。

5.1 包含头文件和引用全局变量
#include "app_gnome.h"
extern GtkWidget *treeview;
extern GtkWidget *app;
extern GtkWidget *appbar;
extern GtkWidget *artist_entry;
extern GtkWidget *title_entry;
extern GtkWidget *catalogue_entry;
static GtkWidget *addcd_dialog;
5.2 退出应用程序回调函数
void quit_app( GtkWidget *window, gpointer data)
{
    database_end();
    gtk_main_quit();
}
5.3 确认退出回调函数
gboolean confirm_exit()
{
    gint result;
    GtkWidget *dialog = gtk_message_dialog_new (NULL,
    GTK_DIALOG_MODAL,
    GTK_MESSAGE_QUESTION,
    GTK_BUTTONS_YES_NO,
    "Are you sure you want to quit?");
    result = gtk_dialog_run (GTK_DIALOG (dialog));
    gtk_widget_destroy (dialog);
    return (result == GTK_RESPONSE_YES);
}

gboolean delete_event_handler ( GtkWidget *window, GdkEvent *event, gpointer data)
{
    return !confirm_exit();
}
5.4 添加CD对话框按钮点击回调函数
void addcd_dialog_button_clicked (GtkDialog * dialog, gint response, 
gpointer userdata)
{
    const gchar *artist_const;
    const gchar *title_const;
    const gchar *catalogue_const;
    gchar artist[200];
    gchar title[200];
    gchar catalogue[200];
    gint *cd_id;

    if (response == GTK_RESPONSE_ACCEPT)
    {
        artist_const = gtk_entry_get_text(GTK_ENTRY (artist_entry));
        title_const =  gtk_entry_get_text(GTK_ENTRY (title_entry));
        catalogue_const = gtk_entry_get_text(GTK_ENTRY (catalogue_entry));
        strcpy(artist, artist_const);
        strcpy(title, title_const);
        strcpy(catalogue, catalogue_const);
        add_cd(artist, title, catalogue, cd_id);
    }

    addcd_dialog = NULL;
    gtk_widget_destroy(GTK_WIDGET(dialog));
}
5.5 搜索按钮点击回调函数
void on_search_button_clicked (GtkButton *button, gpointer userdata)
{
    struct cd_search_st cd_res;
    struct current_cd_st cd;
    struct current_tracks_st ct;
    gint res1, res2, res3;
    gchar track_title[110];
    const gchar *search_string_const;
    gchar search_string[200];
    gchar search_text[200];
    gint i = 0, j = 0;
    GtkTreeStore *tree_store;
    GtkTreeIter parent_iter, child_iter;

    memset(&track_title, 0, sizeof(track_title));

    search_string_const = gtk_entry_get_text(GTK_ENTRY (userdata));
    strcpy (search_string, search_string_const);
    res1 = find_cds(search_string, &cd_res);

    sprintf(search_text, " Displaying %d result(s) for search string ‘ %s ‘", 
    MIN(res1, MAX_CD_RESULT), search_string);
    gnome_appbar_push (GNOME_APPBAR( appbar), search_text);

    tree_store = gtk_tree_store_new (N_COLUMNS,
    G_TYPE_STRING,
    G_TYPE_STRING,
    G_TYPE_STRING);

    while (i < res1 && i < MAX_CD_RESULT)
    {
        res2 = get_cd(cd_res.cd_id[i], &cd);
        gtk_tree_store_append (tree_store, &parent_iter, NULL);

        gtk_tree_store_set (tree_store, &parent_iter,
        COLUMN_TITLE, cd.title,
        COLUMN_ARTIST, cd.artist_name,
        COLUMN_CATALOGUE, cd.catalogue, -1
        );

        res3 = get_cd_tracks(cd_res.cd_id[i++], &ct);
        j = 0;
        while (j < res3)
        {
            sprintf(track_title, " Track %d. ", j+1);
            strcat(track_title, ct.track[j++]);
            gtk_tree_store_append (tree_store, &child_iter, &parent_iter);
            gtk_tree_store_set (tree_store, &child_iter,
            COLUMN_TITLE, track_title, -1);
        }
    }

    gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL(tree_store));
}
5.6 添加CD激活回调函数
void on_addcd_activate (GtkMenuItem * menuitem, gpointer user_data)
{
    if (addcd_dialog != NULL)
        return;
    addcd_dialog = create_addcd_dialog();
    gtk_widget_show_all (addcd_dialog);
}

gboolean close_app ( GtkWidget * window, gpointer data)
{
    gboolean exit;
    if ((exit = confirm_exit()))
    {
        quit_app(NULL, NULL);
    }
    return exit;
}
5.7 关于按钮点击回调函数
void on_about_activate (GtkMenuItem  * menuitem, gpointer user_data)
{
    const char * authors[] = {"Wrox Press", NULL};
    GtkWidget *about = gnome_about_new ("CD Database", "1.0",
    "(c) Wrox Press",
    "Beginning Linux Programming",
    (const char ** ) authors, NULL,
    "Translators", NULL);
    gtk_widget_show(about);
}
6. 主函数(main.c)

该文件包含了程序的主函数。

#include <stdio.h>
#include <stdlib.h>
#include "app_gnome.h"
extern GtkWidget *username_entry;
extern GtkWidget *password_entry;

gint main(gint argc, gchar *argv[])
{
    GtkWidget *main_window;
    GtkWidget *login_dialog; 
    const char *user_const;
    const char *pass_const;
    gchar username[100];
    gchar password[100]; 
    gint result;

    gnome_program_init ("CdDatabase", "0.1", LIBGNOMEUI_MODULE,
    argc, argv,
    GNOME_PARAM_APP_DATADIR, "",
    NULL);

    main_window = create_main_window();
    gtk_widget_show_all(main_window);
    login_dialog = create_login_dialog();

    while (1)
    {   
        result = gtk_dialog_run (GTK_DIALOG (login_dialog));
        if (result != GTK_RESPONSE_ACCEPT)
        {
            if (confirm_exit())
                return 0;
            else
                continue;
        }

        user_const = gtk_entry_get_text(GTK_ENTRY (username_entry));
        pass_const = gtk_entry_get_text(GTK_ENTRY (password_entry));
        strcpy(username, user_const);
        strcpy(password, pass_const);

        if (database_start(username, password) == TRUE)
            break;

        GtkWidget * error_dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
        GTK_DIALOG_DESTROY_WITH_PARENT,
        GTK_MESSAGE_ERROR,
        GTK_BUTTONS_CLOSE,
        "Could not log on! - Check Username and Password");
        gtk_dialog_run (GTK_DIALOG (error_dialog));
        gtk_widget_destroy (error_dialog);
    }

    gtk_widget_destroy (login_dialog);
    gtk_main();

    return 0;
}
7. 编译和运行

编写一个 Makefile 来编译应用程序。

all:  app
app: app_mysql.c callbacks.c interface.c main.c app_gnome.h app_mysql.h
    gcc -o app -I/usr/include/mysql app_mysql.c callbacks.c interface.c main.c -
    lmysqlclient `pkg-config --cflags --libs libgnome-2.0 libgnomeui-2.0`
clean:
    rm -f app

使用 make -f Makefile 命令编译CD应用程序。编译完成后,运行 app 即可启动GNOME风格的CD应用程序。

8. 总结

通过使用GNOME/GTK+库,我们成功地为CD数据库创建了一个功能丰富的GUI应用程序。该应用程序允许用户登录数据库、搜索CD、显示CD和曲目信息、添加CD到数据库、查看关于信息以及确认退出。通过将界面生成函数和回调函数分开,代码结构更加清晰,易于维护和扩展。

附录:流程图

graph TD;
    A[开始] --> B[初始化GNOME库];
    B --> C[创建主窗口];
    C --> D[显示主窗口];
    D --> E[创建登录对话框];
    E --> F{登录成功?};
    F -- 是 --> G[进入主界面];
    F -- 否 --> H[显示错误信息];
    H --> E;
    G --> I{用户操作};
    I -- 搜索CD --> J[执行搜索操作];
    I -- 添加CD --> K[打开添加CD对话框];
    I -- 关于 --> L[显示关于信息];
    I -- 退出 --> M[确认退出];
    M -- 是 --> N[关闭数据库并退出];
    M -- 否 --> G;
    J --> O[显示搜索结果];
    K --> P{添加成功?};
    P -- 是 --> G;
    P -- 否 --> K;

表格:主要功能和对应文件

功能 对应文件
登录数据库 main.c, interface.c
搜索CD callbacks.c
显示CD和曲目信息 callbacks.c, interface.c
添加CD到数据库 callbacks.c, interface.c
创建关于窗口 callbacks.c
确认用户退出 callbacks.c

使用GNOME/GTK+为CD数据库创建GUI应用

9. 代码详细解释
9.1 头文件 cdapp_gnome.h
  • 包含必要的头文件 :引入了GNOME相关的头文件和之前开发的数据库接口函数头文件。
#include <gnome.h>
#include "app_mysql.h"
  • 定义枚举 :为 GtkTreeView 控件的列进行编号,方便后续操作。
enum {
    COLUMN_TITLE,
    COLUMN_ARTIST,
    COLUMN_CATALOGUE,
    N_COLUMNS
};
  • 声明函数 :声明了窗口创建函数和回调函数,将界面生成和回调处理分开。
GtkWidget *create_main_window();
GtkWidget *create_login_dialog();
GtkWidget *create_addcd_dialog();

/* Callback to quit application */
void quit_app( GtkWidget * window, gpointer data);
/* Callback useful for confirming exit before quitting */
gboolean delete_event_handler ( GtkWidget *window, GdkEvent *event, gpointer data);
/* Callback connected to ‘response’ signal of addcd dialog */
void addcd_dialog_button_clicked (GtkDialog * dialog, gint response, 
gpointer userdata);
/* Callback for menu and toolbar ‘Add CD’ button */
void on_addcd_activate (GtkWidget *widget, gpointer user_data);
/* Callback for menu ‘About’ button */
void on_about_activate (GtkWidget  *widget, gpointer user_data);
/* Callback for search button */
void on_search_button_clicked (GtkWidget *widget, gpointer userdata);
9.2 界面生成函数 interface.c
  • 全局变量 :定义了一些全局的控件指针,方便在不同文件中使用。
GtkWidget *treeview;
GtkWidget *appbar;
GtkWidget *artist_entry;
GtkWidget *title_entry;
GtkWidget *catalogue_entry;
GtkWidget *username_entry;
GtkWidget *password_entry;
static GtkWidget *app;
  • 辅助函数 add_widget_with_label 函数用于将标签和控件组合在一起。
void add_widget_with_label ( GtkContainer *box, gchar *caption, GtkWidget *widget)
{
    GtkWidget *label = gtk_label_new (caption);
    GtkWidget *hbox = gtk_hbox_new (TRUE, 4);
    gtk_container_add(GTK_CONTAINER (hbox), label);
    gtk_container_add(GTK_CONTAINER (hbox), widget);
    gtk_container_add(box, hbox);
}
  • 菜单栏定义 :使用 GnomeUIInfo 宏定义了文件菜单和帮助菜单。
static GnomeUIInfo filemenu[] =
{
    GNOMEUIINFO_MENU_NEW_ITEM ("_New CD", NULL, on_addcd_activate, NULL),
    GNOMEUIINFO_SEPARATOR,
    GNOMEUIINFO_MENU_EXIT_ITEM (close_app, NULL),
    GNOMEUIINFO_END
};

static GnomeUIInfo helpmenu[] =
{
    GNOMEUIINFO_MENU_ABOUT_ITEM (on_about_activate, NULL),
    GNOMEUIINFO_END
};

static GnomeUIInfo menubar[] =
{
    GNOMEUIINFO_MENU_FILE_TREE (filemenu),
    GNOMEUIINFO_MENU_HELP_TREE (helpmenu),
    GNOMEUIINFO_END
};
  • 创建主窗口 :创建主窗口并设置其属性,添加菜单栏、工具栏、搜索框和 GtkTreeView 控件。
GtkWidget * create_main_window()
{
    // ... 代码省略 ...
    return app;
}
  • 创建添加CD对话框 :创建一个对话框,包含艺术家、标题和目录的输入框,以及确认和取消按钮。
GtkWidget *create_addcd_dialog()
{
    // ... 代码省略 ...
    return dialog;
}
  • 创建登录对话框 :创建一个模态对话框,用于用户输入用户名和密码。
GtkWidget *create_login_dialog()
{
    // ... 代码省略 ...
    return dialog;
}
9.3 回调函数 callbacks.c
  • 包含头文件和引用全局变量 :引入头文件并引用在 interface.c 中定义的全局变量。
#include "app_gnome.h"
extern GtkWidget *treeview;
extern GtkWidget *app;
extern GtkWidget *appbar;
extern GtkWidget *artist_entry;
extern GtkWidget *title_entry;
extern GtkWidget *catalogue_entry;
static GtkWidget *addcd_dialog;
  • 退出应用程序回调函数 :在退出应用程序时,关闭数据库并终止GTK主循环。
void quit_app( GtkWidget *window, gpointer data)
{
    database_end();
    gtk_main_quit();
}
  • 确认退出回调函数 :弹出确认对话框,根据用户的选择返回相应的布尔值。
gboolean confirm_exit()
{
    // ... 代码省略 ...
    return (result == GTK_RESPONSE_YES);
}

gboolean delete_event_handler ( GtkWidget *window, GdkEvent *event, gpointer data)
{
    return !confirm_exit();
}
  • 添加CD对话框按钮点击回调函数 :当用户点击添加CD对话框的确认按钮时,将输入的信息添加到数据库中。
void addcd_dialog_button_clicked (GtkDialog * dialog, gint response, 
gpointer userdata)
{
    // ... 代码省略 ...
    gtk_widget_destroy(GTK_WIDGET(dialog));
}
  • 搜索按钮点击回调函数 :根据用户输入的搜索关键词,执行搜索操作并将结果显示在 GtkTreeView 控件中。
void on_search_button_clicked (GtkButton *button, gpointer userdata)
{
    // ... 代码省略 ...
    gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL(tree_store));
}
  • 添加CD激活回调函数 :如果添加CD对话框未打开,则打开该对话框。
void on_addcd_activate (GtkMenuItem * menuitem, gpointer user_data)
{
    if (addcd_dialog != NULL)
        return;
    addcd_dialog = create_addcd_dialog();
    gtk_widget_show_all (addcd_dialog);
}

gboolean close_app ( GtkWidget * window, gpointer data)
{
    gboolean exit;
    if ((exit = confirm_exit()))
    {
        quit_app(NULL, NULL);
    }
    return exit;
}
  • 关于按钮点击回调函数 :点击“关于”按钮时,显示标准的GNOME关于框。
void on_about_activate (GtkMenuItem  * menuitem, gpointer user_data)
{
    const char * authors[] = {"Wrox Press", NULL};
    GtkWidget *about = gnome_about_new ("CD Database", "1.0",
    "(c) Wrox Press",
    "Beginning Linux Programming",
    (const char ** ) authors, NULL,
    "Translators", NULL);
    gtk_widget_show(about);
}
9.4 主函数 main.c
  • 包含头文件和引用全局变量 :引入必要的头文件并引用用户名和密码输入框。
#include <stdio.h>
#include <stdlib.h>
#include "app_gnome.h"
extern GtkWidget *username_entry;
extern GtkWidget *password_entry;
  • 主函数 :初始化GNOME库,创建主窗口和登录对话框,处理用户登录操作。
gint main(gint argc, gchar *argv[])
{
    // ... 代码省略 ...
    return 0;
}
10. 注意事项和扩展建议
10.1 注意事项
  • 数据库依赖 :确保MySQL数据库和MySQL开发者库已正确安装,并且数据库连接参数在代码中配置正确。
  • 内存管理 :在使用 GtkWidget 和其他动态分配的资源时,要注意及时释放内存,避免内存泄漏。
  • 错误处理 :在数据库操作和GTK函数调用时,要进行错误检查,确保程序的健壮性。
10.2 扩展建议
  • 功能扩展 :可以添加更多的功能,如编辑CD信息、删除CD、导出CD信息等。
  • 界面优化 :可以对界面进行美化,如调整字体、颜色、布局等,提升用户体验。
  • 多语言支持 :可以添加多语言支持,使应用程序能够在不同语言环境下使用。
11. 总结回顾

本文详细介绍了如何使用GNOME/GTK+库为CD数据库创建一个功能丰富的GUI应用程序。通过将界面生成函数和回调函数分开,代码结构更加清晰,易于维护和扩展。主要实现了以下功能:
- 登录数据库
- 搜索CD
- 显示CD和曲目信息
- 添加CD到数据库
- 查看关于信息
- 确认退出

附录:详细流程图

graph TD;
    A[开始] --> B[初始化GNOME库];
    B --> C[创建主窗口];
    C --> D[显示主窗口];
    D --> E[创建登录对话框];
    E --> F{用户点击登录};
    F -- 是 --> G[获取用户名和密码];
    G --> H{验证用户名和密码};
    H -- 成功 --> I[进入主界面];
    H -- 失败 --> J[显示错误信息];
    J --> E;
    I --> K{用户操作};
    K -- 搜索CD --> L[获取搜索关键词];
    L --> M[执行搜索操作];
    M --> N[显示搜索结果];
    K -- 添加CD --> O[打开添加CD对话框];
    O --> P{用户输入信息并点击确认};
    P -- 是 --> Q[获取输入信息];
    Q --> R{验证输入信息};
    R -- 有效 --> S[添加CD到数据库];
    S --> T{添加成功};
    T -- 是 --> I;
    T -- 否 --> U[显示添加失败信息];
    U --> O;
    R -- 无效 --> V[显示输入错误信息];
    V --> O;
    P -- 否 --> I;
    K -- 关于 --> W[显示关于信息];
    K -- 退出 --> X[确认退出];
    X -- 是 --> Y[关闭数据库并退出];
    X -- 否 --> I;

表格:代码文件和功能模块对应关系

代码文件 功能模块
cdapp_gnome.h 定义全局常量、枚举和函数声明
interface.c 创建主窗口、登录对话框和添加CD对话框
callbacks.c 处理界面控件的回调函数
main.c 程序入口,初始化GNOME库和处理用户登录
app_mysql.c 数据库操作接口
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值