如何在linux下用gtk开发图形界面应用程序

如何在linux下用gtk开发图形界面应用程序,第1张

 * 使用 GTK+ 和 Glade 快速开发 Linux 图形界面

** GTK+ 简介

*** 基本概念

GTK+ 是一种用于创建图形界面的库. 嗯, gnome 用的就是它, 不过并不是说只

有在 gnome 环境中才能用, 只要系统上装有 GTK 的库 (基本上有图形系统的个

人机都会有的啦) 就能运行基于 GTK 的程序. 除了 UNIX-like 平台, 它还移植

到 windows 上, 还有面向嵌入设备的 framebuffer 版本等等.

GTK 依赖两个重要的库. 一个是 GLib, 这并不是一个图形库, 也和 glibc 不同

, 它大抵上提供了一些接口以屏蔽系统的不同, 比如 gint 在哪里都是 32bit

的, 等等另一个是 GDK, 它是一个设备无关的图形库, 支持基本的画点, 以及

与窗口管理器沟通等任务, 由于 GTK 被设计成能在各个平台, 而非仅仅在

XWindow 环境下使用, 所以这个库也是必要的. 而在他们上面的 GTK 库, 就提

供了一些 widget --- 可以理解为控件啦, 不过窗口也是一个 widget 的说, 给

我们使用, 并提供了包装良好的事件响应机制.

*** GTK+ 开发基础

要开发基于 GTK 的软件, 必须先安装 GTK+ 的开发包. 检查是否正确安装的办

法是在安装后执行 ``pkg-config --cflags --libs gtk+-2.0'', 如果安装不正

确, 会提示找不到相应的包.

GTK 本身是基于 C 的库, 当然也有 C++ 等语言的 wrap, 但它的整个体系是面

向对象的. 其最基本的类是 GObject, GtkObject 继承了它, GtkObject 又派生

出我们最经常用到的 GtkWidget, 我们使用的所有窗体控件都派生于它. 于是,

在 C 环境中我们就要手动处理这些类转换, GTK 和底层的 GLib 等提供了一种

统一的转换方法, 比如把类型为 GtkWidget* 的 button 转换成 GtkButton 形,

写法是: GTK_BUTTON(button), 就酱子.

我不打算在这里列一个 GTK 的 hello world 占页面, 这个程序随便 google 一

下就能找到. 我们可以自己想一想一个图形界面应该如何建立.

首先我们要进行初始化, GTK 提供了 gtk_init() 作为初始化, 它检查程序参数

中的一些特定部分, 进行自己的设置. 调用方法如下:

gtk_init(&argc, &argv)

将 argc 和 argv 传指针的目的是 gtk_init 会对他们进行加工, 把 GTK 自己

用到的一些参数抽取出来.

接着, 我们必须要创建这些控件吧, GTK 中, 创建一个控件会返回一个

GtkWidget 类型 (或它的派生类) 的指针, 所有创建控件的函数的格式是

gtk_控件类型_new(参数表). 比如创建一个窗口的写法是:

GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL)

然后, 我们要设定事件响应函数, GTK 中的事件叫做 signal. GTK 的事件响应

函数接口应该是类似这样的

void

destroy(GtkWidget *widget, gpointer data)

我们把它连入到一个控件中的方法是这样的

g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy), NULL)

应该很好理解吧.

我们创建的咚咚, 要说明他们的位置和包含关系, GTK 用 container 来解决这

个问题, 它可以保证在窗口缩放的时候控件的摆放依然合理, container 的具体

使用不详细说, 后面经常要跟 container 打交道, 但基本上不用管 C 代码, 嗯

. 不过其实我们的任何的可视控件都继承自 container, 比如 button 也是一个

container, 它很多时候装的是一个 GtkLabel, 也就是用来显示纯文本的控件,

于是我们就能见到一个文本按钮, 嗯. 使用 container 的方法如下.

gtk_container_add(GTK_CONTAINER(window), button)

这样 button 就会占满整个 window 的控件, 我们后面可以看到可以用

GtkVBox, GtkHBox 等 container 分隔窗体.

好了, 我们可以准备启动程序了, 我们可以用 gtk_widget_show() 来显示各个

控件, 然后我们就应该进入到所谓的事件响应循环了, 这就要使用 gtk_main().

在程序运行过程中, 我们要 *** 纵控件, 比如说我们要在一个 entry 控件(文本框

) 中取出其中的文字, 这样的 *** 作使用 gtk_控件类型_动作(对象, 参数) 的形

式完成的, 比如刚才所说的任务, 做法是 gtk_entry_get_text(entry).

顺便讲讲 GTK 程序的退出, 关闭 GTK 的窗口 (即使是所有窗口) 也不代表程序

退出, 因为那说到底只是一个界面而已, 我们当然可以使用 exit() 的自爆法退

出, 但最好还是给 GTK 一个料理后事的机会, 在主窗口的 destroy 事件响应函

数上用 gtk_main_quit() 就好了.

编译 GTK 程序的办法也很简单.

gcc -o foo foo.c `pkg-config --cflags --libs gtk+-2.0`

** 使用 Glade 快速定制用户界面

好了, 说了一堆有的没的, 如果我现在说我上面说的那一堆中的大部分我们都不

会用到, 是不是觉得偶很歉扁? 前面的介绍只是给大家 GTK 这个库的一些基本

概念, 概念就好了, 我们倒真的不用拿这些来编程的. 想想, 一个复杂一点的界

面, 十几个控件, 再加上各种各样的 container, 自己挂事件, 再加上超常的命

名, 想想都恐怖.

这时候, Glade 横空出世了! 这是一个可视化的界面编辑器 --- 但它仅仅是一

个界面编辑器而已, 甚至连代码编辑框也没有给出. 先不管了, 打开 glade (安

装省略, 饶了我把, 记得装 libglade-dev), 应该很好懂了, 指指点点就能弄好

一个界面, 然后在属性窗的信号一栏中选取需要的信号, 设定响应函数, 非常好

弄.

玩了几分钟, 问题来了, 怎么把它变为程序啊? 提供类似功能的 IDE 如

Borland C++ Builder, 在点击创建新控件的时候, 我们能即使在代码窗看到自

动生成的代码, Glade 也可以采用这种形式, 在设计好界面并保存后, 按一下主

窗口的 build 按钮, 它就自动生成了界面代码, 根本不用自己写的. 打开代码

目录, callbacks.c 里已经有自动创建的事件响应的空函数, 在里面填处理, 然

后 configure make 就行了.

但是, 这样的开发方式还是有问题的. 第一, 自动生成的代码非常复杂, 可是很

多时候我们还是不能完全不看它, 处理和界面是混在一起了, 理解他们变得困难

其二, 由于以上的原因, 修改界面变得非常痛苦其三, 它给你生成那堆有的

没的配置文件不一定是你想要的.

因此 glade 提供了另一种方法, glade 编辑所得的 .glade 文件是一个 XML 文

件, 其实它已经完整地描述了界面, 我们能否采用一种简单的方式直接载入, 配

置它呢. 这样做, 我们的代码中就真真正正地去处了烦人的界面生成代码, 而专

注于处理部分了.

libglade 正是由于这个而来, 它能很好地完成上面所说的工作. 当我们用

glade 创建了一个 .glade 界面后, 用这种方法我们就可以建立界面, 运行程序

了.

#include <gtk/gtk.h>

#include <glade/glade.h>

GladeXML *GUI

int

main(int argc, char **argv)

{

gtk_init(&argc, &argv)

/* load the interface */

GUI = glade_xml_new("frame.glade", NULL, NULL)

/* connect the signals in the interface */

glade_xml_signal_autoconnect(GUI)

/* start the event loop */

gtk_main()

return 0

}

剩下的事情很简单, 如果你的 button 的 clicked 控件有一个响应函数

on_button_clicked, 你写

void

on_button_clicked(GtkWidget *widget, gpointer data)

{

// balabalabala

}

就可以了. 所以, 上面讲的一堆创建界面的方法, 其实大部分时候都用不着.

由于用到了 libglade, 我们的编译方法变为

gcc -o foo foo.c `pkg-config --cflags --libs libglade-2.0`

** 开发举例

一个很简单的程序, 密码学对称加密算法要用到的, 如果说是界面, 就是三个文

本框: 明文, 密码, 密文, 三个按钮, 加密, 解密, 清除, 完了. 为了实验众多

的算法, 我们加了一个下拉窗口, 用来选择算法. 我把它设计成一个 wrapper

和框架, 它不实现任何算法, 只是在界面中获取用户输入, 调用外部程序, 并把

结果显示出来而已. 这样, 实际的算法实现可以用纯 C 写的文本界面程序完成,

移植起来很方便, 在 windows 随便做个一样的界面做前端就整个移植过去了.

于是, 我们需要的窗体元素是 GtkEntry, GtkComboBoxEntry, GtkButton, 查查

手册, 我们用到的界面相关的函数只有以下几个:

- gtk_entry_get_text(), 用于获取文本框输入

- gtk_entry_set_text(), 用于在文本框中显示结果

- gtk_combo_box_get_active_text(), 用于在 ComboBoxEntry (派生自

ComboBox) 取出用户选中的算法.

另一个问题是, 他们都需要相应的对象指针做参数, 这应该怎样获得呢? 我们使

用 glade_xml_get_widget(GUI, "控件名") 就能取得控件了.

void enter_callback( GtkWidget *widget,

GtkWidget *entry )

{

const gchar *entry_text

entry_text = gtk_entry_get_text (GTK_ENTRY (entry))

printf("Entry contents: %s\n", entry_text)

}

void entry_toggle_editable( GtkWidget *checkbutton,

GtkWidget *entry )

{

gtk_editable_set_editable (GTK_EDITABLE (entry),

GTK_TOGGLE_BUTTON (checkbutton)->active)

}

void entry_toggle_visibility( GtkWidget *checkbutton,

GtkWidget *entry )

{

gtk_entry_set_visibility (GTK_ENTRY (entry),

GTK_TOGGLE_BUTTON (checkbutton)->active)

}

/* 我们常见的回调函数 */

void callback( GtkWidget *widget,

gpointer data )

{

g_print ("Hello again - %s was pressed\n", (char *) data)

}

int main( int argc, char *argv[])

{

GtkWidget *window

GtkWidget *ok

GtkWidget *close

GtkWidget *text_view

GtkTextBuffer *buffer

GtkWidget *vbox

GtkWidget *hbox

GtkWidget *halign

GtkWidget *valign

gtk_init(&argc, &argv)

//=============Build a window===========//

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), 350, 280)

gtk_window_set_title(GTK_WINDOW(window), "Labyrinth")

gtk_container_set_border_width(GTK_CONTAINER(window), 10)

g_signal_connect_swapped(G_OBJECT(window), "destroy",

G_CALLBACK(gtk_main_quit), G_OBJECT(window))

//=============Adjust layout============//

vbox = gtk_vbox_new(FALSE, 5)

valign = gtk_alignment_new(0, 1, 0, 0)

gtk_container_add(GTK_CONTAINER(vbox), valign)

gtk_container_add(GTK_CONTAINER(window), vbox)

hbox = gtk_hbox_new(TRUE, 3)

//=============Build tow button========//

ok = gtk_button_new_with_label("OK")

gtk_widget_set_size_request(ok, 70, 30)

gtk_container_add(GTK_CONTAINER(hbox), ok)

close = gtk_button_new_with_label("Close")

gtk_container_add(GTK_CONTAINER(hbox), close)

halign = gtk_alignment_new(1, 0, 0, 0)

close = gtk_button_new_from_stock (GTK_STOCK_CLOSE)

gtk_container_add(GTK_CONTAINER(halign), hbox)

gtk_box_pack_start(GTK_BOX(vbox), halign, FALSE, FALSE, 0)

g_signal_connect_swapped (G_OBJECT (close), "clicked",

G_CALLBACK (gtk_widget_destroy),

window)

GTK_WIDGET_SET_FLAGS (close, GTK_CAN_DEFAULT)

gtk_widget_grab_default (close)

gtk_widget_show (close)

//=============Build a TestView==========//

text_view=gtk_text_view_new()/*创建文本框构件*/

gtk_widget_set_size_request(text_view,200,200)

gtk_container_add(GTK_CONTAINER(window),vbox)

gtk_box_pack_start(GTK_BOX(vbox),text_view,FALSE,FALSE,0)

buffer=gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view))

gtk_widget_show(text_view)

//=============================

GtkWidget *entry

gint tmp_pos

entry = gtk_entry_new ()

gtk_entry_set_max_length (GTK_ENTRY (entry), 50)

g_signal_connect (G_OBJECT (entry), "OK",

G_CALLBACK (enter_callback), entry)

gtk_entry_set_text (GTK_ENTRY (entry), "hello")

tmp_pos = GTK_ENTRY (entry)->text_length

gtk_editable_insert_text (GTK_EDITABLE (entry), " world", -1, &tmp_pos)

gtk_editable_select_region (GTK_EDITABLE (entry),

0, GTK_ENTRY (entry)->text_length)

//tmp_pos = GTK_ENTRY (entry)->text_length

//gtk_editable_insert_text (GTK_EDITABLE (entry), " hello ", -1, &tmp_pos)

//gtk_editable_select_region (GTK_EDITABLE (entry),

// 0, GTK_ENTRY (entry)->text_length)

gtk_box_pack_start (GTK_BOX (vbox), entry, TRUE, TRUE, 0)

gtk_widget_show (entry)

gtk_widget_show_all(window)

gtk_main()

return 0

}

你要的API是gtk_container_set_chain

void gtk_container_set_focus_chain(GtkContainer *container,

GList *focusable_widgets)

focusable_widgets就是tab顺序的控件列表,必须是container的子控件,GList应该会用吧。

如果要设置不同容器中控件的顺序,嵌套就行;只是不能有跨控件的顺序。我实现了一个简单的程序来演示TAB顺序的设置。如下:

#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_position(GTK_WINDOW(window),GTK_WIN_POS_CENTER)

gtk_window_set_default_size(GTK_WINDOW(window),480,360)

g_signal_connect(G_OBJECT(window),"destroy",

G_CALLBACK(gtk_main_quit),NULL)

GtkWidget *box=gtk_box_new(GTK_ORIENTATION_VERTICAL,5)

GtkWidget *box1=gtk_box_new(GTK_ORIENTATION_VERTICAL,5)

GtkWidget *box2=gtk_box_new(GTK_ORIENTATION_VERTICAL,5)

gtk_container_add(GTK_CONTAINER(window),box)

gtk_box_pack_start(GTK_BOX(box),box1,TRUE,TRUE,0)

gtk_box_pack_start(GTK_BOX(box),box2,TRUE,TRUE,0)

GtkWidget *entry1,*entry2,*entry3,*entry4,*entry5,*entry6

GList *chain

entry1=gtk_entry_new()

gtk_entry_set_text(GTK_ENTRY(entry1),"entry1")

entry2=gtk_entry_new()

gtk_entry_set_text(GTK_ENTRY(entry2),"entry2")

entry3=gtk_entry_new()

gtk_entry_set_text(GTK_ENTRY(entry3),"entry3")

entry4=gtk_entry_new()

gtk_entry_set_text(GTK_ENTRY(entry4),"entry4")

entry5=gtk_entry_new()

gtk_entry_set_text(GTK_ENTRY(entry5),"entry5")

entry6=gtk_entry_new()

gtk_entry_set_text(GTK_ENTRY(entry6),"entry6")

gtk_box_pack_start(GTK_BOX(box1),entry1,TRUE,TRUE,0)

gtk_box_pack_start(GTK_BOX(box1),entry2,TRUE,TRUE,0)

gtk_box_pack_start(GTK_BOX(box1),entry3,TRUE,TRUE,0)

gtk_box_pack_start(GTK_BOX(box2),entry4,TRUE,TRUE,0)

gtk_box_pack_start(GTK_BOX(box2),entry5,TRUE,TRUE,0)

gtk_box_pack_start(GTK_BOX(box2),entry6,TRUE,TRUE,0)

/*

* 下面设置TAB顺序,不能设置跨容器的顺序

* 比如524136,634251都不行

* 但是可以132465,或者645312

* 下面设置TAB顺序为564213

*/

/* 先设置两个GtkBox的顺序 */

chain=NULL

chain=g_list_append(chain,box2)

chain=g_list_append(chain,box1)

gtk_container_set_focus_chain(GTK_CONTAINER(box),chain)

g_list_free(chain)

/* 设置box2中的子控件顺序564 */

chain=NULL

chain=g_list_append(chain,entry5)

chain=g_list_append(chain,entry6)

chain=g_list_append(chain,entry4)

gtk_container_set_focus_chain(GTK_CONTAINER(box2),chain)

g_list_free(chain)

/* 设置box1中的子控件顺序213 */

chain=NULL

chain=g_list_append(chain,entry2)

chain=g_list_append(chain,entry1)

chain=g_list_append(chain,entry3)

gtk_container_set_focus_chain(GTK_CONTAINER(box1),chain)

g_list_free(chain)

gtk_widget_show_all(window)

gtk_main()

return 0

}


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/8124017.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-13
下一篇 2023-04-13

发表评论

登录后才能评论

评论列表(0条)

保存