46、GTK+ 编程入门:基础组件与交互实现

GTK+ 编程入门:基础组件与交互实现

1. GTK+ 窗口创建与显示

在 GTK+ 编程中,窗口创建是构建图形用户界面(GUI)的基础。 gtk_window_new 函数用于在内存中创建窗口, GTK_WINDOW_TOPLEVEL 是最常用的窗口类型,而 GTK_WINDOW_POPUP 是无框架窗口,适用于对话框,但通常有更便捷的方式创建对话框。

#include <gtk/gtk.h>

int main (int argc, char *argv[])
{
    GtkWidget *window;
    gtk_init(&argc, &argv);
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_widget_show(window);
    gtk_main ();
    return 0;
}

上述代码中, gtk_init 初始化 GTK+ 库, gtk_window_new 创建窗口, gtk_widget_show 使窗口显示在屏幕上, gtk_main 启动交互过程,直到调用 gtk_main_quit 才会返回。

2. 事件、信号与回调函数

所有 GUI 库都需要一种机制来响应用户操作执行代码。在 GTK+ 中,使用信号和回调函数来实现这一功能。当用户输入(如鼠标点击或键盘按键)发生时, GtkObject 会发出信号,与之连接的函数(回调函数)会被调用。

回调函数原型通常如下:

void a_callback_function ( GtkWidget *widget, gpointer user_data);

连接回调函数使用 g_signal_connect 函数:

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

以下是一个简单的示例,在窗口中添加一个按钮,点击按钮时打印消息:

#include <gtk/gtk.h>
#include <stdio.h>

static int count = 0;

void button_clicked(GtkWidget *button, gpointer data)
{
    printf("%s pressed %d time(s) \n", (char *) data, ++count);
}

int main (int argc, char *argv[])
{
    GtkWidget *window;
    GtkWidget *button;
    gtk_init(&argc, &argv);
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    button = gtk_button_new_with_label("Hello World!");
    gtk_container_add(GTK_CONTAINER(window), button);
    g_signal_connect(GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (button_clicked), "Button 1");
    gtk_widget_show(button);
    gtk_widget_show(window);
    gtk_main ();
    return 0;
}
3. 容器与布局管理

为了创建一个在不同系统上都能保持一致的 GUI,需要使用灵活的布局系统。GTK+ 提供了容器小部件来控制窗口中其他小部件的布局,其中盒式容器(Box Widgets)是非常有用的一种。

盒式容器有两种主要子类:
- GtkHBox :单行水平排列的盒式容器。
- GtkVBox :单列垂直排列的盒式容器。

创建盒式容器时,需要指定两个参数 homogeneous spacing

GtkWidget* gtk_hbox_new (gboolean homogeneous, gint spacing);
GtkWidget* gtk_vbox_new (gboolean homogeneous, gint spacing);

添加小部件到盒式容器使用 gtk_box_pack_start gtk_box_pack_end 函数:

void gtk_box_pack_start (GtkBox *box, GtkWidget *child, gboolean expand, gboolean fill, guint padding);
void gtk_box_pack_end (GtkBox *box, GtkWidget *child, gboolean expand, gboolean fill, guint padding);

以下是一个使用 GtkHBox GtkVBox 布局 GtkLabel 小部件的示例:

#include <gtk/gtk.h>

void closeApp ( GtkWidget *window, gpointer data)
{
    gtk_main_quit();
}

gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
{
    printf("In delete_event\n");
    return FALSE;
}

int main (int argc, char *argv[])
{
    GtkWidget *window;
    GtkWidget *label1, *label2, *label3;
    GtkWidget *hbox;
    GtkWidget *vbox;
    gtk_init(&argc, &argv);
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "The Window Title");
    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
    gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
    g_signal_connect ( GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC ( closeApp), NULL);
    g_signal_connect ( GTK_OBJECT (window), "delete_event", GTK_SIGNAL_FUNC ( delete_event), NULL);
    label1 = gtk_label_new("Label 1");
    label2 = gtk_label_new("Label 2");
    label3 = gtk_label_new("Label 3");
    hbox = gtk_hbox_new ( TRUE, 5 );
    vbox = gtk_vbox_new ( FALSE, 10);
    gtk_box_pack_start(GTK_BOX(vbox), label1, TRUE, FALSE, 5);
    gtk_box_pack_start(GTK_BOX(vbox), label2, TRUE, FALSE, 5);
    gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 5);
    gtk_box_pack_start(GTK_BOX(hbox), label3, FALSE, FALSE, 5);
    gtk_container_add(GTK_CONTAINER(window), hbox);
    gtk_widget_show_all(window);
    gtk_main ();
    return 0;
}
4. 常见 GTK+ 小部件
4.1 GtkWindow

GtkWindow 是所有 GTK+ 应用程序的基本元素,有许多 API 调用,以下是一些值得关注的函数:
| 函数 | 描述 |
| ---- | ---- |
| GtkWidget* gtk_window_new (GtkWindowType type); | 创建新窗口 |
| void gtk_window_set_title (GtkWindow *window, const gchar *title); | 设置窗口标题 |
| void gtk_window_set_position (GtkWindow *window, GtkWindowPosition position); | 设置窗口初始位置 |
| void gtk_window_set_default_size (GtkWindow *window, gint width, gint height); | 设置窗口默认大小 |

窗口位置参数有以下几种取值:
| 位置参数 | 描述 |
| ---- | ---- |
| GTK_WIN_POS_NONE | 由窗口管理器决定位置 |
| GTK_WIN_POS_CENTER | 窗口居中显示 |
| GTK_WIN_POS_MOUSE | 窗口位于鼠标指针位置 |
| GTK_WIN_POS_CENTER_ALWAYS | 窗口始终居中 |
| GTK_WIN_POS_CENTER_ON_PARENT | 窗口在父窗口中居中(适用于对话框) |

4.2 GtkEntry

GtkEntry 是单行文本输入小部件,常用于输入简单的文本信息,如电子邮件地址、用户名或主机名。以下是一些常用的 GtkEntry 函数:
| 函数 | 描述 |
| ---- | ---- |
| GtkWidget* gtk_entry_new (void); | 创建新的 GtkEntry |
| GtkWidget* gtk_entry_new_with_max_length (gint max); | 创建有最大输入长度限制的 GtkEntry |
| G_CONST_RETURN gchar* gtk_entry_get_text (GtkEntry *entry); | 获取输入的文本 |
| void gtk_entry_set_text (GtkEntry *entry, const gchar *text); | 设置输入的文本 |

以下是一个使用 GtkEntry 进行用户名和密码输入的示例:

#include <gtk/gtk.h>
#include <stdio.h>
#include <string.h>

const char * password = "secret";

void closeApp ( GtkWidget *window, gpointer data)
{
    gtk_main_quit();
}

void button_clicked (GtkWidget *button, gpointer data)
{
    const char *password_text = gtk_entry_get_text(GTK_ENTRY((GtkWidget *) data));
    if (strcmp(password_text, password) == 0)
        printf("Access granted!\n");
    else
        printf("Access denied!\n");
}

int main (int argc, char *argv[])
{
    GtkWidget *window;
    GtkWidget *username_label, *password_label;
    GtkWidget *username_entry, *password_entry;
    GtkWidget *ok_button;
    GtkWidget *hbox1, *hbox2;
    GtkWidget *vbox;
    gtk_init(&argc, &argv);
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "GtkEntryBox");
    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
    gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);
    g_signal_connect ( GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC ( closeApp), NULL);
    username_label = gtk_label_new("Login:");
    password_label = gtk_label_new("Password:");
    username_entry = gtk_entry_new();
    password_entry = gtk_entry_new();
    gtk_entry_set_visibility(GTK_ENTRY (password_entry), FALSE);
    ok_button = gtk_button_new_with_label("Ok");
    g_signal_connect (GTK_OBJECT (ok_button), "clicked", GTK_SIGNAL_FUNC(button_clicked), password_entry);
    hbox1 = gtk_hbox_new ( TRUE, 5 );
    hbox2 = gtk_hbox_new ( TRUE, 5 );
    vbox = gtk_vbox_new ( FALSE, 10);
    gtk_box_pack_start(GTK_BOX(hbox1), username_label, TRUE, FALSE, 5);
    gtk_box_pack_start(GTK_BOX(hbox1), username_entry, TRUE, FALSE, 5);
    gtk_box_pack_start(GTK_BOX(hbox2), password_label, TRUE, FALSE, 5);
    gtk_box_pack_start(GTK_BOX(hbox2), password_entry, TRUE, FALSE, 5);
    gtk_box_pack_start(GTK_BOX(vbox), hbox1, FALSE, FALSE, 5);
    gtk_box_pack_start(GTK_BOX(vbox), hbox2, FALSE, FALSE, 5);
    gtk_box_pack_start(GTK_BOX(vbox), ok_button, FALSE, FALSE, 5);
    gtk_container_add(GTK_CONTAINER(window), vbox);
    gtk_widget_show_all(window);
    gtk_main ();
    return 0;
}
4.3 GtkSpinButton

GtkSpinButton 用于让用户输入数值,限制用户只能输入数字,并可以设置允许值的范围。以下是一些常用的 GtkSpinButton 函数:
| 函数 | 描述 |
| ---- | ---- |
| GtkWidget* gtk_spin_button_new (GtkAdjustment *adjustment, gdouble climb_rate, guint digits); | 创建 GtkSpinButton |
| GtkWidget* gtk_spin_button_new_with_range (gdouble min, gdouble max, gdouble step); | 创建带有范围的 GtkSpinButton |
| gdouble gtk_spin_button_get_value (GtkSpinButton *spin_button); | 获取当前值 |
| void gtk_spin_button_set_value (GtkSpinButton *spin_button, gdouble value); | 设置当前值 |

以下是一个使用 GtkSpinButton 的示例:

#include <gtk/gtk.h>

void closeApp ( GtkWidget *window, gpointer data)
{
    gtk_main_quit();
}

int main (int argc, char *argv[])
{
    GtkWidget *window;
    GtkWidget *spinbutton;
    GtkObject *adjustment;
    gtk_init (&argc, &argv);
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_default_size ( GTK_WINDOW(window), 300, 200);
    g_signal_connect ( GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC ( closeApp), NULL);
    adjustment = gtk_adjustment_new(100.0, 50.0, 150.0, 0.5, 0.05, 0.05);
    spinbutton = gtk_spin_button_new(GTK_ADJUSTMENT(adjustment), 0.01, 2);
    gtk_container_add(GTK_CONTAINER(window), spinbutton);
    gtk_widget_show_all(window);
    gtk_main ();
    return 0;
}
4.4 GtkButton 及其子类

GtkButton 是基本的按钮小部件,还有一些子类具有更多功能:
- GtkToggleButton :具有状态的按钮,可以是开启或关闭状态。
- GtkCheckButton :外观为复选框的 GtkToggleButton
- GtkRadioButton :可以分组的按钮,同一组中只能选择一个选项。

以下是一个使用不同类型按钮的示例:

#include <gtk/gtk.h>
#include <stdio.h>

GtkWidget *togglebutton;
GtkWidget *checkbutton;
GtkWidget *radiobutton1, *radiobutton2;

void closeApp ( GtkWidget *window, gpointer data)
{
    gtk_main_quit();
}

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);
}

void print_active(char * button_name, GtkToggleButton *button)
{
    gboolean active = gtk_toggle_button_get_active(button);
    printf("%s is %s\n", button_name, active?"active":"not active");
}

void button_clicked(GtkWidget *button, gpointer data)
{
    print_active("Checkbutton", GTK_TOGGLE_BUTTON(checkbutton));
    print_active("Togglebutton", GTK_TOGGLE_BUTTON(togglebutton));
    print_active("Radiobutton1", GTK_TOGGLE_BUTTON(radiobutton1));
    print_active("Radiobutton2", GTK_TOGGLE_BUTTON(radiobutton2));
    printf("\n");
}

gint main (gint argc, gchar *argv[])
{
    GtkWidget *window;
    GtkWidget *button;
    GtkWidget *vbox;
    gtk_init (&argc, &argv);
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);
    g_signal_connect ( GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (closeApp), NULL);
    button = gtk_button_new_with_label("Ok");
    togglebutton = gtk_toggle_button_new_with_label("Toggle");
    checkbutton = gtk_check_button_new();
    radiobutton1 = gtk_radio_button_new(NULL);
    radiobutton2 = gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(radiobutton1));
    vbox = gtk_vbox_new (TRUE, 4);
    add_widget_with_label (GTK_CONTAINER(vbox), "ToggleButton:", togglebutton);
    add_widget_with_label (GTK_CONTAINER(vbox), "CheckButton:", checkbutton);
    add_widget_with_label (GTK_CONTAINER(vbox), "Radio 1:", radiobutton1);
    add_widget_with_label (GTK_CONTAINER(vbox), "Radio 2:", radiobutton2);
    add_widget_with_label (GTK_CONTAINER(vbox), "Button:", button);
    g_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(button_clicked), NULL);
    gtk_container_add(GTK_CONTAINER(window), vbox);
    gtk_widget_show_all(window);
    gtk_main ();
    return 0;
}
5. GtkTreeView 组件

GtkTreeView 是 GTK+ 2 中新增的一组小部件,用于创建类似电子表格或文件管理器的数据列表和树视图。它采用了模型 - 视图 - 控制器(MVC)设计模式,将视图和模型分离,具有很多优点。

GtkTreeView 家族由四个组件组成:
- GtkTreeView :树和列表视图。
- GtkTreeViewColumn :表示列表或树的列。
- GtkCellRenderer :控制单元格的绘制。
- GtkTreeModel :表示树和列表数据。

以下是一个使用 GtkTreeView 的示例:

#include <gtk/gtk.h>

enum {
    COLUMN_TITLE,
    COLUMN_ARTIST,
    COLUMN_CATALOGUE,
    N_COLUMNS
};

void closeApp ( GtkWidget *window, gpointer data)
{
    gtk_main_quit();
}

int main (int argc, char *argv[])
{
    GtkWidget *window;
    GtkTreeStore *store;
    GtkWidget *view;
    GtkTreeIter parent_iter, child_iter;
    GtkCellRenderer *renderer;
    gtk_init(&argc, &argv);
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_default_size ( GTK_WINDOW(window), 300, 200);
    g_signal_connect ( GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC ( closeApp), NULL);
    store = gtk_tree_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
    gtk_tree_store_append (store, &parent_iter, NULL);
    gtk_tree_store_set (store, &parent_iter, COLUMN_TITLE, "Dark Side of the Moon", COLUMN_ARTIST, "Pink Floyd", COLUMN_CATALOGUE, "B000024D4P", -1);
    gtk_tree_store_append (store, &child_iter, &parent_iter);
    gtk_tree_store_set (store, &child_iter, COLUMN_TITLE, "Speak to Me", -1);
    view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
    renderer = gtk_cell_renderer_text_new ();
    gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), COLUMN_TITLE, "Title", renderer, "text", COLUMN_TITLE, NULL);
    gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), COLUMN_ARTIST, "Artist", renderer, "text", COLUMN_ARTIST, NULL);
    gtk_container_add(GTK_CONTAINER(window), view);
    gtk_widget_show_all(window);
    gtk_main ();
    return 0;
}

通过以上内容,我们介绍了 GTK+ 编程中窗口创建、事件处理、布局管理以及常见小部件的使用,希望能帮助你入门 GTK+ 编程。后续我们还会继续深入探讨 GTK+ 的其他特性和应用场景。

GTK+ 编程入门:基础组件与交互实现

6. 进一步理解 GtkTreeView

GtkTreeView 是一个功能强大的组件,它的 MVC 设计模式带来了很多便利。下面我们进一步详细了解其各个组件和使用方法。

6.1 模型组件

GTK+ 提供了两种模型组件:
- GtkTreeStore :用于存储多级数据,如目录层次结构。创建时需要指定列数和每列的数据类型,示例代码如下:

Gtkwidget *store = gtk_tree_store_new (3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
  • GtkListStore :用于存储扁平数据。

操作数据时,需要借助 GtkTreeIter 结构,它可以帮助我们定位和操作数据。例如,添加数据到 GtkTreeStore 的步骤如下:
1. 获取一个指向新行的迭代器:

GtkTreeIter iter;
gtk_tree_store_append (store, &iter, NULL);
  1. 使用 gtk_tree_store_set 填充行数据:
gtk_tree_store_set (store, &iter,
0, "Def Leppard",
1, 1987,
2, TRUE,
-1);
6.2 视图组件

创建 GtkTreeView 时,需要传入 GtkTreeStore GtkListStore 模型:

GtkWidget *view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));

配置视图时,需要为每列定义 GtkCellRenderer 并设置数据源。 GtkCellRenderer 有三个子类:
- GtkCellRendererText :处理文本单元格。
- GtkCellRendererPixBuf :处理像素图图形单元格。
- GtkCellRendererToggle :处理切换按钮单元格。

以下是使用 GtkCellRendererText 的示例:

GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view),
0,
"This is the column title",
renderer,
"text", 0,
NULL);
7. GTK+ 编程的最佳实践

在 GTK+ 编程中,有一些最佳实践可以帮助我们编写更高效、更易维护的代码。

7.1 减少重复代码

在前面的示例中,我们多次使用 gtk_box_pack_start 语句添加小部件到容器中。为了减少重复代码,可以定义一个辅助函数,例如:

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);
}
7.2 合理使用回调函数

回调函数是 GTK+ 中处理用户交互的重要方式。在编写回调函数时,要确保逻辑清晰,避免在回调函数中编写过于复杂的代码。例如,在处理按钮点击事件时,只进行必要的操作:

void button_clicked (GtkWidget *button, gpointer data)
{
    const char *password_text = gtk_entry_get_text(GTK_ENTRY((GtkWidget *) data));
    if (strcmp(password_text, password) == 0)
        printf("Access granted!\n");
    else
        printf("Access denied!\n");
}
7.3 遵循 MVC 设计模式

在使用 GtkTreeView 等组件时,要充分利用 MVC 设计模式的优势,将数据模型和视图分离。这样可以提高代码的可维护性和可扩展性。

8. GTK+ 编程流程总结

以下是一个简单的 GTK+ 编程流程 mermaid 流程图:

graph LR
    A[初始化 GTK+ 库] --> B[创建窗口]
    B --> C[创建小部件]
    C --> D[布局小部件]
    D --> E[连接信号和回调函数]
    E --> F[显示窗口和小部件]
    F --> G[启动主循环]
9. 总结

通过本文的介绍,我们学习了 GTK+ 编程的基础知识,包括窗口创建、事件处理、布局管理以及常见小部件的使用。同时,我们还深入了解了 GtkTreeView 组件和 GTK+ 编程的最佳实践。

在实际应用中,我们可以根据需求选择合适的小部件和布局方式,利用信号和回调函数处理用户交互,遵循 MVC 设计模式提高代码质量。希望这些内容能帮助你更好地掌握 GTK+ 编程,开发出功能强大、用户友好的图形用户界面应用程序。

以下是一些常见 GTK+ 小部件及其用途的总结表格:
| 小部件 | 用途 |
| ---- | ---- |
| GtkWindow | 所有 GTK+ 应用程序的基本窗口元素 |
| GtkEntry | 单行文本输入,如电子邮件地址、用户名等 |
| GtkSpinButton | 让用户输入数值,可设置范围 |
| GtkButton 及其子类 | 提供各种按钮功能,如切换、复选、单选等 |
| GtkTreeView | 创建数据列表和树视图,适用于复杂数据展示 |

通过不断实践和学习,你将能够熟练运用 GTK+ 开发出更加复杂和完善的应用程序。祝你在 GTK+ 编程的道路上取得成功!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值