GTK+ 简介GTK+ 是用于创建图形用户界面的库。 该库是用 C 编程语言创建的。 GTK+ 库也称为 GIMP 工具箱。 最初,该库是在开发 GIMP 图像处理器时创建的。 从那时起,GTK+ 成为 Linux 和 BSD Unix 下最受欢迎的工具包之一。 如今,开源世界中的大多数 GUI 软件都是在 Qt 或 GTK+ 中创建的。 GTK+ 是一个面向对象的应用编程接口。 面向对象的系统是使用 Glib 对象系统创建的,该系统是 GTK+ 库的基础。GObject还可以为各种其他编程语言创建语言绑定。 存在用于 C++ ,Python,Perl,Java,C# 和其他编程语言的语言绑定。
GTK+ 本身取决于以下库:
```
Glib
Pango
ATK
GDK
GdkPixbuf
Cario
```
Glib 是通用工具库。 它提供各种数据类型,字符串工具,启用错误报告,消息日志记录,使用线程以及其他有用的编程功能。Pango 是一个实现国际化的库。ATK 是可访问性工具包; 它提供了一些工具,可以帮助残障人士使用计算机。GDK 是基础图形系统提供的低级绘图和窗口功能的包装。 在 Linux 上,GDK 位于 X Server 和 GTK+ 库之间。 它处理基本的渲染,例如图形基元,光栅图形,光标,字体以及窗口事件和拖放功能。GdkPixbuf 库是用于图像加载和像素缓冲区操作的工具包。Cario 是用于创建 2D 向量图形的库。 自 2.8 版起,它已包含在 GTK+ 中。
Gnome 和 XFce 桌面环境已使用 GTK+ 库创建。 SWT 和 wxWidgets 是使用 GTK+ 的众所周知的编程框架。 使用 GTK+ 的著名软件应用包括 Firefox 或 Inkscape。
要编译 GTK+ 应用,我们有一个方便的工具pkg-config。 pgk-config返回有关已安装库的元数据。 如果我们要使用特定的库,它将为我们提供必要的依赖库,并包含我们需要的文件。 pkg-config程序从特殊的元数据文件中检索有关包的信息。
```
$ gcc -o simple simple.c `pkg-config --libs --cflags gtk+-2.0`
```
该行编译一个基本程序。 源代码由一个文件simple.c组成。
```
$ pkg-config --cflags gtk+-2.0 | xargs -n3
-pthread -I/usr/include/gtk-2.0 -I/usr/lib/x86_64-linux-gnu/gtk-2.0/include
-I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/gdk-pixbuf-2.0
-I/usr/include/pango-1.0 -I/usr/include/gio-unix-2.0/ -I/usr/include/freetype2
-I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/pixman-1
-I/usr/include/libpng12 -I/usr/include/harfbuzz
```
--cflags参数显示编译 GTK+ 程序所需的预处理器和编译标志,包括所有依赖项的标志。
$ pkg-config --libs gtk+-2.0 | xargs -n5
-lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgio-2.0 -lpangoft2-1.0
-lpangocairo-1.0 -lgdk_pixbuf-2.0 -lcairo -lpango-1.0 -lfontconfig
-lgobject-2.0 -lglib-2.0 -lfreetype
--libs参数列出了必要的库。
以下程序将打印 GTK+ 和 Glib 库的版本。
version.c
```
#include
int main(int argc, char *argv[]) {
gtk_init(&argc, &argv);
g_printf("GTK+ version: %d.%d.%d\n", gtk_major_version,
gtk_minor_version, gtk_micro_version);
g_printf("Glib version: %d.%d.%d\n", glib_major_version,
glib_minor_version, glib_micro_version);
return 0;
}
```
该程序使用内置常量。
```
$ ./version
GTK+ version: 2.24.23
Glib version: 2.40.2
```
这是version程序的输出。
在 GTK+ 编程教程的这一部分中,我们使用菜单和工具栏。菜单栏是 GUI 应用的常见部分。 它是位于各个菜单中的一组命令。
GtkMenuBar是创建菜单栏的窗口小部件。 它包含一对多GtkMenuItems。 菜单项是用户可以选择的对象。 GtkMenu实现了一个由GtkMenuItem对象列表组成的下拉菜单,用户可以对其进行导航和激活以执行应用功能。 GtkMenu附加到菜单栏的菜单项或另一个菜单的菜单项。下图显示了菜单栏及其菜单的结构。
![输入图片说明](http://docs.chinaredflag.cn/uploads/20230826/207d5ade72045d48ee31f50778af6432.png)
```
#include
int main(int argc, char *argv[]) {
GtkWidget *window;
GtkWidget *vbox;
GtkWidget *menubar;
GtkWidget *fileMenu;
GtkWidget *fileMi;
GtkWidget *quitMi;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_title(GTK_WINDOW(window), "Simple menu");
vbox = gtk_vbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
menubar = gtk_menu_bar_new();
fileMenu = gtk_menu_new();
fileMi = gtk_menu_item_new_with_label("File");
quitMi = gtk_menu_item_new_with_label("Quit");
gtk_menu_item_set_submenu(GTK_MENU_ITEM(fileMi), fileMenu);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), quitMi);
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), fileMi);
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
g_signal_connect(G_OBJECT(window), "destroy",G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(G_OBJECT(quitMi), "activate",G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
```
展示效果如下图
![输入图片说明](http://docs.chinaredflag.cn/uploads/20230826/9bb7fcc902905e327873d663c5314cc8.png)
在 GTK+ 编程教程的这一部分中,我们讨论事件系统。GTK+ 是事件驱动的系统。 所有 GUI 应用都是事件驱动的。 应用启动一个主循环,该循环不断检查新生成的事件。 如果没有事件,则应用将等待并且不执行任何操作。 在 GTK+ 中,事件是来自 X 服务器的消息。 当事件到达窗口小部件时,它可以通过发出信号对此事件做出反应。 GTK+ 程序员可以将特定的回调连接到信号。 回调是对信号做出反应的处理函数。
```
#include
void button_clicked(GtkWidget *widget, gpointer data) {
g_print("clicked\n");
}
int main(int argc, char *argv[]) {
GtkWidget *window;
GtkWidget *halign;
GtkWidget *btn;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "GtkButton");
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
halign = gtk_alignment_new(0, 0, 0, 0);
btn = gtk_button_new_with_label("Click");
gtk_widget_set_size_request(btn, 70, 30);
gtk_container_add(GTK_CONTAINER(halign), btn);
gtk_container_add(GTK_CONTAINER(window), halign);
g_signal_connect(G_OBJECT(btn), "clicked", G_CALLBACK(button_clicked), NULL);
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
```
在应用中,我们有两个信号:clicked信号和destroy信号。
```
g_signal_connect(G_OBJECT(btn), "clicked", G_CALLBACK(button_clicked), NULL);
```
我们使用g_signal_connect()函数将clicked信号连接到button_clicked()回调。
```
void button_clicked(GtkWidget *widget, gpointer data) {
g_print("clicked\n");
}
```
回调将"clicked"字符串打印到控制台。 回调函数的第一个参数是发出信号的对象。 在我们的例子中是单击按钮。 第二个参数是可选的。 我们可能会向回调发送一些数据。 在我们的案例中,我们没有发送任何数据; 我们为g_signal_connect()函数的第四个参数提供了NULL值。
```
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
```
如果按标题栏右上角的 x 按钮,或按 Atl + F4 ,则会发出destroy信号。 调用gtk_main_quit()函数,该函数将终止应用。
在 GTK+ 编程教程的这一部分中,我们使用对话框。对话框窗口或对话框是大多数现代 GUI 应用必不可少的部分。 对话被定义为两个或更多人之间的对话。 在计算机应用中,对话框是一个窗口,用于与应用“对话”。 对话框用于输入数据,修改数据,更改应用设置等。消息对话框是方便的对话框,可向应用的用户提供消息。 该消息包含文本和图像数据。
```
#include
void show_info(GtkWidget *widget, gpointer window) {
GtkWidget *dialog;
dialog = gtk_message_dialog_new(GTK_WINDOW(window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO,
GTK_BUTTONS_OK,
"Download Completed");
gtk_window_set_title(GTK_WINDOW(dialog), "Information");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}
void show_error(GtkWidget *widget, gpointer window) {
GtkWidget *dialog;
dialog = gtk_message_dialog_new(GTK_WINDOW(window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK,
"Error loading file");
gtk_window_set_title(GTK_WINDOW(dialog), "Error");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}
void show_question(GtkWidget *widget, gpointer window) {
GtkWidget *dialog;
dialog = gtk_message_dialog_new(GTK_WINDOW(window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_YES_NO,
"Are you sure to quit?");
gtk_window_set_title(GTK_WINDOW(dialog), "Question");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}
void show_warning(GtkWidget *widget, gpointer window) {
GtkWidget *dialog;
dialog = gtk_message_dialog_new(GTK_WINDOW(window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_WARNING,
GTK_BUTTONS_OK,
"Unallowed operation");
gtk_window_set_title(GTK_WINDOW(dialog), "Warning");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}
int main(int argc, char *argv[]) {
GtkWidget *window;
GtkWidget *table;
GtkWidget *info;
GtkWidget *warn;
GtkWidget *que;
GtkWidget *err;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 220, 150);
gtk_window_set_title(GTK_WINDOW(window), "Message dialogs");
table = gtk_table_new(2, 2, TRUE);
gtk_table_set_row_spacings(GTK_TABLE(table), 2);
gtk_table_set_col_spacings(GTK_TABLE(table), 2);
info = gtk_button_new_with_label("Info");
warn = gtk_button_new_with_label("Warning");
que = gtk_button_new_with_label("Question");
err = gtk_button_new_with_label("Error");
gtk_table_attach(GTK_TABLE(table), info, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 3, 3);
gtk_table_attach(GTK_TABLE(table), warn, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 3, 3);
gtk_table_attach(GTK_TABLE(table), que, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 3, 3);
gtk_table_attach(GTK_TABLE(table), err, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 3, 3);
gtk_container_add(GTK_CONTAINER(window), table);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
g_signal_connect(G_OBJECT(info), "clicked",
G_CALLBACK(show_info), (gpointer) window);
g_signal_connect(G_OBJECT(warn), "clicked",
G_CALLBACK(show_warning), (gpointer) window);
g_signal_connect(G_OBJECT(que), "clicked",
G_CALLBACK(show_question), (gpointer) window);
g_signal_connect(G_OBJECT(err), "clicked",
G_CALLBACK(show_error), (gpointer) window);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));
gtk_widget_show_all(window);
gtk_main();
return 0;
}
```
在示例中,我们显示了四种消息对话框:信息,警告,问题和错误消息对话框。
```
void show_question(GtkWidget *widget, gpointer window) {
GtkWidget *dialog;
dialog = gtk_message_dialog_new(GTK_WINDOW(window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_YES_NO,
"Are you sure to quit?");
gtk_window_set_title(GTK_WINDOW(dialog), "Question");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}
```
在show_question()函数中,我们弹出消息对话框。 消息对话框是使用gtk_message_dialog_new()调用创建的。 函数的参数指定我们创建哪种消息对话框。 GTK_MESSAGE_QUESTION常量创建一个问题类型对话框。 GTK_BUTTONS_YES_NO常量将在对话框中添加“是”和“否”按钮。 最后一个参数是我们在对话框中显示的文本。 gtk_dialog_run()函数显示对话框并阻塞主循环,直到对话框响应或被破坏为止。 该对话框必须使用gtk_widget_destroy()函数销毁。
![输入图片说明](http://docs.chinaredflag.cn/uploads/20230826/cd4012ac29fc438cbd624a3a167c9432.png)
![输入图片说明](http://docs.chinaredflag.cn/uploads/20230826/38e7ce4d41f44a21e9cc808ceffc9534.png)
![输入图片说明](http://docs.chinaredflag.cn/uploads/20230826/7ab283570a3dfe09d0fbb6a419feefbf.png)
![输入图片说明](http://docs.chinaredflag.cn/uploads/20230826/7e1ab6c96e1b394efc0606fc53938614.png)
在本章中,我们将展示如何在窗口或对话框中布置窗口小部件。在设计应用的 UI 时,我们决定要使用哪些小部件以及如何组织这些小部件。 为了组织窗口小部件,我们使用称为布局容器的专用非可见窗口小部件。 在本章中,我们将提及GtkAlignment,GtkFixed,GtkVBox和GtkTable。
GtkFixed容器将子窗口小部件放置在固定位置并具有固定大小。 此容器不执行自动布局管理。 因此,它不适用于翻译,字体更改或主题。 在大多数应用中,我们不使用GtkFixed容器。 可能会有一些专门的区域可以使用容器(例如,定位图或图像)
```
#include
int main(int argc, char *argv[]) {
GtkWidget *window;
GtkWidget *fixed;
GtkWidget *btn1;
GtkWidget *btn2;
GtkWidget *btn3;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "GtkFixed");
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
fixed = gtk_fixed_new();
gtk_container_add(GTK_CONTAINER(window), fixed);
btn1 = gtk_button_new_with_label("Button");
gtk_fixed_put(GTK_FIXED(fixed), btn1, 150, 50);
gtk_widget_set_size_request(btn1, 80, 30);
btn2 = gtk_button_new_with_label("Button");
gtk_fixed_put(GTK_FIXED(fixed), btn2, 15, 15);
gtk_widget_set_size_request(btn2, 80, 30);
btn3 = gtk_button_new_with_label("Button");
gtk_fixed_put(GTK_FIXED(fixed), btn3, 100, 100);
gtk_widget_set_size_request(btn3, 80, 30);
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
```
在我们的示例中,我们创建了三个按钮并将它们放置在固定坐标上。 当我们调整应用窗口的大小时,按钮将保持其大小和位置,如下图所示。
![输入图片说明](http://docs.chinaredflag.cn/uploads/20230826/23e14293a5e67c3ded9891537fd7493b.png)
```
#include
int main(int argc, char *argv[]) {
GtkWidget *window;
GtkWidget *align;
GtkWidget *lbl;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "GtkAlignment");
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_container_set_border_width(GTK_CONTAINER(window), 5);
align = gtk_alignment_new(0, 1, 0, 0);
lbl = gtk_label_new("bottom-left");
gtk_container_add(GTK_CONTAINER(align), lbl);
gtk_container_add(GTK_CONTAINER(window), align);
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
```
在示例中,标签位于窗口的左下角。
align = gtk_alignment_new(0, 1, 0, 0);
gtk_alignment_new()函数创建GtkAlignment容器。 参数的取值范围是 0 到 1。第一个参数是水平对齐方式,其中 0 左,1 右。 第二个参数是垂直对齐方式,其中 0 是顶部,1 是底部。 第三个参数是水平比例尺,它是子窗口小部件水平扩展以填充未使用空间的数量。 值为 0 表示子窗口小部件永远不应扩展。 最后一个参数是垂直刻度。
![输入图片说明](http://docs.chinaredflag.cn/uploads/20230826/a7b356c2d59024a625db9d6b072f360d.png)
一般 GUI 应用程序默认只有一个执行线程, 每次只执行一个操作, 如果某个操作耗时较长, 则用户界面会出现冻结的现象。 所以若某个操作的时间比较长一般会创建线程去处理。 GTK+应用程序中创建多线程,除了通过 POSIX 线程函数 pthread_create() 创建线程外 ,实际编程时 ,还可以通过 GTK+ 线程函数 g_thread_create()创建一个线程。值得注意的是GTK+的界面相关的代码不是线程安全函数,因此在新线程中执行图形绘制相关的代码时, 需要用函数 gdk_threads_enter()和 gdk_threads_leave()对GTK+代码进行包装, 以保证对 GTK 界面的操作是互斥的。 GTK+ 中使用多线程需进行相应初始化。 并且编译时需要链接 GTK 线程库 -lgthread-2.0
```
#include
#include
#include
#include
#include
gpointer deal_thread();
gpointer deal_thread1(gpointer data);
void gtk_thread_init(void);
int main(int argc, char *argv[])
{
gtk_thread_init();
gtk_init(&argc, &argv);
//创建新窗口
GtkWidget *window = NULL;
GtkWidget *vbox = NULL;
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "thread number");
gtk_widget_set_size_request(window, 200, 60);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
vbox = gtk_vbox_new(TRUE, 5);
gtk_container_add(GTK_CONTAINER(window), vbox);
//标签
GtkWidget *label = gtk_label_new("");
GtkWidget *labell = gtk_label_new("");
gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 5);
gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 5);
/*
*功能:
* 创建线程函数
*参数:
* func:线程执行的外部函数
* data:传给该外部函数的参数
* joinable:标志线程是否可分离
* error:出错代码返回地址
*GThread *g_thread_create(GThreadFunc func,
* gpointer data,
* gboolean joinable,
* GError **error);
*
*/
g_thread_create((GThreadFunc)deal_thread, label, FALSE, NULL);
g_thread_create((GThreadFunc)deal_thread1, label1, FALSE, NULL);
//pthread_t tid, tid1;
//pthread_create(&tid, NULL, deal_thread, label);
//pthread_create(&tid1, NULL, deal_thread1, label1);
//显示所有控件
gtk_widget_show_all(window);
gtk_main();
return 0;
}
//线程处理
gpointer deal_thread()
{
int i = 0;
while(1)
{
char buf[20] = "";
sprintf(buf, "%d", i++);
printf("i = %d\n", i);
usleep(500 * 1000);
//使用gdk_thread_*()刷新界面
//进入多线程互斥
gdk_thread_enter();
stk_label_set_text(GTK_LABEL(data),buf);
//退出多线程互斥
gdk_thread_leave();
}
}
gpointer deal_thread1(gpointer data)
{
int k = 10;
while(1)
{
char buf[20] = "";
sprintf(buf, "%d", k++);
printf("k = %d\n", k);
sleep(1);
//进入多线程互斥
gdk_thread_enter();
gtk_label_set_text(GTK_LABEL(data), buf);
//退出多线程互斥
gdk_thread_leave();
}
}
void gtk_thread_init(void)
{
if(FALSE == g_thread_supported())
{
g_thread_init(NULL);
}
gdk_threads_init();
}
```
首先, GTK+是C语言实现的。我们需要明白,对于C程序员而言,大部分都没有接触过图形界面编程,但是对于以后的发展,我们很有必要了解一下图形界面编程。那么问题又来了,我们是学C语言的,我们可能不太乐意学习面向对象语言(如 C++, Java, C#, Objective-C 等),现在市场上流行的图形界面工具库基本上都是用面向对象语言开发的。
再者,对于我们 C 程序员,我们可能只需了解一下图形界面开发过程,并不一定以后从事图形界面开发。所以,GTK是一个很好的选择。
实际上,用C语言开发的图形库还有一个MiniGUI,在国内医疗设备应用非常广泛,相反,GTK+在国内基本上没人用。但是,我们学习到的个别知识,以后未必一定能用上,对于我们而言, 重要的是学习方法。如果觉得 MiniGUI 比 GTK+ 在国内应用广,就想学习 MiniGUI,那我们是否应该学Android,因为它应用更广。技术是不断的更新的,但是万变不离其宗。
其实,学习 MiniGUI 和 GTK+ 都差不多。区别在于 MiniGUI 是国内开发, GTK+ 国外开发的。
学习GTK+,我们可以了解到图形界面开发的流程是怎么一个过程,这和 Android 应用开发差不多,假如,有那么一天我们真想做Android开发的工作,我们转过去也容易,因为我们有 C 语言的基础,也有图形界面的基础。
1、gtk窗口移动动画
```
/* $Id: app12.c $
Re: animating position of a top-level Gtk window
jiw July 2011 -- Offered without warranty under GPL v3
terms per http://www.gnu.org/licenses/gpl.html */
#include
#include
#include
#include
#include
typedef struct DATA { GTimer *timer; GtkWidget *window; int w, h; }
DataStruct;
gboolean timerEvent(void *dataset) {
enum { HalfTime=8, CycTime=2*HalfTime };
gulong micros;
DataStruct *data =dataset;
double t = fabs(fmod (g_timer_elapsed (data->timer, µs),CycTime));
int x = (t*data->w)/HalfTime, y = (t*data->h)/HalfTime;
gtk_window_move (GTK_WINDOW(data->window),tw-x, th-y);
return TRUE; /* Keep timeout running */
}
int main(int argc, char **argv) {
GtkWidget *vbox, *b;
GdkScreen *gds;
DataStruct data;
data.timer = g_timer_new();
gtk_init (&argc, &argv);
data.window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW(data.window), 200, 150);
g_signal_connect (G_OBJECT(data.window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
vbox = gtk_vbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER(data.window), vbox);
b = gtk_button_new_with_label ("Click to Exit");
gtk_box_pack_start (GTK_BOX(vbox), b, TRUE, TRUE, TRUE);
g_signal_connect (b, "clicked", G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all (data.window);
gds = gdk_screen_get_default (); /* Get pointer to screen */
data.w = gdk_screen_get_width (gds); /* Find out screen width */
data.h = gdk_screen_get_height (gds); /* Find out screen height */
printf ("Screen size = %d by %d", data.w, data.h); fflush(stdout);
g_timeout_add(3, timerEvent, &data); /* Create .003 sec timer */
gtk_main();
return (0);
}
```
2、gtk+窗口中添加动画(动图)
```
#include
static GtkWidget *ourgif;
int main(int argc, char **argv)
{
GtkWidget *window;
GtkWidget *vbox;
GtkWidget *hbox;
GtkWidget *label;
GtkWidget *image;
gint num = 0;
gchar *filename;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(gtk_main_quit), NULL);
vbox = gtk_vbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
label = gtk_label_new("直接引用GIF动画");
gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
image = gtk_image_new_from_file("hh.gif");
gtk_box_pack_start(GTK_BOX(vbox), image, FALSE, FALSE, 5);
gtk_widget_show_all(window);
gtk_main();
return TRUE;
}
```
3、GTK +的多线程动画
```
#include
#include
#include
#include
//the global pixmap that will serve as our buffer
static GdkPixmap *pixmap = NULL;
gboolean on_window_configure_event(GtkWidget * da, GdkEventConfigure * event, gpointer user_data){
static int oldw = 0;
static int oldh = 0;
//make our selves a properly sized pixmap if our window has been resized
if (oldw != event->width || oldh != event->height){
//create our new pixmap with the correct size.
GdkPixmap *tmppixmap = gdk_pixmap_new(da->window, event->width, event->height, -1);
//copy the contents of the old pixmap to the new pixmap. This keeps ugly uninitialized
//pixmaps from being painted upon resize
int minw = oldw, minh = oldh;
if( event->width < minw ){ minw = event->width; }
if( event->height < minh ){ minh = event->height; }
gdk_draw_drawable(tmppixmap, da->style->fg_gc[GTK_WIDGET_STATE(da)], pixmap, 0, 0, 0, 0, minw, minh);
//we're done with our old pixmap, so we can get rid of it and replace it with our properly-sized one.
g_object_unref(pixmap);
pixmap = tmppixmap;
}
oldw = event->width;
oldh = event->height;
return TRUE;
}
gboolean on_window_expose_event(GtkWidget * da, GdkEventExpose * event, gpointer user_data){
gdk_draw_drawable(da->window,
da->style->fg_gc[GTK_WIDGET_STATE(da)], pixmap,
// Only copy the area that was exposed.
event->area.x, event->area.y,
event->area.x, event->area.y,
event->area.width, event->area.height);
return TRUE;
}
static int currently_drawing = 0;
//do_draw will be executed in a separate thread whenever we would like to update
//our animation
void *do_draw(void *ptr){
//prepare to trap our SIGALRM so we can draw when we recieve it!
siginfo_t info;
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGALRM);
while(1){
//wait for our SIGALRM. Upon receipt, draw our stuff. Then, do it again!
while (sigwaitinfo(&sigset, &info) > 0) {
currently_drawing = 1;
int width, height;
gdk_threads_enter();
gdk_drawable_get_size(pixmap, &width, &height);
gdk_threads_leave();
//create a gtk-independant surface to draw on
cairo_surface_t *cst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
cairo_t *cr = cairo_create(cst);
//do some time-consuming drawing
static int i = 0;
++i; i = i % 300; //give a little movement to our animation
cairo_set_source_rgb (cr, .9, .9, .9);
cairo_paint(cr);
int j,k;
for(k=0; k<100; ++k){ //lets just redraw lots of times to use a lot of proc power
for(j=0; j < 1000; ++j){
cairo_set_source_rgb (cr, (double)j/1000.0, (double)j/1000.0, 1.0 - (double)j/1000.0);
cairo_move_to(cr, i,j/2);
cairo_line_to(cr, i+100,j/2);
cairo_stroke(cr);
}
}
cairo_destroy(cr);
//When dealing with gdkPixmap's, we need to make sure not to
//access them from outside gtk_main().
gdk_threads_enter();
cairo_t *cr_pixmap = gdk_cairo_create(pixmap);
cairo_set_source_surface (cr_pixmap, cst, 0, 0);
cairo_paint(cr_pixmap);
cairo_destroy(cr_pixmap);
gdk_threads_leave();
cairo_surface_destroy(cst);
currently_drawing = 0;
}
}
}
gboolean timer_exe(GtkWidget * window){
static int first_time = 1;
//use a safe function to get the value of currently_drawing so
//we don't run into the usual multithreading issues
int drawing_status = g_atomic_int_get(¤tly_drawing);
//if this is the first time, create the drawing thread
static pthread_t thread_info;
if(first_time == 1){
int iret;
iret = pthread_create( &thread_info, NULL, do_draw, NULL);
}
//if we are not currently drawing anything, send a SIGALRM signal
//to our thread and tell it to update our pixmap
if(drawing_status == 0){
pthread_kill(thread_info, SIGALRM);
}
//tell our window it is time to draw our animation.
int width, height;
gdk_drawable_get_size(pixmap, &width, &height);
gtk_widget_queue_draw_area(window, 0, 0, width, height);
first_time = 0;
return TRUE;
}
int main (int argc, char *argv[]){
//Block SIGALRM in the main thread
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGALRM);
pthread_sigmask(SIG_BLOCK, &sigset, NULL);
//we need to initialize all these functions so that gtk knows
//to be thread-aware
if (!g_thread_supported ()){ g_thread_init(NULL); }
gdk_threads_init();
gdk_threads_enter();
gtk_init(&argc, &argv);
GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(G_OBJECT(window), "expose_event", G_CALLBACK(on_window_expose_event), NULL);
g_signal_connect(G_OBJECT(window), "configure_event", G_CALLBACK(on_window_configure_event), NULL);
//this must be done before we define our pixmap so that it can reference
//the colour depth and such
gtk_widget_show_all(window);
//set up our pixmap so it is ready for drawing
pixmap = gdk_pixmap_new(window->window,500,500,-1);
//because we will be painting our pixmap manually during expose events
//we can turn off gtk's automatic painting and double buffering routines.
gtk_widget_set_app_paintable(window, TRUE);
gtk_widget_set_double_buffered(window, FALSE);
(void)g_timeout_add(33, (GSourceFunc)timer_exe, window);
gtk_main();
gdk_threads_leave();
return 0;
}
```