暂无相关内容

什么是GTK+

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+ 应用

要编译 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+ 菜单和工具栏

在 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+ 编程教程的这一部分中,我们讨论事件系统。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+ 对话框

在 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)

GTK+ 布局管理

在本章中,我们将展示如何在窗口或对话框中布置窗口小部件。在设计应用的 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)

GTK+ 创建多线程

一般 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+ 动画效果

首先, 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; } ```

GTK+ 聊天室代码

``` #include // 头文件 #include #include #include //定义数据结构sockaddr_in #include //定义socket函数以及数据结构 #include #include #include #include #include #include #include #include #include GtkWidget *window; //登录窗口 GtkWidget *home; //主窗口 int clientfd,b_file; struct sockaddr_in clientaddr; char user_name[50]; char fname[]="/var/tmp/"; int sem_id; //初始化信号量。 int init_sem(int sem_id, int init_value){ union semun{ int val; struct semid_ds *buf; unsigned short *array; }; union semun sem_union; sem_union.val = init_value; if(semctl(sem_id, 0, SETVAL, sem_union) == -1){ perror("Initialize semaphore"); //把"Initialize semaphore"输出到标准错误stderr。 return -1; } return 0; } //删除信号量。 int del_sem(int sem_id){ union semun{ int val; struct semid_ds *buf; unsigned short *array; }; union semun sem_union; if(semctl(sem_id, 1, IPC_RMID, sem_union)==-1){ perror("Delete semaphore"); //把"Delete semaphore"输出到标准错误stderr。 return -1; } } //p操作函数。 int sem_p(int sem_id){ struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = -1; sem_b.sem_flg = SEM_UNDO; if(semop(sem_id, &sem_b, 1)==-1){ perror("P operation"); //把"P operation"输出到标准错误stderr。 return -1; } return 0; } //v操作函数。 int sem_v(int sem_id){ struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = 1; sem_b.sem_flg = SEM_UNDO; if(semop(sem_id, &sem_b, 1) == -1){ perror("V operation"); //把"V operation"输出到标准错误stderr。 return -1; } return 0; } //处理登录 void deal_pressed(GtkWidget *button, gpointer entry){ int sendbytes; char *buff; struct hostent *host; char wel[]="Welcome"; host = gethostbyname("127.0.0.1"); //本地地址,可改为服务器端口地址。 buff = (char *)malloc(9); //向系统申请分配指定9个字节的内存空间。 const gchar *text = gtk_entry_get_text(GTK_ENTRY(entry)); if(strlen(text)==0){ printf("不能为空\n"); // 提示 不能为空 } else{ if ((clientfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("fail to create socket"); //把"fail to create socket"输出到标准错误stderr。 exit(1); } bzero(&clientaddr, sizeof(clientaddr)); //置clientaddr的前sizeof(clientaddr)个字节为零且包括‘\0’。 clientaddr.sin_family = AF_INET; //服务器地址族为对于TCP/IP协议的AF_INET。 clientaddr.sin_port = htons((uint16_t)atoi("8787")); //服务器端口将小端模式改变为大端模式。 clientaddr.sin_addr = *((struct in_addr *)host->h_addr); //服务器地址为0.0.0.0,即任意地址。 if (connect(clientfd, (struct sockaddr *)&clientaddr, sizeof(struct sockaddr)) == -1) { perror("fail to connect"); //把"fail to connect"输出到标准错误stderr。 exit(1); } if ((sendbytes = send(clientfd, text, strlen(text), 0)) == -1) { perror("fail to send"); //把"fail to send"输出到标准错误stderr。 exit(1); } if (recv(clientfd, buff, 7, 0) == -1) { perror("fail to recv"); //把"fail to recv"输出到标准错误stderr。 exit(1); } if(strcmp(buff,wel)==0){ strcpy(user_name,text); gtk_widget_destroy(window); }else{ // 弹窗 提醒 提示 昵称重复 GtkWidget *dialog; dialog = gtk_message_dialog_new((gpointer)window, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "该用户已登陆,请勿重复登陆,拒绝登录!"); gtk_window_set_title(GTK_WINDOW(dialog), "拒绝"); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); close(clientfd); } } } //登录界面 void login(int argc,char *argv[]){ // 初始化 gtk_init(&argc, &argv); // 创建顶层窗口 window = gtk_window_new(GTK_WINDOW_TOPLEVEL); // 设置窗口的标题 gtk_window_set_title(GTK_WINDOW(window), "登录"); // 设置窗口在显示器中的位置为居中 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); // 设置窗口的最小大小 gtk_widget_set_size_request(window, 300, 200); // 固定窗口的大小 gtk_window_set_resizable(GTK_WINDOW(window), FALSE); // "destroy" 和 gtk_main_quit 连接 g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); //创建一个固定容器 GtkWidget *fixed = gtk_fixed_new(); // 将布局容器放窗口中 gtk_container_add(GTK_CONTAINER (window), fixed); // 创建标签 GtkWidget *label_one = gtk_label_new("请输入昵称"); // 将按钮放在布局容器里 gtk_fixed_put(GTK_FIXED(fixed), label_one,120,30); // 行编辑的创建 GtkWidget *entry = gtk_entry_new(); //设置最大长度 gtk_entry_set_max_length(GTK_ENTRY(entry),50); // 设置行编辑允许编辑 gtk_editable_set_editable(GTK_EDITABLE(entry), TRUE); gtk_fixed_put(GTK_FIXED(fixed), entry,70,60); // 创建按钮 GtkWidget *button = gtk_button_new_with_label(" 登录 "); gtk_fixed_put(GTK_FIXED(fixed), button,130,110); //绑定点击事件 g_signal_connect(button, "pressed", G_CALLBACK(deal_pressed), entry); // 显示窗口全部控件 gtk_widget_show_all(window); //启动主循环 gtk_main(); } //发送目标用户窗口。 GtkWidget *entryname; // 文本框缓冲区。 GtkTextBuffer *bufferuser; GtkTextBuffer *buffernotice; //保存信息记录buf。 void savelinetxt(char buf[]){ struct flock lock3; //新建结构体flock为lock3。 lock3.l_whence = SEEK_SET; //相对位移量的起点设置为文件头0。 lock3.l_start = 0; //相对位移量。 lock3.l_len = 0; //设置加锁区域的长度。 //以上三个设置可以为整个文件加锁。 lock3.l_type = F_WRLCK; //初始化l_type为写入锁。 lock3.l_pid = -2; //初始化l_pid。 b_file = open(fname,O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); //打开fname,并将文件描述符存在b_file。 fcntl(b_file, F_SETLKW, &lock3); //以阻塞的方式为b_file设置结构为lock3的文件锁。 write(b_file, buf, strlen(buf)); //向b_file写入buf。 fcntl(b_file, F_UNLCK, &lock3); //给b_file解锁。 close(b_file); //关闭文件描述符b_file。 } //根据button的值发送消息时的自我维护。 void sendtouser(GtkButton *button, gpointer entry){ char *buf; //用来存储内容。 buf = (char *)malloc(1024); //向系统申请分配指定1024个字节的内存空间。 memset(buf, 0, 1024); //将buf中当前位置后面的1024个字节用0替换。 int sendbytes; //用来记录发送的字节数。 const gchar *text = gtk_entry_get_text(GTK_ENTRY(entry)); //获得行编辑entry的内容并静态建立text指针进行指定。 const char *but = gtk_button_get_label(button); //获得获取按钮button文本内容并静态建立but指针进行指定。 if(strlen(text)==0){ //如果text的长度为0 printf("不能为空\n"); //打印内容。 return; }else{ if(strcmp(but,"--发送--")==0){ //比较but与"--发送--",若相同返回0;若相同。 const gchar *name = gtk_entry_get_text(GTK_ENTRY(entryname)); //获得行编辑entryname的内容并静态建立name指针进行指定。 if(strlen(name)==0){ //如果name的长度为0 printf("name为空。\n"); //打印内容。 return; } sprintf(buf,"User:%d:%s%s\n",strlen(name),name,text); //将内容写入buf。 if ((sendbytes = send(clientfd, buf, strlen(buf), 0)) == -1) //将buf由指定的socket端口clientfd传给对方主机并将发送的字节数存进sendbytes;如果发送失败。 { perror("fail to send"); //把"fail to send"输出到标准错误stderr。 } return ; }else{ sprintf(buf,"%s%s\n","All::",text); //将内容写入buf。 if ((sendbytes = send(clientfd, buf, strlen(buf), 0)) == -1) //将buf由指定的socket端口clientfd传给对方主机并将发送的字节数存进sendbytes;如果发送失败。 { perror("fail to send"); //把"fail to send"输出到标准错误 stderr。 } return ; } } } //保存消息记录 void savetxt(GtkButton *button, gpointer entry){ struct flock lock1; //新建结构体flock为lock1。 lock1.l_whence = SEEK_SET; //相对位移量的起点设置为文件头0。 lock1.l_start = 0; //设置位移量。 lock1.l_len = 0; //设置加锁区域的长度。 //以上三个设置可以为整个文件加锁。 lock1.l_type = F_WRLCK; //初始化l_type为写入锁。 lock1.l_pid = -1; //初始化l_pid。 struct flock lock2; //新建结构体flock为lock2。 lock2.l_whence = SEEK_SET; //相对位移量的起点设置为文件头0。 lock2.l_start = 0; //设置位移量。 lock2.l_len = 0; //设置加锁区域的长度。 //以上三个设置可以为整个文件加锁。 lock2.l_type = F_RDLCK; //初始化l_type为读取锁。 lock2.l_pid = -1; //初始化l_pid。 int src_file, dest_file;//用来存储源文件与目标文件的描述符。 unsigned char buff[1024];//用来存储内容。 int real_read_len;//用来存储真实读取的字节数。 char txt_name[60];//用来存储文本记录名。 sprintf(txt_name,"%s%s","./msgsave_",user_name); //将内容写入buf。 src_file = open(fname, O_RDONLY);//打开fname,并将文件描述符存在src_file。 dest_file = open(txt_name,O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);//打开txt_name,并将文件描述符存在dest_file。 if (src_file< 0 || dest_file< 0)//如果源文件与目标文件打开失败。 { return; } fcntl(dest_file, F_SETLKW, &lock1);//以阻塞的方式为dest_file设置结构为lock1的文件锁。 fcntl(src_file, F_SETLKW, &lock2);//以阻塞的方式为src_file设置结构为lock2的文件锁。 while ((real_read_len = read(src_file, buff, sizeof(buff))) > 0)//读取源文件sec_file的sizeof(buff)个字节的数据并存在buff中,并记录真实读取的字节数。 { write(dest_file, buff, real_read_len);//向dest_fie写入buff内real_read_len字节的数据。 } fcntl(dest_file, F_UNLCK, &lock1);//给dest_file解锁。 fcntl(src_file, F_UNLCK, &lock2);//给src_file解锁。 close(dest_file);//关闭文件描述符dest_file。 close(src_file);//关闭文件描述符src_file。 } GtkTextBuffer *buffers; //读取消息记录 void readtxt(GtkButton *button, gpointer entry){ FILE *dest_file; char txt_name[60]; sprintf(txt_name,"%s%s","./msgsave_",user_name); GtkTextIter start,end; char StrLine[1024]; if((dest_file=fopen(txt_name,"r"))==NULL){ return; } while(!feof(dest_file)){ memset(StrLine,0,1024); fgets(StrLine,1024,dest_file); gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(buffers),&start,&end); gtk_text_buffer_insert(GTK_TEXT_BUFFER(buffers),&end,StrLine,strlen(StrLine));//将消息放入聊天信息框 } fclose(dest_file); } ///处理接受到的消息 void *strdeal(void *arg){ char sign[10]; char buf[1024]; char s[1024]; struct flock lock; //新建结构体flock为lock1 lock.l_whence = SEEK_SET; //相对位移量的起点设置为文件头0。 lock.l_start = 0; //设置位移量。 lock.l_len = 0; //设置加锁区域的长度。 lock.l_type = F_WRLCK; //初始化l_type为写入锁。 lock.l_pid = -2; //初始化l_pid while(1){ memset(s, 0, strlen(s)); //将s中当前位置后面的strlen(s)个字节用0替换。 memset(sign, 0, strlen(sign)); //将sign中当前位置后面的strlen(sign)个字节用0替换。 memset(buf, 0, strlen(buf)); //将buf中当前位置后面的strlen(buf)个字节用0替换。 if(recv(clientfd, s, 1024, 0) <= 0) //把clientfd的接收缓冲中的1024字节的数据copy到s中;如果接收错误。 { perror("fail to recv"); //把"fail to recv"输出到标准错误stderr。 close(clientfd); //关闭socket端口。 exit(1); } int i=0; int n=0; int j=0; for(i;i