网上有不少GTK的入门文章,例如:应用GTK+编程。本文同样介绍一个Hello World程序,但涉及的知识点略有不同。
本文例程是一个叫作hello3的小程序。GTK是一个跨平台的图形库,让我们看看hello3在各平台的运行效果。
Windows
Ubuntu
Poky
Poky是一个真正开放的嵌入式Linux平台。我们可以在Poky上安装hello3:
运行:
Broncho
Broncho是一个开放SDK的国产手机平台。hello3在Broncho上的运行效果如下:
同一个软件包可以在千差万别的平台上编译、运行,这就是GTK的世界。可以从这里下载hello3的源代码。
网上提到在Windows平台运行GTK,通常都会说cygwin、MinGW。其实,如果只用GTK的图形库,只要下载、安装gladewin32,用VC就可以开发、调试GTK程序。
gladewin32是sourceforge上的一个开源项目。读者可以从我的主页下载本文使用的版本。在安装gladewin32时可以选择是否与VC6集成。所谓与VC6集成就是修改两个注册表项,增加GTK的包含文件目录和库文件目录(假设gladewin32的安装目录是C:/GTK):
如果读者使用VC7或VC8,可以直接在工程中设置包含文件和库文件的附加目录。安装了gladewin32后,用VC打开hello3.dsw,就可以编译、运行、调试了。
不管使用什么库文件,除了设置包含文件目录、库文件目录,还需要设置链接哪些库文件,GTK也不例外。通常,我们只需要链接:
gtk-win32-2.0.lib glib-2.0.lib gobject-2.0.lib
如果用到了gdk函数,还要加上gdk-win32-2.0.lib。如果用到gettext,还要加上intl.lib。
在linux平台,我们通常用pkg-config工具自动产生包含文件路径和要链接的库文件。在windows环境也可以使用pkg-config,不过要先设置PKG_CONFIG_PATH,例如
set PKG_CONFIG_PATH=C:/GTK/lib/pkgconfig
然后再在C:/GTK/lib/pkgconfig目录执行pkg-config。不过我使用的gladewin32版本会有一个错误提示:
pkg-config --cflags --libs gtk+-2.0
Package pixman-1 was not found in the pkg-config search path.
Perhaps you should add the directory containing `pixman-1.pc'
to the PKG_CONFIG_PATH environment variable
Package 'pixman-1', required by 'cairo', not found
修改C:/GTK/lib/pkgconfig/cairo.pc,去掉对pixman-1的依赖后,就可以了:
C:/GTK/lib/pkgconfig>pkg-config --cflags --libs gtk+-2.0
-mms-bitfields -IC:/GTK/include/gtk-2.0 -IC:/GTK/lib/gtk-2.0/include -IC:/GTK/include/atk-1.0 -IC:/GTK/include/cairo -IC:/GTK/include/pango-1.0 -IC:/GTK/include/glib-2.0 -IC:/GTK/lib/glib-2.0/include -IC:/GTK/include/freetype2 -IC:/GTK/include -Wl,-luuid -LC:/GTK/lib -lgtk-win32-2.0 -lgdk-win32-2.0 -limm32 -lshell32 -lole32 -latk-1.0 -lgdk_pixbuf-2.0 -lpangocairo-1.0 -lcairo -lpangoft2-1.0 -lpangowin32-1.0 -lgdi32 -lfreetype -lz -lfontconfig -lpango-1.0 -lm -lgobject-2.0 -lgmodule-2.0 -lglib-2.0 -lintl
读者可以按照pkg-config的输出,在工程中把所有的库都加上:
atk-1.0.lib cairo.lib fontconfig.lib freetype.lib gdk-win32-2.0.lib gdk_pixbuf-2.0.lib glib-2.0.lib gmodule-2.0.lib gobject-2.0.lib gtk-win32-2.0.lib intl.lib pango-1.0.lib pangocairo-1.0.lib pangoft2-1.0.lib pangowin32-1.0.lib z.lib
不过,我习惯用什么加什么。不报错就不加。
在VC中使用GTK,只要建立一个win32控制台的空工程就可以了。如果在运行时,不希望有控制台窗口,只要在程序中加上:
#ifdef WIN32
#pragma comment( linker, "/subsystem:/"windows/" /entry:/"mainCRTStartup/"" )
#endif
在调试版本,我还是习惯保留控制台窗口,这样可以看到g_print的输出。虽然VC环境调试很方便,但在linux平台,打印输出还是常用的调试手段。
gettext是linux平台的标准国际化工具。使用gettext主要做两件事情:
其实真正的国际化和本地化不仅仅是翻译字符串,还有考虑其它区域相关的问题。不过gettext只管文字翻译。
gettext的原理很简单。使用gettext宏包围要翻译的字符串。gettext工具就可以从源文件提取出这些字符串,产生一个模板文件(pot文件)。翻译者根据这个pot文件做翻译,产生po文件。gettext工具根据po文件产生gettext库喜欢的二进制格式:mo文件。gettext化的程序在运行时会根据系统当前语言设置到指定目录去找指定名字的mo文件。如果找到了,包围每个字符串的gettext宏就会根据mo文件把源字符串替换成当前语言要求的字符串。如果找不到mo文件,就用原来的字符串。
程序里要做两件事情:
说得具体点,使用gettext后代码有3处改动:
在文件头包含gettext的头文件,定义gettext宏:_(字符串)。
#ifdef ENABLE_NLS
#include <libintl.h>
#define _(String) gettext(String)
#else
#define _(String) (String)
#endif
在主程序中初始化gettext库:
#ifdef ENABLE_NLS
bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE);
#endif
如果不是GTK程序,在调用bindtextdomain前还要加一句:
setlocale(LC_ALL, "");
GETTEXT_PACKAGE确定mo文件的名字。PACKAGE_LOCALE_DIR确定mo文件的位置。
用gettext宏包围每个需要翻译的字符串。例如:
button = gtk_button_new_with_label(_("Hello World!"));
在linux环境,ENABLE_NLS、GETTEXT_PACKAGE、PACKAGE_LOCALE_DIR都可以用配置工具(Autotool)产生。在VC环境,我们不想用Autotool,可以直接定义它们,例如:
#ifdef WIN32
#define ENABLE_NLS
#define GETTEXT_PACKAGE "hello3"
#define PACKAGE_LOCALE_DIR "./locale"
#endif
将GETTEXT_PACKAGE定义为"hello3",gettext函数就会去找“hello.mo”。将PACKAGE_LOCALE_DIR定义为"./locale",在简体中文环境,gettext函数就会在“./locale/zh_CN/LC_MESSAGES”目录查找mo文件。
在程序外,我们要维护字符串资源。在linux环境,我们通常不需要直接使用gettext工具,Autotool通过intltool脚本和Makefile把我们料理好所有杂事,我们只要维护要翻译的文件列表(POTFILES.in),翻译po文件就可以了。在windows平台,我写了两个批处理文件简化操作。
update-po.bat的内容如下:
@echo off
xgettext -k_ -o hello3.pot ../src/main.c
msgmerge -o zh_CN.po zh_CN.po hello3.pot
xgettext根据源文件产生模板文件(pot文件)。如果现在还没有po文件,可以将pot文件复制一份,后缀改成po,然后填写文件头,翻译字符串。在大多数情况下,我们已经有了po文件,然后源程序又变化了。我们用xgettext产生新的pot文件后,用msgmerge将新的pot文件合并到原来的po文件中。已经翻译的部分当然不用重新翻译了。
update-mo.bat的内容如下:
@echo off
if not exist ../output/locale/zh_CN/LC_MESSAGES mkdir ../output/locale/zh_CN/LC_MESSAGES
msgfmt -o ../output/locale/zh_CN/LC_MESSAGES/hello3.mo zh_CN.po
首先看看放mo文件的目录有没有准备好,没有就建一个。然后用msgfmt根据po文件生成mo文件,并放到指定目录。
GTK内部的字符串都是UTF-8编码。GTK项目的po文件通常都采用UTF-8编码。我们可以用任何支持UTF-8编码的编辑器编辑po文件。但我更喜欢用专门的工具poedit。
很好用。
综上所述,在改动了源文件后,本地化流程如下:
在windows平台使用GTK库就是这么简单,不用makefile、也不用Autotool。下面,我们先看看hello3的代码有什么值得说说的东西,然后再看看在linux环境编译、运行hello3是不是更加简单。