从两个简单的函数开始
只有一个窗口
带一个按钮的程序
增加点东西
点击按钮时候,做点事情
多几个按钮
封装
使用box封装
使用table封装
widget总结
没有窗口的widget
按钮控件(button widget)
普通按钮(normal buttons)
开关按钮(toggle buttons)

从两个简单的函数开始

只有一个窗口

创建一个base.c文件,内容如下: <source lang="c"> <gtk/gtk.h>

int main( int argc,

char *argv[] ) { GtkWidget *window;

gtk_init (&argc, &argv);

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

gtk_main ();

return 0; } </source>

编译运行这个程序:

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

第一个gtk程序
第一个gtk程序

带一个按钮的程序

实现那个”hello,world“:源代码

hello示例
hello示例

编译运行这个hello.c:

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

增加点东西

点击按钮时候,做点事情

这个hell2程序可以在点击hello按钮的时候弹出一个窗口:源代码

编辑hello2.c,并运行:

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

多几个按钮

添加几个按钮:源代码

hello3示例
hello3示例

封装

使用box封装

gtk中的widget是封装在一起的,最常用的封装函数有:

gtk_hbox_new(等分空间与否,0);
gtk_vbox_new(等分空间与否,0);
gtk_box_pack_start(父控件,子控件,扩展与否,填充与否,0)
gtk_box_pack_end(父控件,子控件,扩展与否,填充与否,0)

这有几个典型封装示例:

gtk_hbox_new示例1
gtk_hbox_new示例1

void gtk_box_pack_start( GtkBox    *box,
                         GtkWidget *child,
                         gboolean   expand,
                         gboolean   fill,
                         guint      padding );

第一个参数和第二个参数都是一个Widget类型对象,把第二个控件封装到第一个控件里面。expand参数为TRUE时候,控件可以填充多余空间;为FALSE的时候,控件收缩到自身大小,不填充多余控件。这个值在gtk_hbox_new和gtk_vbox_new的一个值为TRUE的时候,就只有TRUE值,即此时FALSE不起作用。

fill的值只有expand值为TRUE有作用(如果gtk_hbox_new和gtk_vbox_new的第一个参数为TRUE,那么fill的值总是起作用)。FALSE表示控件不填充剩余空间,TRUE自动填充。

GtkWidget *gtk_hbox_new ( gboolean homogeneous,
                          gint     spacing );

gtk_vbox_new和gtk_hbox_new相似,这里以gtk_hbox_new为例。homogeneous([.hɔməu'dʒi:njəs] adj. 同种的,同质的,均质的)指示box怎样对待包含在其中的控件。TRUE表示同等对待,视觉表示上就是每个控件占用的控件大小相等。FALSE为否。如果设置为TRUE,gtk_box_pack_start和gtk_box_pack_end函数都默认expand参数总是TURE。

下图是spacing和gtk_box_pack_start/end中的padding参数的效果:

gtk_hbox_new示例2
gtk_hbox_new示例2

spacing是加载box中个控件对象之间的距离。padding是每个控件对象两边的距离。

使用table封装

GtkWidget *gtk_table_new( guint    rows,
                          guint    columns,
                          gboolean homogeneous );

第一个参数是行(rows),第二个参数是列(columns),第三个参数homogeneous指示table boxes是否自动调整大小。如果值为TRUE,表格中每个boxes都调整到最大的widget对象大小。FALSE表示使用收缩模式。行高是一行中的最高的widget的值,行宽是一列中最宽的widget的值。

一个rows=2,columns=2的table会是下面这个样子:

 0          1          2
0+----------+----------+
 |          |          |
1+----------+----------+
 |          |          |
2+----------+----------+

上面的坐标系统(co-ordinate)从左上角开始(upper left hand corner)。要把一个widget放入box,使用下面函数:

void gtk_table_attach( GtkTable         *table,
                       GtkWidget        *child,
                       guint            left_attach,
                       guint            right_attach,
                       guint            top_attach,
                       guint            bottom_attach,
                       GtkAttachOptions xoptions,
                       GtkAttachOptions yoptions,
                       guint            xpadding,
                       guint            ypadding );

第一,第二个参数很明了。left_attach,right_attach是指widget要装入table的左右边界的y(纵)坐标,top_attach,bottom_attach指上下边界的x(横)坐标。都是gint(整形)值。上面是一个2x2的table,如果我们想把一个widget放到右下角的box中,可以设置:left_attach=1,right_attach=2,top_attach=1,bottom_attach=2。

table可以容许你把一个widget放到几个box(这box值table中的格子)中去。如果我们想把widget放到上表的最上一行两个表格中:left_attach=0,right_attach=2,top_attach=0,bottom_attach=1。

xoptions和yoptions指示封装选项,可以使用OR得到多个特性。它们的值可以有:

padding参数和box封装中的意义相同。都是调整widget周边的空白空间。以pixels为单位的整数。

因为gtk_table_attach()有很多选项,所以gtk提供了一个带默认选项的函数:

void gtk_table_attach_defaults( GtkTable  *table,
                                GtkWidget *widget,
                                guint      left_attach,
                                guint      right_attach,
                                guint      top_attach,
                                guint      bottom_attach );

defaults函数的X和Y选项设置为GTK_FILL|GTK_EXPAND,X和Y的padding值为0.其余参数意义和上面提到的相同。

同样还有gtk_table_set_row_spacing()和gtk_table_set_col_spacing(),它们设置table的行距和列距。对于列,spacing值为列右边的距离;对于行,spacing值为行下面的距离。注意,对于最后一行和列都不起作用。

void gtk_table_set_row_spacing( GtkTable *table,
                                guint     row,  // 哪一行
                                guint     spacing );
void gtk_table_set_col_spacing ( GtkTable *table,
                                 guint     column, // 哪一列
                                 guint     spacing );

举例:创建一个2x2的table,放入3个widget,最后一个widget占用2个表格。源代码

GTK-TABLE学习示例
GTK-TABLE学习示例

widget总结

Widget显示

在GTK中创建一个widget通常有几步:

  1. gtk_*_new()创建一个新的widget。
    • gtk_window_new()
    • gtk_vbox_new()
    • gtk_hbox_new()
    • gtk_table_new()
    • gtk_button_new_with_label()
  2. 链接信号(signal)或事件(event)到我们的回调函数。
  3. 设置widget的属性
  4. 封装widget到container(通常译为包容器)
    • gtk_container_add()
    • gtk_box_pack_start()
    • gtk_box_pack_end()
    • gtk_table_attach_defaults()
  5. gtk_widget_show()显示widget

gtk_widget_show()显示已经设置好的widget,可以再次调用gtk_widget_hide()隐藏widget。显示的顺序不重要,都是通常都是显示好里面包含的widget再显示外面的widget。而且设置好一个widget就显示它,可以让这个变量再次使用。

Casting(类型检查宏)

gtk中有很多类型检查宏,可以在编译的时候防止我们出错:

  G_OBJECT (object)
  GTK_WIDGET (widget)
  GTK_OBJECT (object)
  GTK_SIGNAL_FUNC (function)
  GTK_CONTAINER (container)
  GTK_WINDOW (window)
  GTK_BOX (box)

所有的GtkWidget都是从GObject基类派生出来的。所以可以使用G_OBJECT宏代替其他宏。

g_signal_connect( G_OBJECT (button), "clicked",
                  G_CALLBACK (callback_function), callback_data);

很多widget都可以作为container(包容器),所以很多widget都派生自GTK_CONTAINER类。在使用这样的widget处,使用这个类也是可以的。

widget继承

下面有一个类继承关系图,只有少数不赞成的类和附加类不在里面:

GObject
 |
 GtkObject
  +GtkWidget
  | +GtkMisc
  | | +GtkLabel
  | | | `GtkAccelLabel
  | | +GtkArrow
  | | `GtkImage
  | +GtkContainer
  | | +GtkBin
  | | | +GtkAlignment
  | | | +GtkFrame
  | | | | `GtkAspectFrame
  | | | +GtkButton
  | | | | +GtkToggleButton
  | | | | | `GtkCheckButton
  | | | | |   `GtkRadioButton
  | | | | `GtkOptionMenu
  | | | +GtkItem
  | | | | +GtkMenuItem
  | | | |   +GtkCheckMenuItem
  | | | |   | `GtkRadioMenuItem
  | | | |   +GtkImageMenuItem
  | | | |   +GtkSeparatorMenuItem
  | | | |   `GtkTearoffMenuItem
  | | | +GtkWindow
  | | | | +GtkDialog
  | | | | | +GtkColorSelectionDialog
  | | | | | +GtkFileSelection
  | | | | | +GtkFontSelectionDialog
  | | | | | +GtkInputDialog
  | | | | | `GtkMessageDialog
  | | | | `GtkPlug
  | | | +GtkEventBox
  | | | +GtkHandleBox
  | | | +GtkScrolledWindow
  | | | `GtkViewport
  | | +GtkBox
  | | | +GtkButtonBox
  | | | | +GtkHButtonBox
  | | | | `GtkVButtonBox
  | | | +GtkVBox
  | | | | +GtkColorSelection
  | | | | +GtkFontSelection
  | | | | `GtkGammaCurve
  | | | `GtkHBox
  | | |   +GtkCombo
  | | |   `GtkStatusbar
  | | +GtkFixed
  | | +GtkPaned
  | | | +GtkHPaned
  | | | `GtkVPaned
  | | +GtkLayout
  | | +GtkMenuShell
  | | | +GtkMenuBar
  | | | `GtkMenu
  | | +GtkNotebook
  | | +GtkSocket
  | | +GtkTable
  | | +GtkTextView
  | | +GtkToolbar
  | | `GtkTreeView
  | +GtkCalendar
  | +GtkDrawingArea
  | | `GtkCurve
  | +GtkEditable
  | | +GtkEntry
  | |   `GtkSpinButton
  | +GtkRuler
  | | +GtkHRuler
  | | `GtkVRuler
  | +GtkRange
  | | +GtkScale
  | | | +GtkHScale
  | | | `GtkVScale
  | | `GtkScrollbar
  | |   +GtkHScrollbar
  | |   `GtkVScrollbar
  | +GtkSeparator
  | | +GtkHSeparator
  | | `GtkVSeparator
  | +GtkInvisible
  | +GtkPreview
  | `GtkProgressBar
  +GtkAdjustment
  +GtkCellRenderer
  | +GtkCellRendererPixbuf
  | +GtkCellRendererText
  | +GtkCellRendererToggle
  +GtkItemFactory
  +GtkTooltips
  `GtkTreeViewColumn

没有窗口的widget

有一些widget没有窗口,如果需要处理它们的信号事件,需要是用EventBox。

GtkAlignment
GtkArrow
GtkBin
GtkBox
GtkButton
GtkCheckButton
GtkFixed
GtkImage
GtkLabel
GtkMenuItem
GtkNotebook
GtkPaned
GtkRadioButton
GtkRange
GtkScrolledWindow
GtkSeparator
GtkTable
GtkToolbar
GtkAspectFrame
GtkFrame
GtkVBox
GtkHBox
GtkVSeparator
GtkHSeparator

按钮控件(button widget)

普通按钮(normal buttons)

按钮按键通常有下面几个函数:

gtk_button_new_with_label("标签按钮") //创建带标签的按钮
gtk_button_new_with_mnemonic("_Press") //创建带提示符和其下划线的按钮,
                               提示符有快捷键功能。
gtk_button_new_from_stock() 创建包含图片和文字的按钮
gtk_button_new() 创建一个空白按钮,可以把一个封装其他widget的box发到里面
mnemonic [ni(:)'mɔnik(əl)]助记的,记忆的

在table例子基础上修改一下,把最后一个quit按钮改成一格大小,再增加一个button到最后一格里面。这个button包含一个box,box包含一个xpm图片和一个label。源代码 示例中使用的xpm图片

带图片的按钮示例
带图片的按钮示例

按钮widget可以有下面这些信号:

pressed  按下
released 释放
clicked  点击
enter    鼠标移到按钮上
leave    鼠标移出按钮

开关按钮(toggle buttons)

开关按钮从普通按钮派生而来(derived from normal buttons),不过开关按钮只有两种状态,可以通过鼠标点击交换地改变状态。

创建开关按钮:

GtkWidget *gtk_toggle_button_new (void);
GtkWidget *gtk_toggle_button_new_with_label (const gchar *label);
GtkWidget *gtk_toggle_button_new_with_mnemonic ( const gchar *label);