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);
-
使用
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+ 编程的道路上取得成功!

62

被折叠的 条评论
为什么被折叠?



