2007年3月8日星期四

The Widgets


几种小部件综合练习一下.
/**
* Box 布局
*/

#include <gtk/gtk.h>
#include <string.h>


/* Signal Functions ---------------------------------------------
----------------------------------------------------------------- */

// 关闭程序
void CloseTheApp ( GtkWidget * widget, gpointer data ) {
gtk_main_quit ();
}

// 点击普通按钮
void ButtonClicked ( GtkButton * button, gpointer data ) {
g_print ( "标签里面的内容是: " );
g_print ( gtk_label_get_text ( GTK_LABEL ( data ) ) );
g_print ( "\n" );
}

// 文本域内容改变
void EntryChanged ( GtkEntry * entry, gpointer data ) {
gchar * entry_text;
entry_text = ( gchar * )gtk_entry_get_text ( entry );
gtk_label_set_text ( GTK_LABEL ( data ), entry_text );
}

// 点击开关按钮, 开关按钮状态变化
void ToggleButtonClicked ( GtkButton * button, gpointer data ) {
gboolean visibility = TRUE; // 是否可见
// 开关按钮被按下去
if ( gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON ( button ) ) ) {
visibility = FALSE;
gtk_button_set_label ( GTK_BUTTON ( button ), "公开文本域内容" );
// 如果开关没按下去
} else {
visibility = TRUE;
gtk_button_set_label ( GTK_BUTTON ( button ), "保密文本域内容" );
}
// 改变文本域状态
gtk_entry_set_visibility ( GTK_ENTRY ( data ), visibility );
}

// 单选按钮状态变换
void RadioButtonToggled ( GtkRadioButton * button, gpointer data ) {
gchar * str = NULL;
str = ( gchar * ) gtk_button_get_label ( GTK_BUTTON ( button ) );

// 如果按钮被激活
if ( gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON ( button ) ) ) {
// 被激活的是 Enable
if ( strcmp ( str, "Enable" ) == 0 ) {
gtk_entry_set_editable ( GTK_ENTRY ( data ), TRUE ); // 文本域内容可以更改
// 被激活的是 Disable
} else {
gtk_entry_set_editable ( GTK_ENTRY ( data ), FALSE ); // 文本域内容不能更改
}
}
}

// 复选按钮状态变换
void CheckButtonToggled ( GtkCheckButton * button, gpointer data ) {
// 如果按钮被激活
if ( gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON ( button ) ) ) {
gtk_widget_hide ( GTK_WIDGET ( data ) ); // 隐藏一个指定的小部件
} else {
gtk_widget_show ( GTK_WIDGET ( data ) ); // 显示一个指定的小部件
}
}


/* GUI Functions ---------------------------------------------
----------------------------------------------------------------- */

// 创建一个窗口
GtkWidget * MakeWindow () {
GtkWidget * window;
window = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
gtk_container_set_border_width ( GTK_CONTAINER ( window ), 10 );
gtk_window_set_title ( GTK_WINDOW ( window ), "The Widgets" );
gtk_window_set_position ( GTK_WINDOW ( window ), GTK_WIN_POS_CENTER ); // 显示在屏幕中间
gtk_signal_connect ( GTK_OBJECT ( window ),
"destroy",
GTK_SIGNAL_FUNC ( CloseTheApp ),
NULL );

return window;
}

// 水平添加两个小部件到一个 Box, 并返回
GtkWidget * HPanel ( GtkWidget * widget0, GtkWidget * widget1 ) {
GtkWidget * box;
box = gtk_hbox_new ( TRUE, 10 );

gtk_box_pack_start ( GTK_BOX ( box ), widget0, FALSE, TRUE, 0 );
gtk_box_pack_start ( GTK_BOX ( box ), widget1, FALSE, TRUE, 0 );

return box;
}

// 添加一个单选按钮到一个组和 Box 中去
GSList * AddRedioButton ( GtkWidget * box, gchar * label, GSList * group, GtkWidget * entry ) {
GtkWidget * radiobutton;

// 初始化单选按钮, 并将它加到组里面去
radiobutton = gtk_radio_button_new_with_label ( group, label );
group = gtk_radio_button_group ( GTK_RADIO_BUTTON ( radiobutton ) );
//将单选按钮添加到 Box 里面去
gtk_box_pack_start ( GTK_BOX ( box ), radiobutton, FALSE, TRUE, 0 );

gtk_signal_connect ( GTK_OBJECT ( radiobutton ),
"toggled",
GTK_SIGNAL_FUNC ( RadioButtonToggled ),
entry );

return group;
}

// 添加一个复选按钮到 Box 中去
void AddCheckButton ( GtkWidget * box, gchar * label, GtkWidget * widget ) {
GtkWidget * checkbutton;

// 初始化复选按钮
checkbutton = gtk_check_button_new_with_label ( label );
//将复选按钮添加到 Box 里面去
gtk_box_pack_start ( GTK_BOX ( box ), checkbutton, FALSE, TRUE, 0 );

gtk_signal_connect ( GTK_OBJECT ( checkbutton ),
"toggled",
GTK_SIGNAL_FUNC ( CheckButtonToggled ),
widget );
}


/* Main Functions ---------------------------------------------
----------------------------------------------------------------- */

gint main ( gint argc, gchar * argv[] ) {
GtkWidget * window;
GtkWidget * mainbox;

GSList * group = NULL;
GtkWidget * frame_left;
GtkWidget * box_left;
GtkWidget * frame_right;
GtkWidget * box_right;

GtkWidget * entry;
GtkWidget * label;
GtkWidget * togglebutton;
GtkWidget * button;

gtk_init ( &argc, &argv );
window = MakeWindow ();

// 标签
label = gtk_label_new ( NULL );
gtk_label_set_width_chars ( GTK_LABEL ( label ), 16 );
gtk_label_get_single_line_mode ( GTK_LABEL ( label ) );

// 文本域
entry = gtk_entry_new ();
gtk_entry_set_width_chars ( GTK_ENTRY ( entry ), 16 );
gtk_signal_connect ( GTK_OBJECT ( entry ),
"changed",
GTK_SIGNAL_FUNC ( EntryChanged ),
label );

// 开关按钮
togglebutton = gtk_toggle_button_new_with_label ( "保密文本域内容" );
gtk_signal_connect ( GTK_OBJECT ( togglebutton ),
"clicked",
GTK_SIGNAL_FUNC ( ToggleButtonClicked ),
entry );

// 按钮
button = gtk_button_new_with_label ( "显示标签内容" );
gtk_signal_connect ( GTK_OBJECT ( button ),
"clicked",
GTK_SIGNAL_FUNC ( ButtonClicked ),
label );

// 单选按钮
frame_left = gtk_frame_new ( "文本域状态" );
box_left = gtk_vbox_new ( TRUE, 0 );
group = AddRedioButton ( box_left, "Enable", group, entry );
group = AddRedioButton ( box_left, "Disable", group, entry );
gtk_container_add ( GTK_CONTAINER ( frame_left ), box_left );

// 复选按钮
frame_right = gtk_frame_new ( "隐藏小部件" );
box_right = gtk_vbox_new ( TRUE, 0 );
AddCheckButton ( box_right, "文本域", entry );
AddCheckButton ( box_right, "标签", label );
gtk_container_add ( GTK_CONTAINER ( frame_right ), box_right );

// 主面板
mainbox = gtk_vbox_new ( FALSE, 10 ); // 初始化主面板为垂直分布, 紧凑分配空间, 间隙为 10
gtk_box_pack_start ( GTK_BOX ( mainbox ), HPanel ( frame_left, frame_right ), FALSE, FALSE, 0 );
gtk_box_pack_start ( GTK_BOX ( mainbox ), HPanel ( entry, label ), FALSE, FALSE, 0 );
gtk_box_pack_start ( GTK_BOX ( mainbox ), HPanel ( togglebutton, button ), FALSE, FALSE, 0 );
gtk_container_add ( GTK_CONTAINER ( window ), mainbox ); // 将主面板添加到窗口上

gtk_widget_show_all ( window );
gtk_main ();
return 0;
}

2007年3月6日星期二

Table 布局


Table 是 GTK+ 的另一种布局, 其使用方式类似于使用 jxl 构建 excel 文件的方式. 创建一个表格很简单.

GtkWidget * gtk_table_new ( gint rows, gint columns, gboolean homegenous );
rows 是行数.
columns 是列数.
homegenous 和 Box 的含义相同, 均匀分布而且元素居中.

将小部件添加到表格中也很方便.
void gtk_table_attach_defaults ( GtkTable * table, GtkWidget * widget,
gint left_attach, gint right_attach, gint top_attach, gint bottom_attach );
table 是布局容器.
widget 是想要添加到容器中的小部件.
left_attach 是小部件左边接触的网格线.
right_attach 是小部件右边接触的网格线.
top_attach 是小部件上边接触的网格线.
bottom_attach 是小部件下边接触的网格线.
假如一个小部件占据了第 2 行第 5 列的格子, 那么
gtk_table_attach_defaults ( table, widget, 4, 5, 1, 2 );
下面用 Table 布局来实现 Box 布局中实现的登录界面.
/**
* Table 布局
*/

#include <gtk/gtk.h>

// 关闭程序
void CloseTheApp ( GtkWidget * widget, gpointer data ) {
gtk_main_quit ();
}

// 确认登录
void Login ( GtkWidget * widget, gpointer data ) {
g_print ( "you clicked \'OK\'.\n" );
}

// 创建一个窗口
GtkWidget * MakeWindow () {
GtkWidget * window;
window = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
gtk_container_set_border_width ( GTK_CONTAINER ( window ), 10 );
gtk_window_set_title ( GTK_WINDOW ( window ), "Table" );
gtk_signal_connect ( GTK_OBJECT ( window ),
"destroy",
GTK_SIGNAL_FUNC ( CloseTheApp ),
NULL );

return window;
}

// 创建一个表格里面的标签
GtkWidget * CreateLabelInTable ( GtkWidget * table, gchar * caption,
gint left, gint right, gint top, gint bottom ) {
GtkWidget * label; // 标签
label = gtk_label_new ( caption ); // 初始化
// 添加到表格中
gtk_table_attach_defaults ( GTK_TABLE ( table ), label,
left, right, top, bottom );

return label;
}

// 创建一个表格里面的文本域
GtkWidget * CreateEntryInTable ( GtkWidget * table, gboolean visibility,
gint left, gint right, gint top, gint bottom ) {
GtkWidget * entry; // 文本域
entry = gtk_entry_new (); // 初始化
gtk_entry_set_visibility ( GTK_ENTRY ( entry ), visibility ); // 内容是否可视
// 添加到表格中
gtk_table_attach_defaults ( GTK_TABLE ( table ), entry,
left, right, top, bottom );

return entry;
}

// 创建一个表格里面的按钮
GtkWidget * CreateButtonInTable ( GtkWidget * table, gchar * caption, gpointer clickedFunc,
gint left, gint right, gint top, gint bottom ) {
GtkWidget * button; // 文本域
button = gtk_button_new_with_label ( caption ); // 初始化
gtk_signal_connect ( GTK_OBJECT ( button ),
"clicked",
GTK_SIGNAL_FUNC ( clickedFunc ),
NULL );
// 添加到表格中
gtk_table_attach_defaults ( GTK_TABLE ( table ), button,
left, right, top, bottom );

return button;
}

gint main ( gint argc, gchar * argv[] ) {
GtkWidget * window;
GtkWidget * table;

gtk_init ( &argc, &argv );

window = MakeWindow ();

table = gtk_table_new ( 3, 3, FALSE ); // 初始化为 3 行 3 列的表格, 网格大小不均而且内容不居中
gtk_table_set_row_spacings ( GTK_TABLE ( table ), 5 );
gtk_table_set_col_spacings ( GTK_TABLE ( table ), 5 );
gtk_container_add ( GTK_CONTAINER ( window ), table );

CreateLabelInTable ( table, "Username: ", 0, 1, 0, 1 );
CreateLabelInTable ( table, "Password: ", 0, 1, 1, 2 );
CreateEntryInTable ( table, TRUE, 1, 3, 0, 1 );
CreateEntryInTable ( table, FALSE, 1, 3, 1, 2 );
CreateButtonInTable ( table, "OK", Login, 2, 3, 2, 3 );
CreateButtonInTable ( table, "Cancel", CloseTheApp, 1, 2, 2, 3 );

gtk_widget_show_all ( window );
gtk_main ();
return 0;
}

Box 布局



Box 是 GTK+ 的布局之一, 一般不直接使用 Box, 而是使用其派生类 HBox 和 VBox.HBox 以水平方式排列小部件, 而 VBox 则以垂直方式排列它们.

GtkWidget * gtk_hbox_new ( gboolean homegenous, gint spacing );
GtkWidget * gtk_vbox_new ( gboolean homegenous, gint spacing );
homegenous 为 TRUE 时, 每个小部件分得相等大小的空间, 并居中.
spacing 为小部件周围的空白大小.

要将小部件加到 Box 之内也有两种方式.
void gtk_box_pack_start ( GtkBox * box, GtkWidget * child, gboolean expand, gboolean fill, gint padding );
void gtk_box_pack_end ( GtkBox * box, GtkWidget * child, gboolean expand, gboolean fill, gint padding );
start 方式对于 HBox 来说是从左到右添加小部件, 对于 VBox 来说是自顶向下添加小部件; end 方式则反之.
box 是 Box 小部件.
child 是想要放入布局容器中的小部件.
expand 是..., 我还不太清楚.
padding 是 Box 内部的空白边距.

/**
* Box 布局
*/

#include <gtk/gtk.h>

// 关闭程序
void CloseTheApp ( GtkWidget * widget, gpointer data ) {
gtk_main_quit ();
}

// 确认登录
void Login ( GtkWidget * widget, gpointer data ) {
g_print ( "you clicked \'OK\'.\n" );
}

// 创建一个窗口
GtkWidget * MakeWindow () {
GtkWidget * window;
window = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
gtk_container_set_border_width ( GTK_CONTAINER ( window ), 10 );
gtk_window_set_title ( GTK_WINDOW ( window ), "Box" );
gtk_signal_connect ( GTK_OBJECT ( window ),
"destroy",
GTK_SIGNAL_FUNC ( CloseTheApp ),
NULL );

return window;
}

// 创建一个输入面板
// caption 指向标签上显示的字符串
// visibility 指示文本域的输入是否可见, 如不可见则显示为星号
GtkWidget * MakeEntryBox ( gchar * caption, gboolean visibility ) {
GtkWidget * box; // 输入面板
GtkWidget * label; // 标签
GtkWidget * entry; // 文本域

// 初始化面板为水平分布, 紧凑分配空间, 间隙为 5
box = gtk_hbox_new ( FALSE, 5 );

// 标签, 加到输入面板中
label = gtk_label_new ( caption );
gtk_box_pack_start ( GTK_BOX ( box ), label, TRUE, FALSE, 0 ); // 从左到右添加小部件

// 文本域, 加到输入面板中
entry = gtk_entry_new ();
gtk_entry_set_visibility ( GTK_ENTRY ( entry ), visibility );
gtk_box_pack_start ( GTK_BOX ( box ), entry, FALSE, FALSE, 0 ); // 从左到右添加小部件

return box;
}

// 创建一个按钮面板
GtkWidget * MakeButtonBox () {
GtkWidget * box; // 按钮面板
GtkWidget * okButton; // 确认按钮
GtkWidget * cancelButton; // 取消按钮

// 初始化面板为水平分布, 均匀分配空间, 间隙为 5
box = gtk_hbox_new ( TRUE, 5 );

// 确认按钮, 加到按钮面板中
okButton = gtk_button_new ();
gtk_button_set_label ( GTK_BUTTON ( okButton ), "OK" );
gtk_signal_connect ( GTK_OBJECT ( okButton ),
"clicked",
GTK_SIGNAL_FUNC ( Login ),
NULL );
gtk_box_pack_end ( GTK_BOX ( box ), okButton, FALSE, TRUE, 0 ); // 从右到左添加小部件

// 取消按钮, 加到按钮面板中
cancelButton = gtk_button_new ();
gtk_button_set_label ( GTK_BUTTON ( cancelButton ), "Cancel" );
gtk_signal_connect ( GTK_OBJECT ( cancelButton ),
"clicked",
GTK_SIGNAL_FUNC ( CloseTheApp ),
NULL );
gtk_box_pack_end ( GTK_BOX ( box ), cancelButton, FALSE, TRUE, 0 ); // 从右到左添加小部件

return box;
}

gint main ( gint argc, gchar * argv[] ) {
GtkWidget * window;
GtkWidget * mainbox;
GtkWidget * usernamebox;
GtkWidget * passwordbox;
GtkWidget * buttonbox;

gtk_init ( &argc, &argv );

window = MakeWindow ();
usernamebox = MakeEntryBox ( "Username: ", TRUE );
passwordbox = MakeEntryBox ( "Password: ", FALSE );
buttonbox = MakeButtonBox ();

mainbox = gtk_vbox_new ( FALSE, 10 ); // 初始化主面板为垂直分布, 紧凑分配空间, 间隙为 10
gtk_box_pack_start ( GTK_BOX ( mainbox ), usernamebox, FALSE, FALSE, 0 ); // 从上到下添加小部件
gtk_box_pack_start ( GTK_BOX ( mainbox ), passwordbox, FALSE, FALSE, 0 ); // 从上到下添加小部件
gtk_box_pack_start ( GTK_BOX ( mainbox ), buttonbox, FALSE, FALSE, 0 ); // 从上到下添加小部件
gtk_container_add ( GTK_CONTAINER ( window ), mainbox ); // 将主面板添加到窗口上

gtk_widget_show_all ( window );
gtk_main ();
return 0;
}

Button Event


/**
* 按钮与按钮事件
*/

#include<gtk/gtk.h>

// 关闭窗口
void CloseTheApp ( GtkWidget * window, gpointer data ) {
gtk_main_quit ();
}

// 触发按钮
void ButtonEvent ( GtkButton * button, gpointer data ) {
g_print ( "The button was " );
g_print ( ( gchar * ) data );
g_print ( "\n" );
}

gint main ( gint argc, gchar * argv[] ) {
GtkWidget * window;
GtkWidget * button;

gtk_init ( &argc, &argv );

// 创建一下窗口
window = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
gtk_window_set_title ( GTK_WINDOW ( window ), "Button Events" );
gtk_window_set_default_size ( GTK_WINDOW ( window ), 160, 100 );
gtk_signal_connect ( GTK_OBJECT ( window ),
"destroy",
GTK_SIGNAL_FUNC ( CloseTheApp ),
NULL );

// 创建一个按钮
button = gtk_button_new_with_label ( "Button" );

// 点击按钮
gtk_signal_connect ( GTK_OBJECT ( button ),
"clicked",
GTK_SIGNAL_FUNC ( ButtonEvent ),
"clicked" );
// 鼠标进入按钮区域
gtk_signal_connect ( GTK_OBJECT ( button ),
"enter",
GTK_SIGNAL_FUNC ( ButtonEvent ),
"entered" );
// 鼠标离开按钮区域
gtk_signal_connect ( GTK_OBJECT ( button ),
"leave",
GTK_SIGNAL_FUNC ( ButtonEvent ),
"left" );
// 鼠标在按钮上按下
gtk_signal_connect ( GTK_OBJECT ( button ),
"pressed",
GTK_SIGNAL_FUNC ( ButtonEvent ),
"pressed" );
// 鼠标在按钮上释放
gtk_signal_connect ( GTK_OBJECT ( button ),
"released",
GTK_SIGNAL_FUNC ( ButtonEvent ),
"released" );

gtk_container_add ( GTK_CONTAINER ( window ), button );

gtk_widget_show_all (window); // 显示 window 上所有部件
gtk_main ();
return 0;
}

信号处理函数总有一个 gpointer data 的参数, 它作为最后一个参数传递给 gtk_signal_connect 的指针, 被直接传递给信号处理程序中的这个数据参数.

Signal & Event

是不是觉得 HelloWorld 的程序有点问题? 按了关闭窗口的按钮但程序还不结束, 是因为它对关闭窗口的所产生的信号还没进行处理, 主循环还继续执行着.
先了解一下信号和事件吧.
什么是信号? 信号是用时和程序在交互的过程中由相应的信号处理程序产生的, 称 X 信号.
什么是事件? 事件是 GTK+ 解释来自 X 信号而出现的.
GTK+ 以这两种方式来响应用户动作, 但 GTK+ 程序员可以用相同的方式处理信号和事件.

简单的程序, 代码说明一切...

/**
* 信号和事件
*/

#include <gtk/gtk.h>

// 关闭应用程序的信号处理
void CloseTheApp ( GtkWidget * window, gpointer data ) {
// 停止主循环
gtk_main_quit ();
}

// 窗口的事件处理
// 当窗口产生任何类型的事件是调用此函数, 这里只对部分事件作出响应.
gboolean EventHandler ( GtkWidget * window, GdkEvent * event, gpointer data ) {
switch ( event -> type ) {
case GDK_CONFIGURE:
g_print ( "The window is being reconfigured.\n" );
break;
case GDK_EXPOSE:
g_print ( "The window contents were redrawn.\n" );
break;
case GDK_ENTER_NOTIFY:
g_print ( "The mouse entered the window.\n" );
break;
case GDK_LEAVE_NOTIFY:
g_print ( "The mouse left the window.\n" );
break;
case GDK_DELETE:
g_print ( "Uh oh~ the user killed the window.\n" );
break;
default:
break;
}
return FALSE;
}

int main ( int argc, char *argv[] ) {
GtkWidget * window;

gtk_init ( &argc, &amp;argv );

window = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
gtk_window_set_title( GTK_WINDOW ( window ), "Signal & Event" );

// 窗口连接到 "窗口关闭" 信号产生时的回调函数
gtk_signal_connect ( GTK_OBJECT ( window ),
"destroy", // destroy 是关闭窗口是产生的信号
GTK_SIGNAL_FUNC ( CloseTheApp ),
NULL ); // 最后一个参数是传递给信号处理函数的某个数据的指针

// 窗口连接到窗口事件产生时的回调函数
gtk_signal_connect ( GTK_OBJECT ( window ),
"event", // event 是指定部件产生的任何事件
GTK_SIGNAL_FUNC ( EventHandler ),
NULL );

gtk_widget_show( window );

gtk_main ();
return 0;
}

下面是 GDK 参考手册上的事件说明:

2007年3月5日星期一

HelloWorld



/**
* HelloWorld, 第一个 Gtk+ 程序
*/

#include <gtk/gtk.h>
/**
* 一般可以在 /usr/include/gtk-2.0 下找到上边的头文件
* 上边的头文件的作用是包含进行GTK编程所有可能用到的头
* 文件,包括glib.h等
*/

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

/**
* GtkObject->GtkWidget,
* Gtk 中任何一个图形化的东西都是一个 GtkWidget,
* GtkWidget 是一个部件, 相当于 Windows 所说的控件,
* 它可以是一个 Label, Text, Button, 或者 Window.
*/
// 一个窗口是一个 GtkWidget, 这里声明这个窗口是 GtkWidget.
GtkWidget *window;

/**
* 在调用 GTK+ 的函数前, 必须初始化 GTK+.
* 传递给 main 函数的参数使你可以获得应用程序运行时给出的任何命令行的参数.
*/
// 初始化 GTK+
gtk_init ( &argc, &amp;amp;amp;argv );

// 创建一个窗体, GTK_WINDOW_TOPLEVEL 参数指创建的是窗口
window = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
// 设置窗口标题
gtk_window_set_title(GTK_WINDOW(window), "Hello World!");
// 显示窗口
gtk_widget_show(window);

/**
* gtk_main () 成为主循环.
* 程序将会不停的运行, 直到 gtk_main_quit () 被调用或者程序崩溃为止.
* 这个函数什么事情都不做, 但它关注并等待某些事情(信号和事件)的发生.
*/
// Gtk+ 程序主循环
gtk_main ();
// 本程序中, 最后一行永远不被执行,
return 0;
}

在终端运行命令
$ gcc -Wall -o HelloWorld helloworld.c `pkg-config --cflags --libs gtk+-2.0`
$ ./HelloWorld
其中 -Wall 使 gcc 给出关于源代码的任何可能问题的警告; -o 告诉 gcc 如果没有任何错误并编译和连接顺利的话, 要输出编译应用程序的可执行程序, 本程序中 HelloWorld 是可执行文件的名字.

About

这个博客主要用于软件程序开发方面的学习与经验总结, 作为个人的学习记录.
如果你对这些没兴趣, 可以访问我的另外一个博客: mg12's Blog