Nameless Site

But one day, you will stand before its decrepit gate,without really knowing why.

0%

GTK入门学习

一个简单的空白窗口

1
2
3
4
5
6
7
8
9
10
11
12
#include <gtk/gtk.h>

int main(int argc, char** argv) {
gtk_init(&argc, &argv);

GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_show(window);

gtk_main();

return 0;
}

写完代码后,我们对代码进程编译,直接用gcc编译即可,但是需要加上pkg-config --cflags --libs gtk+-3.0

pkg-config 是一个为已经安装的包提供了include,以及实际库安装的位置编译选项的输出和管理的工具;

—cflags 选项作用为自动获得预处理参数,如宏定义,头文件的位置;

—libs 选项作用为自动获得链接参数,如库及依赖的其它库的位置,文件名及其它一些连接参数;

gtk+-3.0 选项作用为指定GTK版本。

具体编译命令如下:
gcc demo.c -o demo pkg-config --cflags --libs gtk+-2.0

头文件〈gtk/gtk.h> 包括了GTK+中所有的控件、变量、函数和结构的声明。

gtk_init (&argc, &argv);所有 GTK应用程序都要调用该函数,而且必须在控件定义之前使用,它为我们设置一些缺省值( 例如视觉和颜色 )映射这个函数将函数库初始化,设置缺省的信号处理函数,并检查通过命令行传递给应用程序的参数,自动完成一些必要的初始化工作。

gtk_main()是在每个Gtk应用程序都要调用的函数。程序运行停在这里等待事件(如键盘事件或鼠标事件)的发生,等待用户来操作窗口。

GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_new()的功能创建一个窗口并返回这个窗口的控件指针。GTK_WINDOW_TOPLEVEL指明窗口的类型为最上层的主窗口,它最常用。接着创建一个GtkWidget *类型的window变量来接收。GtkWidget是GTK+控件类型,GtkWidget * 能指向任何控件的指针类型。

gtk_widget_show(window);用来显示上一步创建的窗口控件。
在这个简单例子里,所有事件都被忽略。用鼠标点击窗口右上角的“×”按钮也不能将窗口关闭。我们可以在终端敲 Ctrl + C 关闭程序。

控件的添加

控件是对数据和方法的封装。控件有自己的属性和方法。属性是指控件的特征。方法是指控件的一些简单而可见的功能。如按钮就是一个控件,这个按钮是方形的,里面有张图片,这是我们能看到外观属性,同时,这个按钮具备被人按下的功能。

GTK中控件主要分为两类:容器控件非容器控件

容器控件:它可以容纳别的控件,我们可以理解为盒子,盒子拿来装东西。容器控件又分为两类,一类只能容纳一个控件,如窗口,按钮;另一类能容纳多个控件,如布局控件。

非容器控件:它不可以容纳别的控件,如标签、行编辑。

前面我们写的空白窗口是能容纳一个控件的控件,现在我们要做的是,给这个窗口添加一个按钮。

首先,我们需要创建一个按钮,然后需要把按钮添加到窗口,接着,显示按钮控件。

按钮的创建:
GtkWidget *gtk_button_new_with_label(const gchar *label);
label:按钮上的字符串类型,gchar相当于C语言的char
返回值:按钮控件指针

容器添加控件:
void gtk_container_add(GtkContainer *container, GtkWidget *widget);
container:容纳控件的容器
widget:要添加的控件

显示控件:
void gtk_widget_show(GtkWidget *widget);
widget:需要显示的控件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int main(int argc, char** argv) {
gtk_init(&argc, &argv); //初始化

GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); //创建顶层窗口

//创建按钮,文本信息为"Hello World"
GtkWidget* button = gtk_button_new_with_label("Hello World");

// 把按钮放入窗口(窗口也是一种容器)
gtk_container_add(GTK_CONTAINER(window), button);

gtk_widget_show(button); // 显示按钮
gtk_widget_show(window); // 显示窗口

gtk_main(); // 主事件循环


return 0;
}

代码分析:

void gtk_container_add(GtkContainer *container, GtkWidget *widget);
这里我们是把按钮添加到窗口容器里,widget为按钮指针(以上代码的button),container为窗口指针(以上代码的window),这里需要注意的是,我们创建的窗口(window)返回值为GtkWidget *类型,而gtk_container_add()的第一个参数为GtkContainer *类型,由于 GtkWidget *类型的变量为指向任何控件的万能指针,所以,在函数传参时,根据参数类型进行相应的转换,如:gtk_container_add( (GtkContainer *)window, button);

C语言里,我们常用这种方法转换。在GTK中,很多内部变量指针类型转换都定义了宏定义,如:#define GTK_CONTAINER(x) (GtkContainer *)(x)

所以,我们还可以这么写:gtk_container_add(GTK_CONTAINER(window), button);

那么,我们如何知道,哪个类型对应哪个宏定义呢?把要转换类型名字全部变为大写,同时,单词和单词之间以下划线“_”连接,然后,这个名字就是哪个宏定义的名字,如,(GtkButton *)x用宏定义的方法为 GTK_BUTTON(x)。

假如窗口里有100个控件,还是这样一个一个地显示出来会比较麻烦,这时候,我们可以通过gtk_widget_show_all()显示所有的控件,里面需要传的是容纳控件的那个容器(这里为窗口)。这样,容器上的控件也会跟着显示。

gtk_widget_show_all(window);// 显示窗口上的所有控件

布局容器

如果我们希望窗口里多放添加几个控件,直接添加是不成功的,因为窗口只能容纳一个控件的容器。这时候,我们需要借助布局容器,我们先把布局容器添加到窗口里,然后再把所需要添加的控件放在布局容器里。

布局容器的主要分类:水平布局( GtkHBox)、垂直布局(GtkVBox )、表格布局(GtkTable)、固定布局(GtkFixed )。

水平布局

水平布局容器的创建:
GtkWidget *gtk_hbox_new( gboolean homogeneous, gint spacing );
homogeneous:容器内控件是否大小一致( gboolean 取值为TRUE 或 FALSE )
spacing:控件之间的间隔( 以像素点为单位 ),gint相当于 C语言的int
返回值:水平布局控件指针

容器添加添加控件:
void gtk_container_add(GtkContainer *container, GtkWidget *widget);
container:容纳控件的容器
widget:要添加的控件

显示容器上所有控件
void gtk_widget_show_all(GtkWidget *widget);
widget:需要显示的控件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <gtk/gtk.h>

int main(int argc, char** argv) {
gtk_init(&argc, &argv); //初始化

GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); //创建顶层窗口


// 创建水平布局容器
GtkWidget* hbox = gtk_hbox_new(TRUE, 10);
// 把容器添加到窗口
gtk_container_add(GTK_CONTAINER(window), hbox);

// button1
GtkWidget* button = gtk_button_new_with_label("button1");
gtk_container_add(GTK_CONTAINER(hbox), button); // 按钮添加到水平布局容器里

// button2
button = gtk_button_new_with_label("button2");
gtk_container_add(GTK_CONTAINER(hbox), button); // 按钮添加到水平布局容器里

// button3
button = gtk_button_new_with_label("button3");
gtk_container_add(GTK_CONTAINER(hbox), button); // 按钮添加到水平布局容器里

gtk_widget_show_all(window); // 显示所有控件

gtk_main(); // 主事件循环

return 0;
}

垂直布局

垂直布局和水平布局的用法基本是一样,无非是新建垂直布局容器接口,还有控件摆放的方向不同。

垂直布局容器的创建:
GtkWidget *gtk_vbox_new( gboolean homogeneous, gint spacing );

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <gtk/gtk.h>

int main(int argc, char** argv) {
gtk_init(&argc, &argv); //初始化

GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); //创建顶层窗口


// 创建垂直布局容器
GtkWidget* hbox = gtk_vbox_new(TRUE, 10);
// 把容器添加到窗口
gtk_container_add(GTK_CONTAINER(window), hbox);

// button1
GtkWidget* button = gtk_button_new_with_label("button1");
gtk_container_add(GTK_CONTAINER(hbox), button); // 按钮添加到水平布局容器里

// button2
button = gtk_button_new_with_label("button2");
gtk_container_add(GTK_CONTAINER(hbox), button); // 按钮添加到水平布局容器里

// button3
button = gtk_button_new_with_label("button3");
gtk_container_add(GTK_CONTAINER(hbox), button); // 按钮添加到水平布局容器里

gtk_widget_show_all(window); // 显示所有控件

gtk_main(); // 主事件循环

return 0;
}

表格布局

学习水平和垂直布局容器后,我们几乎能布出任何风格的布局,只需要嵌套使用水平布局容器和垂直布局容器即可。假如我们要完成下图的布局,我们该怎么做呢?

img

1)创建一个垂直布局容器( A )
2)创建一个水平布局容器( B ),一个close按钮( C )
3)将水平布局容器和close按钮添加到垂直布局容器里( 将 B 和 C 添加到 A )
4)创建button 1按钮( D ) 和 button 2按钮( E )
5)再将button 1按钮 和 button 2按钮添加到水平布局容器里( 将 D 和 E 添加到 B )

这样是可以完成上图的布局,但是,假如布局里有有很多控件,我们只是通过水平和垂直容器嵌套布局会很麻烦,而且,控件越多,布局越麻烦。有没有更简单的方法呢?有,就是我们马上要学习的表格布局,具体的操作流程和水平垂直布局一样。

表格布局容器的创建:
GtkWidget *gtk_table_new(guint rows, guint columns, gboolean homogeneous );
rows: 行数
coumns: 列数
homogeneous:容器内表格的大小是否相等
返回值:表格布局容器指针

布局容器添加控件:
void gtk_table_attach_defaults(GtkTable *table, GtkWidget *widget,guint left_attach,guint right_attach,guint top_attach,guint bottom_attach );
table: 容纳控件的容器
widget: 要添加的控件?
后四个参数为控件摆放的坐标,规则如下:
img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <gtk/gtk.h>

int main(int argc, char** argv) {
gtk_init(&argc, &argv); //初始化

GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); //创建顶层窗口


GtkWidget* table = gtk_table_new(2, 2, TRUE); // 表格布局,2行2列
gtk_container_add(GTK_CONTAINER(window), table); // 容器加入窗口

// button1
GtkWidget* button = gtk_button_new_with_label("button1");
gtk_table_attach_defaults(GTK_TABLE(table), button, 0, 1, 0, 1);// 把按钮加入布局

// button2
button = gtk_button_new_with_label("button2");
gtk_table_attach_defaults(GTK_TABLE(table), button, 1, 2, 0, 1);

// button3
button = gtk_button_new_with_label("button3");
gtk_table_attach_defaults(GTK_TABLE(table), button, 0, 2, 1, 2);

gtk_widget_show_all(window); // 显示所有控件

gtk_main(); // 主事件循环

return 0;
}

固定布局

前面我们学习的水平、垂直和表格布局容器,控件会跟着容器大小的变化进行自动适应,而固定布局容器里的控件则不会跟着变化( 则固定不变 )。

固定布局的创建:
GtkWidget *gtk_fixed_new(void);
返回值:固定布局容器指针

固定布局容器添加控件:
void gtk_fixed_put( GtkFixed fixed,GtkWidget widget,gint x,gint y );
fixed:容纳控件的容器
widget:要添加的控件
x, y:控件摆放位置的起点坐标

设置控件的大小( 宽和高 ):
void gtk_widget_set_size_request(GtkWidget *widget,gint width,gint height );
widget:需要设置的控件
width:宽度
height:高度

移动固定布局里控件的位置:
void gtk_fixed_move( GtkFixed *fixed,GtkWidget *widget,gint x,gint y);
fixed:固定布局容器
widget:需要移动的控件
x, y: 移动的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <gtk/gtk.h>

int main(int argc, char** argv) {
gtk_init(&argc, &argv); //初始化

GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); //创建顶层窗口


GtkWidget* fixed = gtk_fixed_new(); //创建一个固定容器
gtk_container_add(GTK_CONTAINER(window), fixed); // 固定放进窗口

// button1
GtkWidget* button = gtk_button_new_with_label("button1");
gtk_fixed_put(GTK_FIXED(fixed), button, 50, 50); // 按钮添加到固定布局

// button2
GtkWidget* button2 = gtk_button_new_with_label("button2");
gtk_fixed_put(GTK_FIXED(fixed), button2, 0, 0); // 按钮添加到固定布局
gtk_fixed_move(GTK_FIXED(fixed), button2, 150, 150); // 移动控件的位置

// button3
//GtkWidget* button3 = gtk_button_new_with_label("button3");
gtk_widget_set_size_request(button2, 100, 50); // 设置控件的大小

gtk_widget_show_all(window); // 显示所有控件

gtk_main(); // 主事件循环

return 0;
}

行编辑控件

行编辑的创建:
GtkWidget * gtk_entry_new(void);
返回值:行编辑指针行

编辑内容的设置:
void gtk_entry_set_text (GtkEntry *entry,const gchar *text);
entry: 行编辑
text: 需要设置的内容

设置行编辑是否允许编辑:
void gtk_editable_set_editable(GtkEditable *editable, gboolean is_editable);
editable:行编辑
is_editable:TRUE代表可编辑,FALSE不允许编辑

表格布局练习之计算器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <gtk/gtk.h> 	// 头文件

int main(int argc, char* argv[])
{
gtk_init(&argc, &argv); // 初始化

// 窗口的操作
GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); // 创建窗口
gtk_widget_set_size_request(window, 270, 320);

// 表格的操作
GtkWidget* table = gtk_table_new(5, 4, TRUE); // 表格布局,5行4列

// 行编辑的操作
GtkWidget* entry = gtk_entry_new(); // 行编辑的创建
gtk_entry_set_text(GTK_ENTRY(entry), "2+2=4"); // 给行编辑设置内容
gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);// 设置行编辑不允许编辑,只做显示用

// 按钮的操作
GtkWidget* button0 = gtk_button_new_with_label("0"); // 按钮0
GtkWidget* button1 = gtk_button_new_with_label("1"); // 按钮1
GtkWidget* button2 = gtk_button_new_with_label("2"); // 按钮2
GtkWidget* button3 = gtk_button_new_with_label("3"); // 按钮3
GtkWidget* button4 = gtk_button_new_with_label("4"); // 按钮4
GtkWidget* button5 = gtk_button_new_with_label("5"); // 按钮5
GtkWidget* button6 = gtk_button_new_with_label("6"); // 按钮6
GtkWidget* button7 = gtk_button_new_with_label("7"); // 按钮7
GtkWidget* button8 = gtk_button_new_with_label("8"); // 按钮8
GtkWidget* button9 = gtk_button_new_with_label("9"); // 按钮9
GtkWidget* button_add = gtk_button_new_with_label("+"); // 加
GtkWidget* button_minus = gtk_button_new_with_label("-"); // 减
GtkWidget* button_multiply = gtk_button_new_with_label("*"); // 乘
GtkWidget* button_divide = gtk_button_new_with_label("/"); // 除
GtkWidget* button_equal = gtk_button_new_with_label("="); // 等于
GtkWidget* button_detele = gtk_button_new_with_label("c"); // 退格

// 布局操作
gtk_container_add(GTK_CONTAINER(window), table); // 表格放入窗口
gtk_table_attach_defaults(GTK_TABLE(table), entry, 0, 4, 0, 1); // 行编辑放入表格
gtk_table_attach_defaults(GTK_TABLE(table), button0, 0, 1, 4, 5);// 按钮放入表格
gtk_table_attach_defaults(GTK_TABLE(table), button1, 0, 1, 3, 4);
gtk_table_attach_defaults(GTK_TABLE(table), button2, 1, 2, 3, 4);
gtk_table_attach_defaults(GTK_TABLE(table), button3, 2, 3, 3, 4);
gtk_table_attach_defaults(GTK_TABLE(table), button4, 0, 1, 2, 3);
gtk_table_attach_defaults(GTK_TABLE(table), button5, 1, 2, 2, 3);
gtk_table_attach_defaults(GTK_TABLE(table), button6, 2, 3, 2, 3);
gtk_table_attach_defaults(GTK_TABLE(table), button7, 0, 1, 1, 2);
gtk_table_attach_defaults(GTK_TABLE(table), button8, 1, 2, 1, 2);
gtk_table_attach_defaults(GTK_TABLE(table), button9, 2, 3, 1, 2);
gtk_table_attach_defaults(GTK_TABLE(table), button_add, 1, 2, 4, 5);
gtk_table_attach_defaults(GTK_TABLE(table), button_minus, 2, 3, 4, 5);
gtk_table_attach_defaults(GTK_TABLE(table), button_multiply, 3, 4, 2, 3);
gtk_table_attach_defaults(GTK_TABLE(table), button_divide, 3, 4, 3, 4);
gtk_table_attach_defaults(GTK_TABLE(table), button_equal, 3, 4, 4, 5);
gtk_table_attach_defaults(GTK_TABLE(table), button_detele, 3, 4, 1, 2);


gtk_widget_show_all(window); // 显示所有控件

gtk_main(); //进入事件循环

return 0;
}

信号与回调函数

前面我们学习的GTK界面都是静态的,我们按下按钮它是没有响应的,如何让它有响应呢?接下来我们一起学习GTK的信号与回调函数。

GTK采用了信号与回调函数来处理窗口外部传来的事件、消息或信号。当信号发生时,程序自动调用为信号连接的回调函数。

学习应用编程,我们会经常接触到“信号”这个名词。GTK中的“信号”实际上是一种软件中断。“中断”在我们生活中经常遇到,譬如,我正在房间里打游戏,突然送快递的来了,把正在玩游戏的我给“中断”了,我去签收快递( 处理中断 ),处理完成后,再继续玩我的游戏。GTK中的“信号”就是属于这么一种“中断”,当用户按下按钮的时候,就产生一个“中断”,相当于产生一个信号,接着就会处理这么一个“中断任务”(程序里体验为调用一个函数)。

“信号”在GTK中可以认为一种中断的标志,如按下按钮的标志为”pressed”,释放按钮的标志为”released”,这些标志就相当于 C 语言的关键字一样,我们使用的时候必须完全按照它的名字来写。需要注意的是,每个控件的信号标志不一定都一样,如按钮(GtkButton)里有”pressed”信号,窗口(GtkWindow)里就没这个信号,每个控件具体有哪个信号,应该查看帮助文档来确定。

按钮的常用信号:

  • “clicked” : 按下按钮时触发
  • “pressed” : 释放按钮时触发
  • “released” : 释放按钮时触发

对于程序而言,我们按下按钮,是让其调用一个函数。假如有函数A, B, C,我们如何确定按下按钮后只调用函数A,而不是函数 B 或 C。这时候,我们需要一种规则规定,按下按钮后就调用函数A,就像交通规则一样,红灯走绿灯行,信号注册函数就是做这样的事情。

信号注册函数:
gulong g_signal_connect( gpointer instance,const gchar *detailed_signal,GCallback c_handler, gpointer data );
instance:信号发出者,可以认为我们操作的控件,如按下按钮,这个就为按钮指针
detailed_signal:信号标志,如”pressed”
c_handler:回调函数的名称,需要用G_CALLBACK()进行转换
data:给回调函数传的参数,gpointer 相当于C语言的 void *
返回值:注册函数的标志

如:
g_signal_connect(button, "pressed",G_CALLBACK(callback), NULL);

当按下button按钮时,就会自动调用回调函数callback(相当于处理中断任务),回调函数callback可以是任意函数,函数名字我们根据需要自行命名,如果不是库函数,我们还得定义这个回调函数,这里需要注意的是,回调函数的写法(返回值,参数),不是我们想怎么写就怎么写,帮助文档里已经规定好了回调函数应该如何写,如果不按规定来写,可能产生意想不到的错误。

帮助文档的使用请点此处。

回调函数的定义:

1
2
3
4
5
void callback(GtkButton *button,gpointer data)

{

}

设置容器与控件之间的间距:
void gtk_container_set_border_width(GtkContainer *container,guint border_width);
container:容器
border_width:容器与控件之间的间距,guint相当于C语言的uint

获取按钮上的文本内容:
const gchar *gtk_button_get_label(GtkButton *button);
button:按钮
返回值:获取到的文本内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <gtk/gtk.h>	// 头文件

// 按钮按下的处理函数, gpointer 相当于 void *
void deal_pressed(GtkButton* button, gpointer user_data)
{
// button指向main函数的button
// user_data 指向main函数的"I am a button"
// 获得按钮的文本信息
const char* text = gtk_button_get_label(button);
printf("%s /////// %s\n", (char*)user_data, text); // 打印内容
}

int main(int argc, char* argv[])
{
gtk_init(&argc, &argv); // 初始化

GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); // 创建顶层窗口
// 设置窗口边框的宽度(窗口里的控件与窗口边框间隔为15)
gtk_container_set_border_width(GTK_CONTAINER(window), 5);


GtkWidget* button = gtk_button_new_with_label("^_^"); // 创建按钮
gtk_container_add(GTK_CONTAINER(window), button);// 把按钮放入窗口(窗口也是一种容器)

/* 按钮按下(pressed)后会自动调用deal_pressed()
* "I am a button"是传给回调函数deal_pressed()的参数
*/
g_signal_connect(button, "pressed", G_CALLBACK(deal_pressed), "I am a button");

gtk_widget_show_all(window); // 显示窗口全部控件

gtk_main(); // 主事件循环

return 0;
}

GTK简单版计算器

接下来我们做一个简单版的计算器。

  1. 获取按钮上的内容。
  2. 如果获取的内容是“ c ”,则代表进行退格操作,相当于删去最后一个字符。
  3. 如果获取的内容不是“ c ”,则把每一次获取的内容连接起来,如:第一次按了 “ 1 ”, 第二次按了 “ + ”, 第三次按了 “ 1 ”, 连起来则变为 “1+1”。
  4. 如果获取的内容是“ = ”,则需要相应的运算。先把连起来的字符串拆出来,然后再进行相应操作,最后把运算结果重新显示到行编辑上。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#include <gtk/gtk.h> 	// 头文件
#include <string.h>

char buf[50] = { 0 }; //全局变量,保存数据

// 按钮的回调函数
void deal_num(GtkButton* button, gpointer data)
{
const char* txt = gtk_button_get_label(button); // 获取点击按钮的内容

// 退格操作
if (0 == strcmp("c", txt)) { // 如果是退格键
buf[strlen(buf) - 1] = 0; // 最后一个字符变为 0
//gtk_entry_set_text(GTK_ENTRY(data), buf); //重新给行编辑设定内容

}
else {
int a = 0, b = 0;
char c;
strcat(buf, txt); // 内容追加

// 运算操作
if (0 == strcmp("=", txt)) { // 如果是 " = "
sscanf(buf, "%d%c%d", &a, &c, &b); // 拆包
printf("a=%d, c = %c, b=%d\n", a, c, b);
if ('+' == c) {
sprintf(buf, "%d", a + b);
}
else if ('-' == c) {
sprintf(buf, "%d", a - b);
}
else if ('*' == c) {
sprintf(buf, "%d", a * b);
}
else if ('/' == c) {
sprintf(buf, "%d", a / b);
}
}
}

gtk_entry_set_text(GTK_ENTRY(data), buf); // 给行编辑设定内容


}


int main(int argc, char* argv[])
{
gtk_init(&argc, &argv); // 初始化

// 窗口的操作
GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); // 创建窗口
gtk_widget_set_size_request(window, 270, 320);

// 表格的操作
GtkWidget* table = gtk_table_new(5, 4, TRUE); // 表格布局,5行4列

// 行编辑的操作
GtkWidget* entry = gtk_entry_new(); // 行编辑的创建
//gtk_entry_set_text(GTK_ENTRY(entry), "2+2=4"); // 给行编辑设置内容
gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);// 设置行编辑不允许编辑,只做显示用

// 按钮的操作
GtkWidget* button0 = gtk_button_new_with_label("0"); // 按钮0
GtkWidget* button1 = gtk_button_new_with_label("1"); // 按钮1
GtkWidget* button2 = gtk_button_new_with_label("2"); // 按钮2
GtkWidget* button3 = gtk_button_new_with_label("3"); // 按钮3
GtkWidget* button4 = gtk_button_new_with_label("4"); // 按钮4
GtkWidget* button5 = gtk_button_new_with_label("5"); // 按钮5
GtkWidget* button6 = gtk_button_new_with_label("6"); // 按钮6
GtkWidget* button7 = gtk_button_new_with_label("7"); // 按钮7
GtkWidget* button8 = gtk_button_new_with_label("8"); // 按钮8
GtkWidget* button9 = gtk_button_new_with_label("9"); // 按钮9
GtkWidget* button_add = gtk_button_new_with_label("+"); // 加
GtkWidget* button_minus = gtk_button_new_with_label("-"); // 减
GtkWidget* button_multiply = gtk_button_new_with_label("*"); // 乘
GtkWidget* button_divide = gtk_button_new_with_label("/"); // 除
GtkWidget* button_equal = gtk_button_new_with_label("="); // 等于
GtkWidget* button_detele = gtk_button_new_with_label("c"); // 退格

// 布局操作
gtk_container_add(GTK_CONTAINER(window), table); // 表格放入窗口
gtk_table_attach_defaults(GTK_TABLE(table), entry, 0, 4, 0, 1); // 行编辑放入表格
gtk_table_attach_defaults(GTK_TABLE(table), button0, 0, 1, 4, 5); // 按钮放入表格
gtk_table_attach_defaults(GTK_TABLE(table), button1, 0, 1, 3, 4);
gtk_table_attach_defaults(GTK_TABLE(table), button2, 1, 2, 3, 4);
gtk_table_attach_defaults(GTK_TABLE(table), button3, 2, 3, 3, 4);
gtk_table_attach_defaults(GTK_TABLE(table), button4, 0, 1, 2, 3);
gtk_table_attach_defaults(GTK_TABLE(table), button5, 1, 2, 2, 3);
gtk_table_attach_defaults(GTK_TABLE(table), button6, 2, 3, 2, 3);
gtk_table_attach_defaults(GTK_TABLE(table), button7, 0, 1, 1, 2);
gtk_table_attach_defaults(GTK_TABLE(table), button8, 1, 2, 1, 2);
gtk_table_attach_defaults(GTK_TABLE(table), button9, 2, 3, 1, 2);
gtk_table_attach_defaults(GTK_TABLE(table), button_add, 1, 2, 4, 5);
gtk_table_attach_defaults(GTK_TABLE(table), button_minus, 2, 3, 4, 5);
gtk_table_attach_defaults(GTK_TABLE(table), button_multiply, 3, 4, 2, 3);
gtk_table_attach_defaults(GTK_TABLE(table), button_divide, 3, 4, 3, 4);
gtk_table_attach_defaults(GTK_TABLE(table), button_equal, 3, 4, 4, 5);
gtk_table_attach_defaults(GTK_TABLE(table), button_detele, 3, 4, 1, 2);

// 注册信号函数,把 entry 传给回调函数 deal_num()
g_signal_connect(button0, "pressed", G_CALLBACK(deal_num), entry);
g_signal_connect(button1, "pressed", G_CALLBACK(deal_num), entry);
g_signal_connect(button2, "pressed", G_CALLBACK(deal_num), entry);
g_signal_connect(button3, "pressed", G_CALLBACK(deal_num), entry);
g_signal_connect(button4, "pressed", G_CALLBACK(deal_num), entry);
g_signal_connect(button5, "pressed", G_CALLBACK(deal_num), entry);
g_signal_connect(button6, "pressed", G_CALLBACK(deal_num), entry);
g_signal_connect(button7, "pressed", G_CALLBACK(deal_num), entry);
g_signal_connect(button8, "pressed", G_CALLBACK(deal_num), entry);
g_signal_connect(button9, "pressed", G_CALLBACK(deal_num), entry);
g_signal_connect(button_add, "pressed", G_CALLBACK(deal_num), entry);
g_signal_connect(button_minus, "pressed", G_CALLBACK(deal_num), entry);
g_signal_connect(button_multiply, "pressed", G_CALLBACK(deal_num), entry);
g_signal_connect(button_divide, "pressed", G_CALLBACK(deal_num), entry);
g_signal_connect(button_equal, "pressed", G_CALLBACK(deal_num), entry);
g_signal_connect(button_detele, "pressed", G_CALLBACK(deal_num), entry);

gtk_widget_show_all(window); // 显示所有控件

gtk_main(); //进入事件循环

return 0;
}

常用控件

窗口

窗口的创建
GtkWidget *gtk_window_new(GtkWindowType type);
GtkWindowType是一个枚举,有两种情况:

  • GTK_WINDOW_TOPLEVEL:顶层窗口,有边框
  • GTK_WINDOW_POPUP: 弹式窗口,没边框
    返回值:窗口指针

标题的设置:
void gtk_window_set_title(GtkWindow *window, const gchar *title);
window:窗口
title:标题

控件最小大小的设置:

void gtk_widget_set_size_request(GtkWidget *widget,gint width,gint height);
widget:需要操作的控件,可以是任何控件
width:宽度
height:高度

窗口伸缩设置:
void gtk_window_set_resizable(GtkWindow *window, gboolean resizable);
window:窗口
resizable:TURE默认属性,可伸缩,FALSE不可伸缩

显示或隐藏所有控件:

1
2
void gtk_widget_show_all(GtkWidget *widget);
void gtk_widget_hide_all(GtkWidget *widget);

widget:控件

窗口在显示器位置的设置:
void gtk_window_set_position(GtkWindow *window, GtkWindowPosition position);
window:窗口
position常用有4种情况:
GTK_WIN_POS_NONE: 不固定
GTK_WIN_POS_CENTER: 居中
GTK_WIN_POS_MOUSE: 出现在鼠标位置
GTK_WIN_POS_CENTER_ALWAYS: 窗口总是居中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <gtk/gtk.h>	// 头文件

int main(int argc, char* argv[])
{
gtk_init(&argc, &argv); // 初始化

// 创建顶层窗口
GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
// 设置窗口的标题
gtk_window_set_title(GTK_WINDOW(window), "Window");
// 设置窗口在显示器中的位置为居中
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
// 设置窗口的最小大小
gtk_widget_set_size_request(window, 400, 300);
// 固定窗口的大小
gtk_window_set_resizable(GTK_WINDOW(window), FALSE);

// "destroy" 和 gtk_main_quit 连接
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window); // 显示窗口全部控件
//gtk_widget_hide_all(window); // 隐藏窗口

gtk_main(); // 主事件循环

return 0;
}

注意这一句g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
把”destroy” 和 gtk_main_quit 连接

标签

标签主要是显示文本信息,用作标志。

标签的创建:
GtkWidget *gtk_label_new(const gchar *str);
str:文本内容
返回值:标签指针

设置标签的内容:
void gtk_label_set_text (GtkLabel *label, const gchar *str);
label:操作的标签
str:文本内容

获取标签的内容:
const gchar *gtk_label_get_text( GtkLabel *label );
label:操作的标签
返回值:获取到的文本内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <gtk/gtk.h>	// 头文件

int main(int argc, char* argv[])
{
gtk_init(&argc, &argv); // 初始化

GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); // 创建窗口

GtkWidget* vbox = gtk_vbox_new(TRUE, 10); // 创建垂直布局容器
gtk_container_add(GTK_CONTAINER(window), vbox); // 把纵直布局容器放入窗口

// label one
GtkWidget* label_one = gtk_label_new("label one"); // 创建标签
gtk_container_add(GTK_CONTAINER(vbox), label_one); // 将按钮放在布局容器里

// label two
GtkWidget* label_two = gtk_label_new("label two");
const char* str = gtk_label_get_label(GTK_LABEL(label_two)); // 获得标签的内容
printf("str = %s\n", str);

gtk_container_add(GTK_CONTAINER(vbox), label_two); // 将按钮放在布局容器里

// label three
GtkWidget* label_three = gtk_label_new("label three");
gtk_label_set_text(GTK_LABEL(label_three), "change the label text"); // 设置内容

gtk_container_add(GTK_CONTAINER(vbox), label_three); // 将按钮放在布局容器里

// "destroy" 和 gtk_main_quit 连接
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window); // 显示窗口控件

gtk_main(); // 主事件循环

return 0;
}

图片控件

图片控件和标签的作用很类似,都是作为显示用的,只是图片控件显示的内容是图片。

图片控件的创建:
GtkWidget *gtk_image_new_from_file( const gchar *filename );
filename:图片的名字,带路径的话需要加上路径( 相对或绝对 )
返回值:图片控件指针

通过上面方法创建的图片控件,以图片默认大小来显示,不能修改其大小。如果要改变图片的大小,我们要借助图片资源对象GdkPixbuf,需要注意的是,GdkPixbuf不属于控件类,它以 Gdk 开头。

图片资源对象pixbuf的创建:

GdkPixbuf *gdk_pixbuf_new_from_file(const gchar *filename, GError **error);
filename:图片的名字,带路径的话需要加上路径( 相对或绝对 )
error:储存错误的指针
返回值:pixbuf指针

设置图片的大小:
GdkPixbuf *gdk_pixbuf_scale_simple(const GdkPixbuf *src, int dest_width, int dest_height, GdkInterpType interp_type);
src:gdk_pixbuf_new_from_file()的返回值
dest_width:图片的宽度
dest_height:图片的高度
interp_type:是一个枚举变量,标志图片的加载速度和质量,常用GDK_INTERP_BILINEAR
返回值:指定大小好的pixbuf指针

通过gdk_pixbuf_new_from_file(), gdk_pixbuf_scale_simple()创建的pixbuf,它会动态分配空间,占用资源,用完后,需要人为释放资源。

释放资源:
void g_object_unref(GtkObject *object);
object:需要释放的对象

通过pixbuf来创建图片控件:
GtkWidget *gtk_image_new_from_pixbuf(GdkPixbuf *pixbuf );
pixbuf:指定好大小的pixbuf
返回值:图片控件指针

图片控件重新设置一张图片(pixbuf):
void gtk_image_set_from_pixbuf(GtkImage *image, GdkPixbuf *pixbuf );
image:图片控件指针
pixbuf:指定好大小的pixbuf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <gtk/gtk.h>	// 头文件

int main(int argc, char* argv[])
{
gtk_init(&argc, &argv); // 初始化

GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); // 创建窗口

GtkWidget* hbox = gtk_hbox_new(TRUE, 5); // 创建水平布局容器
gtk_container_add(GTK_CONTAINER(window), hbox); // 把水平布局容器放入窗口

// 创建图片控件,这种方法创建以图片默认大小来显示,不能修改
GtkWidget* image_one = gtk_image_new_from_file("./1.jpg");
//gtk_container_add(GTK_CONTAINER(hbox), image_one);// 添加到布局里


// 下面借助GdkPixbuf来修改图片的大小,注意接口以gdk_开头,不属于控件类
// 创建pixbuf,需要占用资源,使用完,需要人为释放
GdkPixbuf* src = gdk_pixbuf_new_from_file("./1.jpg", NULL);// 读取原图片
GdkPixbuf* dst = gdk_pixbuf_scale_simple(src,100, 100, GDK_INTERP_BILINEAR); // 修改图片大小(100, 100), 保存在dst

GtkWidget* image_two = gtk_image_new_from_pixbuf(dst); // 通过pixbuf创建图片控件
g_object_unref(src); // pixbuf使用完,需要人为释放资源
g_object_unref(dst);
gtk_container_add(GTK_CONTAINER(hbox), image_two); // 添加到布局里

// 获取图片控件里的pixbuf,以这个pixbuf又重新创建一个图片控件
GdkPixbuf* tmp = gtk_image_get_pixbuf(GTK_IMAGE(image_two));
GtkWidget* image_three = gtk_image_new_from_pixbuf(tmp);
gtk_container_add(GTK_CONTAINER(hbox), image_three); // 添加到布局里

// 给创建的图片控件重新设置一张图片
GtkWidget* image_four = gtk_image_new_from_file("./1.jpg");
src = gdk_pixbuf_new_from_file("./2.jpg", NULL);// 读取原图片
dst = gdk_pixbuf_scale_simple(src,
200, 200, GDK_INTERP_BILINEAR); // 修改图片大小(200, 200), 保存在dst
gtk_image_set_from_pixbuf(GTK_IMAGE(image_four), dst);// 给image_four重新设置一张图片
g_object_unref(src); // pixbuf使用完,需要人为释放资源
g_object_unref(dst);
gtk_container_add(GTK_CONTAINER(hbox), image_four); // 添加到布局里

g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window); // 显示窗口控件

gtk_main(); // 主事件循环

return 0;
}

进度条

进度条的创建:
GtkWidget *gtk_progress_bar_new(void);
返回值:进度条指针

设置进度条显示的进度比例:
void gtk_progress_bar_set_fraction(GtkProgressBar *pbar, gdouble fraction);
pbar:需要操作的进度条
fraction:0.0到1.0

获取进度条显示的进度比例:
gdouble gtk_progress_bar_get_fraction (GtkProgressBar *pbar);
pbar:需要操作的进度条
返回值:进度比例

设置滑槽上的文本显示:
void gtk_progress_bar_set_text(GtkProgressBar *pbar, gchar *text);
pbar:需要操作的进度条
text:设置的文本内容

设置进度条的移动方向:
void gtk_progress_bar_set_orientation(GtkProgressBar *pbar, GtkProgressBarOrientation orientation);
pbar:需要操作的进度条
orientation:移动方向,它是一个枚举变量

  • GTK_PROGRESS_LEFT_TO_RIGHT:从左向右
  • GTK_PROGRESS_RIGHT_TO_LEFT:从右向左
  • GTK_PROGRESS_BOTTOM_TO_TOP:从下向上
  • GTK_PROGRESS_TOP_TO_BOTTOM:从上向下

获取进度条的方向:
GtkProgressBarOrientation gtk_progress_bar_get_orientation(GtkProgressBar *pbar);
pbar:需要操作的进度条
返回值:进度条的方向,它是一个枚举类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <gtk/gtk.h>
#include <string.h>

// 回调函数,切换进度条的移动方向
void toggle_orientation(GtkWidget* widget, gpointer data)
{
// gtk_progress_bar_get_orientation: 获得进度条当前移动的方向
/*switch (gtk_progress_bar_get_orientation(GTK_PROGRESS_BAR(data))) {
case GTK_PROGRESS_LEFT_TO_RIGHT:
gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(data), GTK_PROGRESS_RIGHT_TO_LEFT);
break;
case GTK_PROGRESS_RIGHT_TO_LEFT:
gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(data), GTK_PROGRESS_LEFT_TO_RIGHT);
break;
default: // 什么也不做
break;
}*/
}

// 更新进度条,这样就能够看到进度条的移动
void callback(GtkWidget* widget, gpointer data)
{
// 在原来值基础上增加 0.05
gdouble new_val = gtk_progress_bar_get_fraction(GTK_PROGRESS_BAR(data)) + 0.05;

if (new_val > 1.0) { // 越界处理
new_val = 0.0;
}

// 设置进度条的新值
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(data), new_val);
}

int main(int argc, char* argv[])
{
gtk_init(&argc, &argv); // 初始化

GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); // 创建主窗口
gtk_window_set_title(GTK_WINDOW(window), "GtkProgressBar"); // 设置窗口标题
gtk_container_set_border_width(GTK_CONTAINER(window), 10); // 设置边框宽度

// 设置窗口的最小大小
gtk_widget_set_size_request(window, 300, 200);
// 窗口关联 destroy 信号 到 gtk_main_quit
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);

GtkWidget* vbox = gtk_vbox_new(FALSE, 5); // 垂直布局容器
gtk_container_add(GTK_CONTAINER(window), vbox); // 容器加入窗口

// 创建一个进度条
GtkWidget* progress = gtk_progress_bar_new();
gtk_container_add(GTK_CONTAINER(vbox), progress); // 加入垂直布局容器
// 设置进度条显示的百分比:50%
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), 0.5);
// 设置在进度条的滑槽上的文本显示
gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), "some text");

// 添加一个按钮,切换移动方向
GtkWidget* button_orientation = gtk_button_new_with_label("Right to Left");
g_signal_connect(button_orientation, "clicked", G_CALLBACK(toggle_orientation), progress);
gtk_container_add(GTK_CONTAINER(vbox), button_orientation); // 加入垂直布局容器

// 增加进度条进度按钮
GtkWidget* button = gtk_button_new_with_label("add");
g_signal_connect(button, "clicked", G_CALLBACK(callback), progress); // connect
gtk_container_add(GTK_CONTAINER(vbox), button); // 加入垂直布局容器

gtk_widget_show_all(window);

gtk_main(); // 主事件循环

return 0;
}

应该是GTK3.0取消了有关进度条方向操作的原因,在回调函数toggle_orientation里直接全部取消有关操作。

按钮

空按钮的创建(按钮上没有任何内容):
GtkWidget *gtk_button_new(void);
返回值:按钮指针

创建带文本内容的按钮:
GtkWidget *gtk_button_new_with_label (const gchar *label);
label:文本内容
返回值:按钮指针

设置按钮的文本内容:
void gtk_button_set_label(GtkButton *button, const gchar *label);
button:操作的按钮
label:要设置的文本内容

获取按钮的文本内容:
const gchar *gtk_button_get_label(GtkButton *button);
button:操作的按钮
返回值:获取到的文本内容

控件使能设置:
void gtk_widget_set_sensitive(GtkWidget *widget, gboolean sensitive);
widget:要操作的控件
sensitive:FALSE变灰,不允许相应响应;TRUE,正常响应

给按钮设置一张图片:
void gtk_button_set_image(GtkButton *button,GtkWidget *image);
button:按钮
image:图片控件

获取按钮上的图片:
GtkWidget *gtk_button_set_image(GtkButton *button);
button:按钮
返回值:图片控件

设置按钮透明背景色:
void gtk_button_set_relief(GtkButton *button, newstyle);
button:按钮
newstyle: 枚举变量,GTK_RELIEF_NONE为透明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <gtk/gtk.h>	// 头文件

int main(int argc, char* argv[])
{
gtk_init(&argc, &argv); // 初始化

GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); // 创建窗口

GtkWidget* hbox = gtk_hbox_new(TRUE, 10); // 水平布局容器
gtk_container_add(GTK_CONTAINER(window), hbox); // 把水平布局容器放入窗口

// 普通按钮
GtkWidget* normal_button = gtk_button_new_with_label("normal button");
const char* str = gtk_button_get_label(GTK_BUTTON(normal_button)); // 获取按钮的文本内容
printf("str === %s\n", str);

gtk_button_set_label(GTK_BUTTON(normal_button), "change"); // 设置按钮的文本内容
gtk_container_add(GTK_CONTAINER(hbox), normal_button); // 把按钮放入水平布局容器里

// 给按钮设置一张图片
GtkWidget* button = gtk_button_new(); // 先创建空按钮
//GtkWidget* image = gtk_image_new_from_file("./1.jpg"); // 图像控件
GdkPixbuf* src = gdk_pixbuf_new_from_file("./1.jpg", NULL);//
GdkPixbuf* dst = gdk_pixbuf_scale_simple(src, 100, 100, GDK_INTERP_BILINEAR);

GtkWidget* image = gtk_image_new_from_pixbuf(dst); // 通过pixbuf创建图片控件
g_object_unref(src); // pixbuf使用完,需要人为释放资源
g_object_unref(dst);
gtk_button_set_image(GTK_BUTTON(button), image); // 给按钮添加图片
gtk_container_add(GTK_CONTAINER(hbox), button); // 把按钮放入水平布局容器里
gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); // 按钮背景色透明

// 按钮使能设置(是否变灰),默认为使能TRUE,非使能FALSE
//gtk_widget_set_sensitive(button, FALSE);


gtk_widget_show_all(window); // 显示窗口控件

gtk_main(); // 主事件循环

return 0;
}

行编辑

行编辑,只允许输入一行内容的控件,如密码输入框。

行编辑的创建:
GtkWidget *gtk_entry_new(void);
返回值:行编辑指针

设置行编辑内容的最大长度:
void gtk_entry_set_max_length(GtkEntry *entry, gint max);
entry:行编辑
max:长度的最大值,这里填0代表长度不作限制

设置行编辑的文本内容:
void gtk_entry_set_text(GtkEntry *entry,const gchar *text);
entry:行编辑
text:文本内容

获取行编辑的内容:
const gchar *gtk_entry_get_text(GtkEntry *entry);
entry:行编辑
返回值:获取到的行编辑内容

设置编辑控件是否允许编辑:
void gtk_editable_set_editable(GtkEditable *editable,gboolean is_editable);
editable:需要操作的控件
is_editable:TRUE代表能允许编辑,默认属性,FALSE不允许编辑

设置行编辑的内容是否可视(不能可视相当于密码模式,看不到文本的内容)
void gtk_entry_set_visibility(GtkEntry *entry,gboolean visible);
entry:行编辑
visible:TRUE可视,FALSE不可视

常用信号:”activate”
当用户在文本输入控件内部按回车键时引发activate信号;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <gtk/gtk.h> 

// 按Enter,获取行编辑的内容
void enter_callback(GtkWidget* widget, gpointer entry)
{
const gchar* entry_text;
// 获得文本内容
entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
printf("Entry contents: %s\n", entry_text);
}

int main(int argc, char* argv[])
{
gtk_init(&argc, &argv); // 初始化

GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); // 创建窗口
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_set_size_request(window, 200, 50); // 设置窗口的大小

GtkWidget* entry = gtk_entry_new(); // 创建行编辑
gtk_container_add(GTK_CONTAINER(window), entry); // 表格放入窗口
gtk_entry_set_max_length(GTK_ENTRY(entry), 100); // 设置行编辑显示最大字符的长度
gtk_entry_set_text(GTK_ENTRY(entry), "hello word"); // 设置内容
//gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); // 密码模式

/* 如果我们想在用户输入文本时进行响应,可以为activate设置回调函数。
* 当用户在文本输入构件内部按回车键时引发Activate信号;
*/
g_signal_connect(entry, "activate", G_CALLBACK(enter_callback), entry);

gtk_widget_show_all(window); // 显示窗口所有控件

gtk_main(); // 主事件循环

return 0;
}

笔记本控件

笔记本控件,可以让用户标签式地切换多个界面。当我们需要多窗口切换时,使用笔记本控件是一个明智的选择。

img

笔记本控件的创建:
GtkWidget *gtk_notebook_new(void);
返回值:笔记本控件指针

设置页标签的位置:
void gtk_notebook_set_tab_pos( GtkNotebook *notebook, GtkPositionType pos );
notebook:笔记本控件
pos取值如下:

  • GTK_POS_LEFT: 左
  • GTK_POS_RIGHT: 右
  • GTK_POS_TOP: 上
  • GTK_POS_BOTTOM: 下

追加页面:
gint gtk_notebook_append_page(GtkNotebook *notebook, GtkWidget *child, GtkWidget *tab_label );
notebook: 容纳别的控件的笔记本
child: 放在笔记本页面里的子控件
tab_label:要添加页面的标题名
返回值:成功返回值页面值(从0开始),失败返回-1

在指定位置添加页面(从0开始):
gint gtk_notebook_insert_page(GtkNotebook *notebook,GtkWidget *child, tkWidget *tab_label,gint position );
notebook: 容纳别的控件的笔记本
child: 放在笔记本页面里的子控件
tab_label:要添加页面的标题名
position:页面的位置,从0开始,如果-1代表添加到最后
返回值:成功返回值页面值(从0开始),失败返回-1

设置起始页,从0开始算,相当于设置第几个页面显示:
void gtk_notebook_set_current_page(GtkNotebook *notebook, gint page_num );
notebook:要设置的笔记本控件
page_num:页面数

常用信号:”switch-page”
当切换页面时,会触发”switch-page”信号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <gtk/gtk.h>

// "switch-page"信号处理函数,page_num指第几个页面,从0开始
void deal_switch_pape(GtkNotebook* notebook, gpointer page,
guint page_num, gpointer user_data)
{
printf("This is page %d\n", page_num + 1);
}

int main(int argc, char* argv[])
{
gtk_init(&argc, &argv); // 初始化

GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); // 创建主窗口
gtk_widget_set_size_request(window, 400, 300); //设置窗口大小

GtkWidget* notebook = gtk_notebook_new(); // 创建笔记本控件
gtk_container_add(GTK_CONTAINER(window), notebook); // 笔记本放进窗口

// 页标签的位置,可以有四种位置:上、下、左或右。
gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT); //上面


// 第一个页面
GtkWidget* label = gtk_label_new("Page one"); //创建标签
GtkWidget* box = gtk_vbox_new(TRUE, 5); // 创建布局
GtkWidget* button_one = gtk_button_new_with_label("I am first page");
GtkWidget* button_two = gtk_button_new_with_label("I am first button of first page");
gtk_container_add(GTK_CONTAINER(box), button_one);
gtk_container_add(GTK_CONTAINER(box), button_two);

/* gtk_notebook_append_page( GtkNotebook *notebook, GtkWidget *child,
* GtkWidget *tab_label );
* 插入页面到笔记本的后端(append)或前端(prepend)来添加页面。
* child是放在笔记本页面里的子控件,它必须另外创建,一般是容器
* tab_label是要添加的页面的标签。
*/
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), box, label);

// 第二个页面
label = gtk_label_new("Page two"); //创建标签
box = gtk_hbox_new(TRUE, 5); // 创建布局
button_one = gtk_button_new_with_label("I am second page");
button_two = gtk_button_new_with_label("I am first button of second page");
gtk_container_add(GTK_CONTAINER(box), button_one);
gtk_container_add(GTK_CONTAINER(box), button_two);
gtk_notebook_prepend_page(GTK_NOTEBOOK(notebook), box, label);

// 第三个页面,在指定位置添加页面,从0开始
button_one = gtk_button_new_with_label("insert_page");
label = gtk_label_new("Page three");
gtk_notebook_insert_page(GTK_NOTEBOOK(notebook), button_one, label, 1);

// 处理信号,当切换页面时,会触发"switch-page"信号
g_signal_connect(notebook, "switch-page",
G_CALLBACK(deal_switch_pape), NULL);

gtk_widget_show_all(window); //显示所有控件

// 设置起始页(第2页),从0开始算,相当于设置第2个页面显示
gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), 1);

gtk_main(); // 主事件循环

return 0;
}

设置窗口背景图

有问题,暂且存疑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <gtk/gtk.h>

/* 功能: 设置背景图
* widget: 主窗口
* w, h: 图片的大小
* path: 图片路径
*/
void chang_background(GtkWidget* widget, int w, int h, const gchar* path)
{
gtk_widget_set_app_paintable(widget, TRUE); //允许窗口可以绘图
gtk_widget_realize(widget);

/* 更改背景图时,图片会重叠
* 这时要手动调用下面的函数,让窗口绘图区域失效,产生窗口重绘制事件(即 expose 事件)。
*/
gtk_widget_queue_draw(widget);

GdkPixbuf* src_pixbuf = gdk_pixbuf_new_from_file(path, NULL); // 创建图片资源对象
// w, h是指定图片的宽度和高度
GdkPixbuf* dst_pixbuf = gdk_pixbuf_scale_simple(src_pixbuf, w, h, GDK_INTERP_BILINEAR);

GdkPixmap* pixmap = NULL;

/* 创建pixmap图像;
* NULL:不需要蒙版;
* 123: 0~255,透明到不透明
*/
gdk_pixbuf_render_pixmap_and_mask(dst_pixbuf, &pixmap, NULL, 200);
// 通过pixmap给widget设置一张背景图,最后一个参数必须为: FASLE
gdk_window_set_back_pixmap(widget->window, pixmap, FALSE);

// 释放资源
g_object_unref(src_pixbuf);
g_object_unref(dst_pixbuf);
g_object_unref(pixmap);
}

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

//主窗口操作
GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

gtk_window_set_title(GTK_WINDOW(window), "设置窗口背景图");
gtk_widget_set_size_request(window, 500, 450);

chang_background(window, 500, 400, "./1.jpg"); // 设置窗口背景图

GtkWidget* hbox = gtk_hbox_new(TRUE, 50); // 水平布局容器
gtk_container_add(GTK_CONTAINER(window), hbox); // 把水平布局容器放入窗口


// 普通按钮
GtkWidget* normal_button = gtk_button_new_with_label("normal button");
gtk_container_add(GTK_CONTAINER(hbox), normal_button); // 把按钮放入水平布局容器里
gtk_widget_show_all(window); // 显示所有部件

gtk_main();

return 0;
}

改变控件字体大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <gtk/gtk.h>

/* 功能: 设置控件字体大小
* widget: 需要改变字体的控件
* size: 字体大小
* is_button: TRUE代表控件为按钮,FALSE为其它控件
*/
static void set_widget_font_size(GtkWidget* widget, int size, gboolean is_button)
{
GtkWidget* labelChild;
PangoFontDescription* font;
gint fontSize = size;

font = pango_font_description_from_string("Sans"); //"Sans"字体名
pango_font_description_set_size(font, fontSize * PANGO_SCALE);//设置字体大小

if (is_button) {
labelChild = gtk_bin_get_child(GTK_BIN(widget));//取出GtkButton里的label
}
else {
labelChild = widget;
}

//设置label的字体,这样这个GtkButton上面显示的字体就变了
gtk_widget_modify_font(GTK_WIDGET(labelChild), font);
pango_font_description_free(font);
}

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

//主窗口操作
GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

gtk_window_set_title(GTK_WINDOW(window), "change ");
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
gtk_widget_set_size_request(window, 500, 450);

GtkWidget* vbox = gtk_vbox_new(TRUE, 5); // 创建垂直布局
gtk_container_add(GTK_CONTAINER(window), vbox); // 把布局放在窗口里

// 改变标签字体大小,参数为 FALSE
GtkWidget* label = gtk_label_new("I am a label"); // 标签
set_widget_font_size(label, 50, FALSE); // 设置标签字体大小
gtk_container_add(GTK_CONTAINER(vbox), label);

// 改变按钮字体大小,参数为 TRUE
GtkWidget* button = gtk_button_new_with_label("I am a button");
set_widget_font_size(button, 50, TRUE); // 改变按钮字体大小
gtk_container_add(GTK_CONTAINER(vbox), button);


gtk_widget_show_all(window); // 显示所有部件

gtk_main();

return 0;
}

定时器

定时器在应用编程里用得很广,我们手机里的闹钟,幻灯片播放图片等应用都用到定时器。定时器,每隔一段时间干一件事(程序里表现为调用一个函数),像闹钟,每隔一天响一次。

定时器的创建:
guint g_timeout_add(guint interval, GSourceFunc function, gpointer data);
interval:设置的时间间隔,以毫秒为单位( 1000即为1秒 )
function:回调函数的名字
data:给回调函数传的参数
返回值:定时器id号

使用实例:
guint timer = g_timeout_add(500, (GSourceFunc)deal_time, NULL);
每隔500ms会执行一次deal_time()回调函数。
注意:当回调函数返回值为FALSE时,定时器执行一次后便会停止工作,不再循环执行。所以,要想定时器连续工作,循环执行所指定的回调函数,应该返回TRUE。

定时器的移除:
gboolean g_source_remove(guint tag);
tag:定时器id号
返回值:TRUE代表成功,FALSE代表失败

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <gtk/gtk.h>
#include <stdlib.h>
#include <string.h>

guint timer; // 定时器id

/* 功能: 设置控件字体大小
* widget: 需要改变字体的控件
* size: 字体大小
* is_button: TRUE代表控件为按钮,FALSE为其它控件
*/
void set_widget_font_size(GtkWidget* widget, int size, int is_button)
{
GtkWidget* labelChild;
PangoFontDescription* font;
gint fontSize = size;

font = pango_font_description_from_string("Sans");//"Sans"字体名
pango_font_description_set_size(font, fontSize * PANGO_SCALE);//设置字体大小

if (is_button) {
labelChild = gtk_bin_get_child(GTK_BIN(widget));//取出GtkButton里的label
}
else {
labelChild = widget;
}

//设置label的字体,这样这个GtkButton上面显示的字体就变了
gtk_widget_modify_font(GTK_WIDGET(labelChild), font);
pango_font_description_free(font);
}

/* 功能: 定时器处理函数
* label: 主要用于显示数字
*/
gboolean deal_time(gpointer* label)
{
char buf[5] = "Time";
static int num = 10;
num--;
sprintf(buf, "%d", num);
//printf("%s \n", buf);
gtk_label_set_text(GTK_LABEL(label), buf);

if (0 == num) {
num = 11;
//g_source_remove(timer); // 移除定时器
}

return TRUE;//尽量返回TRUE
}

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

/////////////////主窗口操作
GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "timer");
gtk_container_set_border_width(GTK_CONTAINER(window), 0);
//设置窗口默认大小,设置一个最小大小
gtk_window_set_default_size(GTK_WINDOW(window), 320, 400);
//设置窗口在显示器中的位置为居中。
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL); //按关闭按钮可以把程序中断

// 倒计时显示区域
GtkWidget* label = gtk_label_new("10"); // label的创建
set_widget_font_size(label, 230, FALSE); // 设置label的字体大小
gtk_container_add(GTK_CONTAINER(window), label);

// 定时器的创建, label传给回调函数
timer = g_timeout_add(1000, (GSourceFunc)deal_time, (gpointer)label);

gtk_widget_show_all(window); // 显示所有部件

gtk_main();

return 0;
}

鼠标事件

鼠标事件,可以理解为操作鼠标的动作。对于窗口而言,用户操作鼠标,窗口检测到鼠标的操作( 产生一个信号 ),然后去做相应处理( 调用其规定的回调函数 ),即可认为是鼠标事件,还是信号与回调函数的知识点。

窗口默认不接收鼠标的操作,需要手动添加让其接收。

设置控件捕获(接收)相应的事件:
void gtk_widget_add_events( GtkWidget *widget, gint events );
widget:控件
events:事件类型,它是GdkEventMask的枚举常量,取值列举如下

  • GDK_BUTTON_PRESS_MASK:鼠标点击
  • GDK_BUTTON_RELEASE_MASK:鼠标释放
  • GDK_BUTTON_MOTION_MASK:鼠标移动
  • GDK_KEY_PRESS_MASK:键盘按下
  • GDK_ENTER_NOTIFY_MASK:进入控件区域
  • …… ……

鼠标点击事件产生的信号:button-press-event
鼠标释放事件产生的信号:button-release-event

其回调函数的定义如下:
gboolean callback( GtkWidget *widget, GdkEventButton *event, gpointer data )
event:鼠键事件结构体变量,系统内部的变量,不是用户传参的
event->x,event->y:得到点击坐标值
event->button: 鼠标哪个键按下
event->type: 是否双击
返回值:没有太大意义

鼠标移动事件产生的信号:motion-notify-event

其回调函数的定义如下:
gboolean callback( GtkWidget *widget, GdkEventMotion *event, gpointer data )
{
event->x,event->y:得到移动的坐标值
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include <gtk/gtk.h>	// 头文件

// 鼠标点击事件处理函数
gboolean deal_mouse_press(GtkWidget *widget, GdkEventButton *event, gpointer data)
{
switch(event->button){ // 判断鼠标点击的类型
case 1:
printf("Left Button!!\n");
break;
case 2:
printf("Middle Button!!\n");
break;
case 3:
printf("Right Button!!\n");
break;
default:
printf("Unknown Button!!\n");
}

if(event->type == GDK_2BUTTON_PRESS){
printf("double click\n");
}

// 获得点击的坐标值,距离窗口左顶点
gint i = event->x;
gint j = event->y;
printf("press_x = %d, press_y = %d\n", i, j);

return TRUE;
}

// 鼠标移动事件(点击鼠标任何键)的处理函数
gboolean deal_motion_notify_event(GtkWidget *widget, GdkEventMotion *event, gpointer data)
{
// 获得移动鼠标的坐标值,距离窗口左顶点
gint i = event->x;
gint j = event->y;
printf("motion_x = %d, motion_y = %d\n", i, j);

return TRUE;
}

int main( int argc,char *argv[] )
{
gtk_init(&argc, &argv); // 初始化

// 创建顶层窗口
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
// 设置窗口的标题
gtk_window_set_title(GTK_WINDOW(window), "mouse_event");
// 设置窗口在显示器中的位置为居中
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
// 设置窗口的最小大小
gtk_widget_set_size_request(window, 400, 300);
// "destroy" 和 gtk_main_quit 连接
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

// 窗口接收鼠标事件
// GDK_BUTTON_PRESS_MASK:鼠标点击事件
// GDK_BUTTON_MOTION_MASK:按住鼠标移动事件
gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_MOTION_MASK);

// "button-press-event" 与 deal_mouse_event 连接,鼠标点击事件
g_signal_connect(window, "button-press-event", G_CALLBACK(deal_mouse_press), NULL);
// "motion-notify-event" 与 deal_motion_notify_event 连接,按住鼠标移动事件
g_signal_connect(window, "motion-notify-event", G_CALLBACK(deal_motion_notify_event), NULL);

gtk_widget_show_all(window); // 显示窗口全部控件

gtk_main(); // 主事件循环

return 0;
}

​```C
#include <gtk/gtk.h> // 头文件

// 鼠标点击事件处理函数
gboolean deal_mouse_press(GtkWidget* widget, GdkEventButton* event, gpointer data)
{
switch (event->button) { // 判断鼠标点击的类型
case 1:
printf("Left Button!!\n");
break;
case 2:
printf("Middle Button!!\n");
break;
case 3:
printf("Right Button!!\n");
break;
default:
printf("Unknown Button!!\n");
}

if (event->type == GDK_2BUTTON_PRESS) {
printf("double click\n");
}

// 获得点击的坐标值,距离窗口左顶点
gint i = event->x;
gint j = event->y;
printf("press_x = %d, press_y = %d\n", i, j);

return TRUE;
}

// 鼠标移动事件(点击鼠标任何键)的处理函数
gboolean deal_motion_notify_event(GtkWidget* widget, GdkEventMotion* event, gpointer data)
{
// 获得移动鼠标的坐标值,距离窗口左顶点
gint i = event->x;
gint j = event->y;
printf("motion_x = %d, motion_y = %d\n", i, j);

return TRUE;
}

int main(int argc, char* argv[])
{
gtk_init(&argc, &argv); // 初始化

// 创建顶层窗口
GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
// 设置窗口的标题
gtk_window_set_title(GTK_WINDOW(window), "mouse_event");
// 设置窗口在显示器中的位置为居中
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
// 设置窗口的最小大小
gtk_widget_set_size_request(window, 400, 300);
// "destroy" 和 gtk_main_quit 连接
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

// 窗口接收鼠标事件
// GDK_BUTTON_PRESS_MASK:鼠标点击事件
// GDK_BUTTON_MOTION_MASK:按住鼠标移动事件
gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_MOTION_MASK);

// "button-press-event" 与 deal_mouse_event 连接,鼠标点击事件
g_signal_connect(window, "button-press-event", G_CALLBACK(deal_mouse_press), NULL);
// "motion-notify-event" 与 deal_motion_notify_event 连接,按住鼠标移动事件
g_signal_connect(window, "motion-notify-event", G_CALLBACK(deal_motion_notify_event), NULL);

gtk_widget_show_all(window); // 显示窗口全部控件

gtk_main(); // 主事件循环

return 0;
}

键盘事件

键盘事件,可以理解为操作键盘的动作。对于窗口而言,用户操作键盘,窗口检测到键盘的操作( 产生一个信号 ),然后去做相应处理( 调用其规定的回调函数 ),即可认为是键盘事件,还是信号与回调函数的知识点。

窗口默认就能接收键盘事件,其中的键值定义在 /usr/include/gtk-2.0/gdk/gdkkeysyms.h 文件里

键盘按下事件产生的信号:key-press-event
键盘释放事件产生的信号:key-release-event

其回调函数的定义如下:
gboolean callback( GtkWidget *widget, GdkEventKey *event, gpointer data )
{
event:键盘事件结构体变量,系统内部的变量,不是用户传参的
event->keyval:获取按下(释放)键盘键值,每个键值对于一个ASCII码
}

注意:可以在 GtkWidget 里找到相应的事件信号,回调函数如何定义应该查看帮助文档。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <gtk/gtk.h>	// 头文件
#include <gdk/gdkkeysyms.h> //键盘头文件,GDK_Up在这声明

// 键盘按下事件处理函数
gboolean deal_key_press(GtkWidget* widget, GdkEventKey* event, gpointer data)
{

switch (event->keyval) { // 键盘键值类型
case GDK_KEY_Up:
printf("Up\n");
break;
case GDK_KEY_Left:
printf("Left\n");
break;
case GDK_KEY_Right:
printf("Right\n");
break;
case GDK_KEY_Down:
printf("Down\n");
break;
}

int key = event->keyval; // 获取键盘键值类型
printf("keyval = %d\n", key);

return TRUE;
}

int main(int argc, char* argv[])
{
gtk_init(&argc, &argv); // 初始化

// 创建顶层窗口
GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
// 设置窗口的标题
gtk_window_set_title(GTK_WINDOW(window), "mouse_event");
// 设置窗口在显示器中的位置为居中
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
// 设置窗口的最小大小
gtk_widget_set_size_request(window, 400, 300);
// "destroy" 和 gtk_main_quit 连接
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);

// "key-press-event" 与 deal_key_press 连接
g_signal_connect(window, "key-press-event",
G_CALLBACK(deal_key_press), NULL);

gtk_widget_show_all(window); // 显示窗口全部控件

gtk_main(); // 主事件循环

return 0;
}

属性改变事件

当窗口初始化,被移动或拉伸时,会触发属性改变事件。

属性改变事件产生的信号:configure_event

其回调函数的定义如下:

gboolean callback( GtkWidget *widget, GdkEventConfigure *event, gpointer data )
event:属性事件结构体变量,系统内部的变量,不是用户传参的
event->x,event->y:窗口的起点坐标
event->width,event->height:窗口的大小

以下例子为拉伸或移动窗口,能获取窗口的起点坐标以及大小:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <gtk/gtk.h>	// 头文件

// 回调函数
gboolean on_configure_event(GtkWidget * widget,
GdkEventConfigure * event,
gpointer data)
{
// 窗口的起点坐标
gint x = event->x;
gint y = event->y;
printf("x = %d, y = %d\n", x, y);

// 窗口的宽度和高度
gint w = event->width;
gint h = event->height;
printf("w = %d, h = %d\n", w, h);

return TRUE;

}

int main( int argc, char *argv[] )
{
gtk_init(&argc, &argv); // 初始化

// 创建顶层窗口
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
// 设置窗口的标题
gtk_window_set_title(GTK_WINDOW(window), "mouse_event");
// 设置窗口在显示器中的位置为居中
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
// 设置窗口的最小大小
gtk_widget_set_size_request(window, 400, 300);
// "destroy" 和 gtk_main_quit 连接
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

// "configure_event" 与 on_configure_event 连接
g_signal_connect(window, "configure_event", G_CALLBACK(on_configure_event), NULL);

gtk_widget_show_all(window); // 显示窗口全部控件

gtk_main(); // 主事件循环

return 0;
}

事件盒子

有些控件( 如:标签GtkLabel,图片控件GtkImage 等),不响应GDK事件,相当于鼠标在其上面点击,它是捕获不到的。用户通过事件盒子可以解决这个问题。

事件盒子给控件提供一个GDK窗口来捕获事件。我们把控件放在事件盒子里,当我们用鼠标点击这个控件时,这个事件盒子就能捕获到点击的状态。

事件盒子的创建:

GtkWidget *gtk_event_box_new(void);
返回值:事件盒子指针

把控件添加到事件盒子里:
void gtk_container_add(GtkContainer *container, GtkWidget *widget );
container:容纳控件的事件盒子
widget:要添加的控件

以下例子为,通过事件盒子实现,双击标签改变其内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <gtk/gtk.h>

// "button_press_event"信号处理函数
gboolean button_pressed(GtkWidget* eventbox,GdkEventButton* event,GtkLabel* label)
{
if (event->type == GDK_2BUTTON_PRESS) {// 双击
const gchar* text = gtk_label_get_text(label); // 获取label内容
if (text[0] == 'D') {
gtk_label_set_text(label, "I Was Double-Clicked!"); // 修改
}
else {
gtk_label_set_text(label, "Double-Click Me Again!");
}
}

return FALSE;
}

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

GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); // 主窗口
gtk_window_set_title(GTK_WINDOW(window), "Event Box"); // 标题
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); // 居中显示
gtk_widget_set_size_request(window, 200, 50); // 最小大小
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

GtkWidget* eventbox = gtk_event_box_new(); // 事件盒子的创建
gtk_widget_set_events(eventbox, GDK_BUTTON_PRESS_MASK); // 捕获鼠标点击事件
gtk_container_add(GTK_CONTAINER(window), eventbox); // 事件盒子放入窗口

GtkWidget* label = gtk_label_new("Double-Click Me!"); // label
gtk_container_add(GTK_CONTAINER(eventbox), label); // label放入事件盒子里

// 处理"button_press_event"信号,把标签label传给回调函数
g_signal_connect(eventbox, "button_press_event",G_CALLBACK(button_pressed), (gpointer)label);

gtk_widget_show_all(window); // 显示所有控件

gtk_main();

return 0;
}

菜单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/* 添加菜单 menu.c */
#include <gtk/gtk.h>
void on_menu_activate (GtkMenuItem* item,gpointer data)
{
//g_print("菜单项 %s 被激活\n",(gchar*)data);
g_print("Menuitem %s is pressed.\n",(gchar*)data);
}
int main (int argc, char* argv[])
{
GtkWidget* window;
GtkWidget* box;
GtkWidget* menubar;
GtkWidget* menu;
GtkWidget* editmenu;
GtkWidget* helpmenu;
GtkWidget* rootmenu;
GtkWidget* menuitem;
GtkAccelGroup* accel_group ;

gtk_init(&argc,&argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_set_size_request(window,400,300);
gtk_window_set_title(GTK_WINDOW(window),"菜单测试程序");
g_signal_connect(G_OBJECT(window),"destroy",
G_CALLBACK(gtk_main_quit),NULL);
accel_group = gtk_accel_group_new();

gtk_window_add_accel_group(GTK_WINDOW(window),accel_group);// AccelGroup

box = gtk_vbox_new(FALSE,0);
gtk_container_add(GTK_CONTAINER(window),box);
menu = gtk_menu_new();//文件菜单
menuitem = gtk_image_menu_item_new_from_stock
(GTK_STOCK_NEW,accel_group);
gtk_menu_shell_append(GTK_MENU_SHELL(menu),menuitem);
g_signal_connect(G_OBJECT(menuitem),"activate",
G_CALLBACK(on_menu_activate),//(gpointer)("新建"));
(gpointer)("New"));
menuitem = gtk_image_menu_item_new_from_stock
(GTK_STOCK_OPEN,accel_group);
gtk_menu_shell_append(GTK_MENU_SHELL(menu),menuitem);
g_signal_connect(G_OBJECT(menuitem),"activate",
G_CALLBACK(on_menu_activate),//(gpointer)("打开"));
(gpointer)("Open"));
menuitem = gtk_image_menu_item_new_from_stock
(GTK_STOCK_SAVE,accel_group);
gtk_menu_shell_append(GTK_MENU_SHELL(menu),menuitem);
g_signal_connect(G_OBJECT(menuitem),"activate",
G_CALLBACK(on_menu_activate),//(gpointer)("保存"));
(gpointer)("Save"));
menuitem = gtk_image_menu_item_new_from_stock
(GTK_STOCK_SAVE_AS,accel_group);
gtk_menu_shell_append(GTK_MENU_SHELL(menu),menuitem);
g_signal_connect(G_OBJECT(menuitem),"activate",
G_CALLBACK(on_menu_activate),//(gpointer)("另存为"));
(gpointer)("Save As"));
menuitem = gtk_separator_menu_item_new();
gtk_menu_shell_append(GTK_MENU_SHELL(menu),menuitem);
menuitem = gtk_image_menu_item_new_from_stock
(GTK_STOCK_QUIT,accel_group);
gtk_menu_shell_append(GTK_MENU_SHELL(menu),menuitem);
g_signal_connect(G_OBJECT(menuitem),"activate",
G_CALLBACK(on_menu_activate),//(gpointer)("退出"));
(gpointer)("Exit"));
rootmenu = gtk_menu_item_new_with_label(" 文件 ");
gtk_menu_item_set_submenu(GTK_MENU_ITEM(rootmenu),menu);
menubar = gtk_menu_bar_new();
gtk_menu_shell_append(GTK_MENU_SHELL(menubar),rootmenu);
rootmenu = gtk_menu_item_new_with_label(" 编辑 ");
editmenu = gtk_menu_new();//编辑菜单
menuitem = gtk_image_menu_item_new_from_stock
(GTK_STOCK_CUT,accel_group);
gtk_menu_shell_append(GTK_MENU_SHELL(editmenu),menuitem);
g_signal_connect(G_OBJECT(menuitem),"activate",
G_CALLBACK(on_menu_activate),//(gpointer)("剪切"));
(gpointer)("Cut"));
menuitem = gtk_image_menu_item_new_from_stock
(GTK_STOCK_COPY,accel_group);
gtk_menu_shell_append(GTK_MENU_SHELL(editmenu),menuitem);
g_signal_connect(G_OBJECT(menuitem),"activate",
G_CALLBACK(on_menu_activate),//(gpointer)("复制"));
(gpointer)("Copy"));
menuitem = gtk_image_menu_item_new_from_stock
(GTK_STOCK_PASTE,accel_group);
gtk_menu_shell_append(GTK_MENU_SHELL(editmenu),menuitem);
g_signal_connect(G_OBJECT(menuitem),"activate",
G_CALLBACK(on_menu_activate),//(gpointer)("粘贴"));
(gpointer)("Paste"));
menuitem = gtk_image_menu_item_new_from_stock
(GTK_STOCK_FIND,accel_group);
gtk_menu_shell_append(GTK_MENU_SHELL(editmenu),menuitem);
g_signal_connect(G_OBJECT(menuitem),"activate",
G_CALLBACK(on_menu_activate),//(gpointer)("查找"));
(gpointer)("Search"));
gtk_menu_item_set_submenu(GTK_MENU_ITEM(rootmenu),editmenu);
gtk_menu_shell_append(GTK_MENU_SHELL(menubar),rootmenu);
rootmenu = gtk_menu_item_new_with_label(" 帮助 ");
helpmenu = gtk_menu_new();
menuitem = gtk_image_menu_item_new_from_stock
(GTK_STOCK_HELP,accel_group);
gtk_menu_shell_append(GTK_MENU_SHELL(helpmenu),menuitem);
g_signal_connect(G_OBJECT(menuitem),"activate",
G_CALLBACK(on_menu_activate),//(gpointer)("帮助"));
(gpointer)("Help"));
menuitem = gtk_menu_item_new_with_label(" 关于... ");
gtk_menu_shell_append(GTK_MENU_SHELL(helpmenu),menuitem);
g_signal_connect(G_OBJECT(menuitem),"activate",
G_CALLBACK(on_menu_activate),//(gpointer)("关于"));
(gpointer)("About"));
gtk_menu_item_set_submenu(GTK_MENU_ITEM(rootmenu),helpmenu);
gtk_menu_shell_append(GTK_MENU_SHELL(menubar),rootmenu);
gtk_box_pack_start(GTK_BOX(box),menubar,FALSE,FALSE,0);
gtk_widget_show_all(window);
gtk_main();
return FALSE;
}