Feb 19

GTK+ 是一套跨平台的图形用户界面(Graphical User Interface)开发工具包,按 LGPL 许可协议发布的。此地介绍一下 GTK+ 开发工具包在 MS Windows 上的安装配置。

〇、预备工作

由于 GTK+ 是由 C 语言开发,因此要开发 GTK+ 应用程序,首先需要配置好 C 编译环境。在 MS Windows 平台,C 编译器的选择很多,主流的有 MSVC、GCC 的移植版本 MinGW 与 Cygwin。由于 GTK+ 是 GNU 项目,因此,这里推荐使用 GNU 的编译器,当然并不是说不能使用其他 C 编译器,这完全取决于个人喜好。MinGW 的下载与安装参考其官方网站 http://www.mingw.org 的说明。由于 MinGW 最初是 32 位,近年来,TDM-GCC 推出了 MinGW 的 64 位版本,详见其官方主页 http://tdm-gcc.tdragon.net/。至于 Cygwin,请到它的官方网站 http://www.cygwin.com/ 了解详情。在 Cygwin 与 MinGW 两者之间,一般推荐 MinGW 作为 C/C++ 编译工具链,除非需要在 MS Windows 平台上开发 Unix/Linux 应用程序。当然,在 MS Windows 平台上开发大型应用程序,少不得使用集成开发环境。集成开发环境也有很多选择,例如 MSVS、Code::Blocks、QtCreator 等。下面的介绍中使用的 Code::Blocks,请到其官网 http://codeblocks.org 下载最新版本(留意 Code::Blocks 的安装包是否已带有 MinGW)。

一、下载与配置 GTK+ 开发包

在 MS Windows 中安装 GTK+ 有多种选择,可利用 Cygwin 或 MinGW 工具自行由源码编译 GTK+ 工具包,不过要解决 GTK+ 依赖,工程相对浩大。目前,GTK+ 官方推荐利用 MSYS2(官网地址为 http://msys2.github.io)提供的包管理器 pacman 来自动完成 GTK+ 的编译与安装。这些 GTK+ 安装方法的共同特点是,安装过程都会顺带安装预备工作中提到的编译器。为了与预备工作中相衔接,这里直接利用预编译的 GTK+ 集成开发包来完成它们安装。

虽然预编译的集成开发包有些陈旧,集成开发包有些陈旧,但好处也是明显地,它包含了所有开发与运行 GTK+ 应用程序的头文件与库文件。需要 GTK+ 2 的用户,可到 http://ftp.gnome.org/pub/GNOME/binaries/ 下载 32 位的集成开发包 gtk+-bundle_2.24.10-20120208_win32.zip 或 64 位的集成开发包 gtk+-bundle_2.22.1-20101229_win64.zip;需要 GTK+ 3 的用户,可到 http://win32builder.gnome.org/ 下载 32 位的集成开发包 gtk+-bundle_3.10.4-20131202_win32.zip 或 64 位的集成开发包 gtk+-bundle_3.10.4-20131202_win64.zip。它们的安装非常简单,只需要将这些压缩包解压缩即可,一般建议将它们安装磁盘的根目录(如 C:\GTK+-2.12C:\GTK+-3.10),但不推荐安装到 MS Windows 的应用程序文件夹。为了能够使用 GTK+ 提供的 pkg-config 来配置 GCC 的编译、链接选项,还需配置环境变量 PATH。只需在 MS Windows 的文件管理器中鼠标右击 "My Computer" -> "Properties" -> "Advanced system settings" -> "Environment variables",接着双击 "System variables" 页面中的 PATH 行,将 GTK+ 目录中的 bin 路径(如 ;C:\GTK+-2.12\bin;C:\GTK+-3.10\bin)添加到原 PATH 变量的末尾。命令行操作也可完成 PATH 的设置,也即打开命令行窗口(CMD.exe),然后执行

> setx PATH "%PATH%;C:\GTK+-2.12\bin"

或者

> setx PATH "%PATH%;C:\GTK+-3.10\bin"

设置好环境变量 PATH 之后,可选择性的做下述设置

> pango-querymodules > C:\GTK+-2.12\etc\pango\pango.modules
> gdk-pixbuf-query-loaders > C:\GTK+-2.12\lib\gdk-pixbuf-2.0\2.10.0\loaders.cache
> gtk-query-immodules-2.0 > C:\GTK+-2.12\lib\gtk-2.0\2.12.11\immodules.cache

二、开发环境的测试与 Code::Blocks 的整合

在正式开始 GTK+ 开发工作之前,要确保 GTK+ 环境配置的正确性,为此还需做一些简单的测试。前述 GTK+ 集成开发包带有 GTK+ 的开发演示文档,可到相应目录查找。此地,以命令行方式演示一个由 GTK+ 2 开发简单应用的例子:

> notepad.exe hellogtk.c

#include <gtk/gtk.h>

int main(int argc, char *argv[])
{
    GtkWidget *window;
    gtk_init(&argc, &argv);
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    g_signal_connect(G_OBJECT(window), "destroy",
    G_CALLBACK(gtk_main_quit), NULL);

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

现在在 MinGW Shell 中执行

> gcc hellogtk.c -o hellogtk.exe `pkg-config --cflags --libs gtk+-2.0`

一切正常的话,会在 hellogtk.c 所在目录生成一个可执行文件 hellogtk.exe,鼠标双击运行它。

要开发一个大型的 GTK+ 程序,除了使用 Makefile 管理源代码文件之外,使用集成开发环境是方便的。下面以 Code::Blocks 作为集成开发环境演示如何进行 GTK+ 的开发工作,而这仅需搞清楚如何在 Code::Blocks 中配置 GTK+ 的库文件与头文件路径,当然有很多方法达到这个目的,这里只给出针对 GTK+ 工程相关的配置方法。为此,先按下述操作新建一个 GTK+ 工程:启动 Code::Blocks,点击 "File" -> "New" -> "GTK+ Project" 创建一个 GTK+ 2 项目。下面点击该工程对应的菜单 "Project" -> "build options" 子标签。下面从简到烦介绍三种在 GTK+ 工程属性中配置 GTK+ 开发环境的方法:

1、利用 pkg-config 直接设置编译选项与链接选项

由于 pkg-config 已在 PATH 中,故而 GTK+ 的编译选项与链接选项的设置非常简捷,只需在 "Project build options "-> "Compiler settings" -> "Other Compiler options" 中增加编译选项设置 pkg-config --cflags gtk+-2.0;再在 "Project build options" -> "Linker settings" -> "Other linker options" 增加链接选项为 pkg-config --libs gtk+-2.0

2、利用 pkg-config 的输出设置编译选项与链接选项

2.1、设置编译选项。先在命令行窗口(CMD.exe) 中执行

> pkg-config --cflags gtk+-2.0
-mms-bitfields -IC:/GTK+-2.12/include/gtk-2.0 -IC:/GTK+-2.12/lib/gtk-2.0/include -IC:/GTK+-2.12/include/atk-1.0 -IC:/GTK+-2.12/include/cairo -IC:/GTK+-2.12/include/pango-1.0 -IC:/GTK+-2.12/include/glib-2.0 -IC:/GTK+-2.12/lib/glib-2.0/include -IC:/GTK+-2.12/include/libpng12

然后开启 Code::Blocks,将 -mms-bitfields -IC:/GTK+-2.12/include/gtk-2.0 -IC:/GTK+-2.12/lib/gtk-2.0/include -IC:/GTK+-2.12/include/atk-1.0 -IC:/GTK+-2.12/include/cairo -IC:/GTK+-2.12/include/pango-1.0 -IC:/GTK+-2.12/include/glib-2.0 -IC:/GTK+-2.12/lib/glib-2.0/include -IC:/GTK+-2.12/include/libpng12 拷贝到 "Project build options" -> "Compiler settings" -> "Other Compiler options";

2.2、设置链接选项。在命令行窗口(CMD.exe) 中执行

> pkg-config --libs gtk+-2.0
-LC:/GTK+-2.12/lib -lgtk-win32-2.0 -lgdk-win32-2.0 -latk-1.0 -lgio-2.0 -lgdk_pixbuf-2.0 -lpangowin32-1.0 -lgdi32 -lpangocairo-1.0 -lpango-1.0 -lcairo -lgobject-2.0 -lgmodule-2.0 -lglib-2.0 -lintl

然后将 -LC:/GTK+-2.12/lib -lgtk-win32-2.0 -lgdk-win32-2.0 -latk-1.0 -lgio-2.0 -lgdk_pixbuf-2.0 -lpangowin32-1.0 -lgdi32 -lpangocairo-1.0 -lpango-1.0 -lcairo -lgobject-2.0 -lgmodule-2.0 -lglib-2.0 -lintl 拷贝到 "Project build options" -> "Linker settings" -> "Other linker options"。

3、将搜索路径、编译选项、链接选项分开设置

3.1、设置编译选项。在 "Project build options" -> "Compiler settings" -> "Other Compiler options" 加入编译选项 -mms-bitfields

3.2、设置链接选项与库文件。在 "Project build options" -> "Linker settings" -> "Link libraries" 中将 GTK+ 2 库的所有库文件(动态库或者静态库)加入,也即点击 ADD 按钮后弹出的文件选择对话框中将 atk-1.0cairogdi32gdk-win32-2.0gdk_pixbuf-2.0gio-2.0glib-2.0gmodule-2.0gobject-2.0gtk-win32-2.0intlpango-1.0pangowin32-1.0pangocairo-1.0 逐一加入;在 "Project build options" -> "Linker settings" -> "Other linker options" 中加入链接选项 -mwindows

3.3、设置搜索目录。在 "Project build options" -> "Search directories" -> "Compiler" 子标签中加入 GTK+ 2 的头文件路径:C:\GTK+-2.12\include\atk-1.0C:\GTK+-2.12\include\cairoC:\GTK+-2.12\include\glib-2.0C:\GTK+-2.12\include\gtk-2.0C:\GTK+-2.12\include\libpng12C:\GTK+-2.12\include\pango-1.0C:\GTK+-2.12\lib\glib-2.0\includeC:\GTK+-2.12\lib\gtk-2.0\include。一般来说就这些,如果安装了新的库再加。若上一步的 "Link libraries" 已带有绝对路径,就不必设库文件的搜索路径了;如若不然,还需在 "Project build options" -> "Search directories" -> "Linker" 子标签中加入库文件路径,也即 C:\GTK+-2.12\lib

介绍第三种比较麻烦的配置方法的目的是让那些使用 MSVC 作为编译工具的用户看清楚如何配置 GTK+ 开发环境。

三、GTK+ 程序界面的快速构建工具

Glade 用来为 GTK+ 和 GNOME 程序快速地设计图形用户界面,并自动生成图形界面的 C 源代码。历史上,Glade 有三个版本,不过目前主要的是 Glade2 与 Glade3,推荐使用 Glade3。Glade2 和 Glade3 的主要区别是: Glade2 会自动生成 makefile 等文件,而 Glade3 只是用来生成界面,然后采用 Libglade 和 GtkBuilder 格式调用 .glade 文件,这样的好处是代码和界面完全分开,避免代码的改变又需要重新编译。与 GTK+ 类似,在 MS Windows 中安装 Glade 的方法很多种,如前,此地直接利用预编译的 Glade 工具包。安装方法很简单,到 http://ftp.gnome.org/pub/GNOME/binaries/ 下载安装包 glade3_3.8.1-1_win32.zip 即可,由于 Glade 是来生成布局文件的,因此无须在意是否是 32 位还是 64 位。当然,为了避免在运行时出现依赖错误,也可安装带有完整 GTK+ 库的版本,例如 glade3-3.6.7-with-GTK+.exe。若使用 Libglade 格式的布局文件,还需调用 libglade 库,为此安装 libglade 库,也即到 http://ftp.gnome.org/pub/GNOME/binaries/下载 32 位安装包 libglade-dev_2.6.4-1_win32.zip 或 64 位安装包 libglade-dev_2.6.3-1_win64.zip 即可;并假设 Glade 的安装目录均在 C:\Glade,据此设置环境变量 PKG_CONFIG_PATH,也即在命令行窗口(CMD.exe) 中执行

> setx  PKG_CONFIG_PATH "%PKG_CONFIG_PATH%;C:\Glade\lib\pkgconfig"

接着试试下述命令行

> pkg-config --modversion libglade-2.0

看是否有版本信息输出?若找不到该命令,就需要重启电脑;若有,说明系统变量设置正确。

四、GTK+ 的 C++ 开发包的安装与配置

GTKmm 便是 GTK+ 的 C++ 开发包。与 GTK+ 类似,在 MS Windows 中安装 GTKmm 有多种选择,如前,这里直接利用预编译的 GTKmm 开发包包来完成安装,不过需要注意,它是以 GTK+ 包的安装为前提,要预先配置好 GTK+ 才行。从 http://ftp.gnome.org/pub/GNOME/binaries/ 下载 32 位的 GTKmm 开发包 gtkmm-win32-devel-2.22.0-1.exe 或者 64 位的 GTKmm 开发包 gtkmm-win64-devel-2.22.0-1.exe,然后点击它们安装即可,假设安装目录是 C:\GTKmm-2.22。在使用 GTKmm 之前,还需设置系统变量,打开命令行窗口(CMD.exe) ,执行:

> setx PKG_CONFIG_PATH "%PKG_CONFIG_PATH%;C:\GTKmm-2.22\lib\pkgconfig"

在打开命令行窗口(CMD.exe)中再试试

> pkg-config --modversion gtkmm-2.4

若一切正常,可以简单测试一下 GTKmm 是否正常工作?在 MinGW Shell 中,执行下述命令行

> notepad.exe hellogtkmm.cpp

/*Gtkmm程序的基本头文件,下面用到的两个类就由它定义*/
#include <gtkmm.h>

int main (int argc, char *argv[])
{
   /* 
   每一个Gtkmm应用程序都必须有这样一个类的实例,并且必需是第一个生成的Gtk对象。
   它对环境进行必要的初始化,需要用 argc 和 argv 两个参数对它进行实例化。
   */
   Gtk::Main app(argc, argv);
   
   /* 定义一个GTK的窗口类,默认地它以可执行程序名作为窗口标题。*/
   Gtk::Window main_win;
   
   /* 显示窗口,并进入监听事件的循环状态,当窗口关闭时返回。*/
   app.run(main_win);
   
   return 0;
}

> g++ hellogtkmm.cpp -o hellogtkmm `pkg-config --cflags --libs gtkmm-2.4`
> hellogtkmm.exe

附录

MS Visual C/C++(MSVC) 是由 Mirosoft 公司在 MS Windows 操作系统开发的 C/C++ 编译器,目前对应的集成开发环境是 MS Visual Studio(MSVS)。因此,开发 GTK+,使用 MSVS 也是一个不错的选择,这里介绍一下如何利用 MSVS(或 MSVC)进行开发工作。

先来说说在命令行环境中测试 GTK+ 开发环境的方法。通常,安装完 MSVS 之后,并不会将 MSVC 的命令行路径加入到系统环境变量 PATH 之中,但 MSVS 带有配置好环境变量 PATH 的 MSVS 命令行提示窗口。因此,要使用 MSVC 命令行,可在启动 MSVS 之后调用 MSVS 命令行提示窗口,也即启动 MSVS -> "Open the Visual Studio Command Prompt",在打开的命令行窗口中试试 pkg-config 的输出 :

> pkg-config --cflags gtk+-2.0

若一切正常的话,将这些选项保存下来,并删去其中 -mms-bitfields,并在末尾增加 -Dinline= /link /libpath:C:\GTK+-2.12\lib gtk-win32-3.0.lib gobject-2.0.lib。例如编译前述 hellogtk.c,在 MSVS 命令行提示窗口中执行

> cl hellogtk.c -IC:/GTK+-2.12/include/gtk-2.0 -IC:/GTK+-2.12/lib/gtk-2.0/include -IC:/GTK+-2.12/include/atk-1.0 -IC:/GTK+-2.12/include/cairo -IC:/GTK+-2.12/include/pango-1.0 -IC:/GTK+-2.12/include/glib-2.0 -IC:/GTK+-2.12/lib/glib-2.0/include -IC:/GTK+-2.12/include/libpng12 -Dinline= /link /libpath:C:/GTK+-2.12/lib gtk-win32-3.0.lib gobject-2.0.lib

其中 "gtk-win32-3.0.lib gobject-2.0.lib" 是编译 GTK+ 应用程序最少的链接选项要求;更多的链接选项请执行

> pkg-config --libs gtk+2.0

之后,自行添加需要的 .lib 项。

跟以往一样,开发大型程序,还是使用集成开发环境比较好。不过 MSVS 并没有提供 GTK+ 工程向导,因此只能通过在 MS Visual Studio 中新建一个空的 C++ 项目,然后在该项目的工程属性中配置 GTK+ 所需要的编译、链接参数。具体来说,

1、配置头文件搜索目录:鼠标右键新建的 C++ 工程属性->配置属性->C++目录->包含目录,把下述目录 C:\GTK+-2.12\include\atk-1.0C:\GTK+-2.12\include\cairoC:\GTK+-2.12\include\glib-2.0C:\GTK+-2.12\include\gtk-2.0C:\GTK+-2.12\include\libpng12C:\GTK+-2.12\include\pango-1.0C:\GTK+-2.12\lib\glib-2.0\includeC:\GTK+-2.12\lib\gtk-2.0\include 添加进去。

2、配置库文件搜索目录,鼠标右键新建的 C++ 工程属性->配置属性->C++目录->库目录,把 C:\GTK+-2.12\lib 目录添加进去;

3、配置附加依赖项, C++ 工程属性->配置属性->链接器->输入->附加依赖项,把 atk-1.0.libcairo.libgdi32.libgdk-win32-2.0.libgdk_pixbuf-2.0.libgio-2.0.libglib-2.0.libgmodule-2.0.libgobject-2.0.libgtk-win32-2.0.libintl.libpango-1.0.libpangowin32-1.0.libpangocairo-1.0.lib 等项添加进去,注意每项要换行。

要注意,不同的 GTK+ 版本,每个链接项的版本可能不同,请根据 pkg-config 的输出自行调整。

Feb 19

一、简介

GTK+ 是一套跨平台的图形用户界面(Graphical User Interface)开发工具包,按 LGPL 许可协议发布的。虽然最初是为 GIMP(GNU Image Manipulation Program) 写的,但早已发展为一个功能强大、设计灵活的通用图形库。特别是在 GTK+ 被 Linux 桌面项目 GNOME 选中使得它广为流传,成为 Linux 下开发图形用户界面应用程序的主流开发工具之一。当然 GTK+ 应用程序开发与运行并不要求必须在 Linux 上,事实上,GTK+ 早已经被成功地移植到了 Mac OS X 以及 MS Windows 上。在开发早期的 GIMP 版本时,Peter Mattis 和 Spencer Kimball 创建了 GTK(GIMP Toolkit) 工具包,作为 Motif 工具包的替代,后者在那个时候不是免费的。当 GTK 开发工具包获得了面向对象特性和可扩展性之后,才在名称后面追加上了符号 "+",也即 GTK+。

二、GCC 工具链

GCC(GNU Compiler Collections) 是 Linux 系统中默认的开发工具、Autotools 是 Linux 中开发大型项目管理工具。Red Hat 系中这些开发工具的安装可执行

$ sudo dnf install "Development Tools"

Debian 系可执行

$ sudo apt-get install build-essential

Gentoo 由于本身的理念就是编译所有的包,因此系统已经自带了 GCC 工具链,无须额外安装其他 GCC 开发工具。

三、C/C++ 的手册页

在编程的过程中有时会记不得某个函数的用法,此时查找手册页是比较快的。为了能够用命令行 man 查看 C/C++ 的函数,需要安装相关的手册页及其工具。在 Red Hat 系中执行

$ sudo dnf install man-pages libstdc++-docs

Debian 系可执行

$ sudo apt-get install manpages-dev manpages-posix manpages-posix-dev glibc-doc libstdc++6-4.3-doc

Gentoo 中安装稍微麻烦一点,可执行

$ sudo sh -c 'echo "sys-devel/gcc doc cxx " >> /etc/portage/package.use'
$ sudo emerge -av1 gcc
$ sudo emerge -av man-pages man-pages-posix

手册页的索引由 mandb 命令管理,有时在安装了新的手册页后,可能需要更新一下索引才能用 man kman f 查询到函数,也即

$ mandb c

然后就可以查看这些文档了。比如,查看 fopen 的手册页:

$ man fopen

四、GTK+ 库的概述

GTK+ 是基于以下库开发的:

  • glib:GTK+ 与 GNOME 的底层核心库,主要提供了 C 数据结构、可移植封装、运行时功能接口,譬如事件循环、多线程、动态装载和对象机制等。换而言之,Glib 是 GTK+ 能够“面向对象”的基础。GTK+ 中与界面无关的底层部分基本都被并入 glib。GLib 库还进一步分离成 GIO、GObject 等库。GIO 专门处理输入输出流。而 GObject 则维护着 GTK+ 所使用的一套对象系统。正是 GObject 提供的面向对象的机制,使得 GTK+ 可绑定很多种开发语言,例如 C++、Python、Perl、Java、C#、PHP 等其他高级语言。
  • pango:国际化文本陈列及渲染库,它是 GTK+ 的文本与字体处理核心;
  • atk:可访问接口库,它可以让 GTK+ 程序很方便地使用屏幕阅读器、放大镜以及一些输入设备等;
  • cairo:过去 cario 被称为 Xr 或 Xr/Xc,它是一个跨平台的开放源代码的矢量图形函数库,可以提供高质量的显示和打印输出。通过 Glitz 函数库, Cairo 能使用 OpenGL 或 X Render 扩展的硬件加速功能来绘制图像,这基于 Cairo 的应用能在现代化的 3D 显示硬件上获得益处。
  • gdk-pixbuf:GDK(GDK 是 GTK+ 从窗口系统细节中提取出来的一组接口,可以直接访问窗口细节。实际上,GDK 是 GTK+ 用户界面图形库提供的一些底层“图形实现”和“窗口实现”的方法,在 Linux 中,GDK 是对 Xlib 库提供接口的封装。)的一个部分,提供了一组位图函数,包括位图变换、位图文件读写等等,用于加载图像和维护图像“缓存”的(pixel buffer)。

要装 GTK+,先安装这写库,当然它们又依赖其它一些库,在 Linux 系统中可完全交由 Linux 的包管理器处理。还有一些库是 GTK+ 运行依赖的,下面是几个重要的:

  • gettext:它是国际化库,主要用于制作多语言程序。运行时 gettext 自动识别操作系统语言,然后从已有语言包中选择一个最合适用户的语言。当操作系统上没有 gettext() 函数的时候需要安装它。
  • libiconv:它是字符集转换库,通常操作系统上没有提供 iconv() 函数的时候才会安装它。GTK+ 内部使用 UTF-8 字符集,有时需要字符集转换。
  • freetype:它是一个操作字体的函数库,不但可以处理点阵字体,也可以处理多种矢量字体,包括 truetype 字体,为上层应用程序提供了一个统一的调用接口。
  • fontconfig:它提供系统范围内字体设置、定制和允许应用程序访问的函数库。实际上,GTK+ 的字体绘制是通过 pango、freetype 与 fontconfig 三者协作来完成的。其中,fontconfig 负责字体的管理和配置,freetype 负责单个字符的绘制,pango 则完成对文字的排版布局。
  • libjpeg:它提供了 JPEG 算法压缩文件图形文件供 GTK+ 程序读写 JPEG 格式的图形文件;
  • libtiff:它是用操作 TIFF 格式(标记图象文件格式)的图形文件并为 GTK+ 程序读写 TIFF 格式的图形文件的库;
  • libpng:它是用来创建与操作 PNG 格式的图形文件并为 GTK+ 程序提供读写 PNG 文件的库。PNG 格式的图形文件是被设计来替代 GIF 格式的,它对于更小范围的 TIFF 格式是来说,有了很多的进步和拓展并且减少了关于专利权的麻烦。

五、GTK+ 的 C 开发包

目前,GTK+ 开发包有三个版本:GTK+ 1、GTK+ 2 与 GTK+ 3,在 Linux 系统上是可以共存的,但由它们开发的程序需要对应的 GTK+ 开发包才能正常编译与运行。现在 GTK+ 2 是主流,但都在向 GTK+ 3 过度。根据自己的需要选择 GTK+ 版本,这里就以 GTK+ 2 为例。安装 GTK+ 开发环境,在 Red Hat 系中执行

$ sudo dnf install gtk2-devel gtk2-devel-docs

Debian 系可执行

$ sudo apt-get install libgtk2.0-dev libgtk2.0-doc

Gentoo 中执行

$ sudo sh -c 'echo "x11-libs/gdk-pixbuf doc \
                    dev-libs/glib doc \
                    x11-libs/pango doc \
                    x11-libs/cairo doc \
                    dev-libs/atk doc \
                    x11-libs/gtk+ doc" >> /etc/portage/package.use'
$ sudo emerge -av gtk+

若在开发过程中需整合 GNOME 环境,在 Red Hat 系中执行

$ sudo dnf install libgnome-devel gnome-devel-docs

Debian 系可执行

$ sudo apt-get install gnome-devel gnome-devel-docs

Gentoo 中执行

$ sudo emerge -av gnome-devel-docs

六、GTK+ 应用程序接口文档查看器

在 GNOME 桌面中,查看 GTK+ 应用程序接口文档的查看器是 DevHelp。安装它的方法很简单,在 Red Hat 系中执行

$ sudo dnf install devhelp

Debian 系可执行

$ sudo apt-get install devhelp

Gentoo 中执行

$ sudo emerge -av devhelp

通常,这些 API 文档会随着开发工具包的安装会被放入 /usr/share/gtk-doc/ 目录下。有了 DevHelp,查看它们非常容易,只需要在应用程序列表中点击 Devhelp 图标,或者在终端模拟器中执行

$ devhelp

八、pkg-config 的安装与使用

pkg-config 是编译器的辅助工具,可以帮助 GCC 找到所需要的头文件与库文件路径。它的安装非常简单。在 Red Hat 系中执行

$ sudo dnf install pkg-config

Debian 系可执行

$ sudo apt-get install pkg-config

Gentoo 中执行

$ sudo emerge -av dev-util/pkgconfig

现在试试 pkg-config,先查看 Linux 系统中是否安装 glib 库:

$ pkg-config --list-all | grep -i glib

若已安装,例如 glib-2.0,接着查看一下所安装的 glib 的具体版本:

$ pkg-config --modversion glib-2.0

再来列出 GCC 所需要的 glib-2.0 头文件所在目录

$ pkg-config --cflags glib-2.0

接着列出 GCC 所需要的 glib-2.0 库文件所在目录

$ pkg-config --libs glib-2.0

当然 pkg-config 无法智能到无所不知的地步,它是通过存放在标准目录 /usr/lib/pkgconfig 下的 .pc 文件来查找 GCC 所需头文件与库文件路径的。当然,也可以通过设置环境变量 PKG_CONFIG_PATH 让 pkg-config 找到非标准目录下的 .pc 文件,在这些文件里同样记录了 GCC 所需要的头文件和库文件所在的路径。例如

$ export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib

另外,值得提到一下 GTK+ 1 自带的工具 gtk-config,它与 pkg-config 的运作机制相像,但显然已被 pkg-config 取代。

九、GTK+ 的基本概念与约定

首先来解释 GTK+ 中几个基本的概念,以方便将来的分析。

  • 物件(GtkWidget):GTK+ 中每一个窗口里的组成要素都被视为一个物件,如按钮、文本等等,窗口本身也是一个物件。总之 GTK+ 的界面就是由物件构成的。注意,物件都使用指针来管理,物件外在表现就是一个特定类型的指针。
  • 容器(GtkContainer):物件里的一大类,容器的特点是其内部能够容纳其他物件。容器最基本的功能之一是将各种物件良好地组织起来。 GTK+ 的容器能在大小改变时自动调整内含物件的大小,这使得 GTK+ 能够很智能地相应窗口或其他物件的大小改变。这为我们提供了很大的方便,往往我们不需要指定某个物件的大小,只需说明他所在的容器位置, GTK+ 会把物件的实际位置和大小自动计算出来。
  • 继承、组合:虽然是 C 语言写的,但 GTK+ 灵活地运用了面向对象思想。 GTK+ 的物件体系中就有继承、组合这样的关系,如窗口(GtkWindow)是由容器(GtkContainer)派生出来的。
  • 类型转换宏:C 语言本身没有“继承”这个概念,那么,如果把派生的物件直接当做基物件使用,会出现一个编译警告,即“隐式指针类型转换”,但不会出错。为了消除这个警告,需要做指针类型转换。一般情况下类型转换使用类型转换宏。类型转换宏内部会检查物件的继承关系,确定能否进行转换,然后再做显式类型转换。
  • 事件(event):用户的操作,比如按下某个按钮或快捷键,被视为一个事件。
  • 信号(signal):GTK+ 是基于信号回调(signal-slot)机制的。信号捆绑了一个事件和一个函数,在用户触发这个事件时,这个函数会被调用一次。从这个角度来说, GTK+ 是基于物件的,即程序围绕物件属性、事件、方法进行。
  • 主循环(main loop):GTK+ 程序在一个主循环中运行。当一个事件被触发时,它将被插入队列中;在主循环中被触发的事件会被逐个处理(和这个事件绑定的函数被逐个调用);没有事件被触发时,程序就处于等待状态,等待下一个事件被用户触发。直到退出主循环的函数被调用,GTK+ 程序才结束。

GTK+ 拥有开源软件的很多特点,比如结构高度严谨、可读性甚好。现在介绍一下 GTK+ 的关键字命名方式也即 GTK+ 命名规范,以便阅读一段 GTK+ 程序。

  • 普通变量类型名:全小写写法。其中以 "g" 开头属于 GLib 库,如 "gint";
  • 物件类型名:驼峰写法(首字母大写),以 "Gtk" 开头,形如 "GtkWindow"。在 GTK+ 内部,类型是像下面这样定义的(以 GtkWindow 为例)。
    typedef _GtkWindow GtkWindow
  • 函数名:小写夹下划线写法,以 "gtk_"、"g_" 为前缀,形如 "gtk_main()"、"g_print()"。如果是针对某类物件的函数,则前缀中还有物件类型名,形如 "gtk_window_new()"。
  • 常量名:大写夹下划线写法,以 "GTK_" 为前缀,形如 "GTK_WINDOW_TOPLEVEL"。
  • 类型转换宏:大写夹下划线写法,以 "GTK_" 为前缀。一般来说,宏名字和类型名相仿,比如要把 GtkWindow* 类型的物件转换为 GtkContainer* 类型,就使用宏 "GTK_CONTAINER()"。

GTK+ 本身只负责界面组织,它提供的函数大致可分为三类,物件(Widget)、对象(Object)和其它工具函数。物件就不多说了,GTK+ 中的对象是一些功能更加复杂的不可见元素,它们和界面息息相关,比如 GtkBuilder。工具函数提供一些与界面关系密切的实用功能,比如剪贴板读写。

十、GTK+ 开发环境的简单测试

GTK+ 安装完成后,做个测试程序,代码如下

$ cat hellogtk.c

#include <gtk/gtk.h>

void hello(GtkWidget *widget,gpointer data)
{
    g_print("Hello GTK+!\n");
}

gint delete_event(GtkWidget *widget,GdkEvent *event,gpointer data)
{
    g_print ("delete event occurred\n");
    return(TRUE);
}

void destroy(GtkWidget *widget,gpointer data)
{
    gtk_main_quit();
}

int main( int argc, char *argv[] )
{
    GtkWidget *window;
    GtkWidget *button;
    gtk_init (&argc, &argv);
    window=gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_signal_connect (GTK_OBJECT(window),"delete_event",GTK_SIGNAL_FUNC(delete_event),NULL);
    gtk_signal_connect (GTK_OBJECT (window), "destroy",GTK_SIGNAL_FUNC (destroy), NULL);
    gtk_container_set_border_width (GTK_CONTAINER (window), 10);
    button = gtk_button_new_with_label ("Hello Ubuntu!");
    gtk_signal_connect (GTK_OBJECT (button), "clicked",GTK_SIGNAL_FUNC (hello), NULL);
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                                                 GTK_SIGNAL_FUNC(gtk_widget_destroy),GTK_OBJECT (window));
    gtk_container_add (GTK_CONTAINER (window), button);
    gtk_widget_show (button);
    gtk_widget_show (window);   /*显示一个窗口*/
    gtk_main();   /*进入主循环*/
    return(0);
}

用下面命令编译运行

$ gcc hellogtk.c -o hellogtk `pkgconfig --cflags --libs gtk+2.0`
$ ./hellogtk

会显示带有一个按钮的窗口,点击按钮以后窗口关闭,命令行显示 Hello GTK+!

十一、安装 GTK+ 界面快速设计工具

Glade 是 GTK+ 的界面辅助设计工具,可以通过拖放控件的方式快速设计出用户界面,这样的优势在于在设计的同时能直观地看到界面上的控件,并且可以随时调整界面上的设计。用 Glade 设计的图形用户界面是以 XML 格式的文件保存,它描述了控件的结构、每个控件的属性。用户可以动态加载这个界面文件。而且,界面和程序逻辑是完全分离,用户修改了界面,也不需要重新编译程序。Glade 有三个版本:基于 GTK+ 1 版本的 Glade、基于 GTK+ 2 及之后版本的 Glade2、Glade3。一般推荐使用 Glade3 设计 GTK+ 程序界面。

Glade 的布局文件有 2 种格式: Libglade、GtkBuilder。由于布局文件格式的不一样,最终使用的库函数不一样。选择 GtkBuilder 直接生成 XML 格式文件,但是后缀名仍是 .glade。如果选择 Libglade,可通过下述命令行

$ gtk-builder-convert

将它装换为 XML 格式文件。总的来说,推荐使用 GtkBuilder 格式的布局文件。

Glade 的安装很容易,不要要记住,若要使用 Libglade 格式的布局文件,需要安装 libglade 库。在 Red Hat 系中执行

$ sudo dnf install glade3 libglade2-devel

Debian 系可执行

$ sudo apt-get install glade libglade2-dev

Gentoo 中执行

$ sudo sh -c 'echo "dev-util/glade doc" >> /etc/portage/package.use'
$ sudo emerge -av dev-util/glade libglade

安装完成后,在应用程序列表中点击 Devhelp 图标,或者在终端模拟器中执行

$ glade

即可启动 Glade 工具。

1)Glade 画 UI,注意保存为 Libglade 格式,然后在下述 C 代码中使用该布局文件

$ cat libgladedemo.c

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

/**
 * 假设布局文件定义了一个名为 button1 的按钮,并且其 clicked 信号处理函数如下
 * 若是在 MS Windows 中,回调函数要加上修饰词 G_MODULE_EXPORT,也即
 */
/* G_MODULE_EXPORT */
void on_button1_clicked(GtkWidget* widget,gpointer data)
{
    g_print("hello, world!\n\r");
}

int main(int argc,char **argv)
{
    /* Libglade 类型,用于布局 */
    GladeXML *gxml;
    GtkWidget *window;
    gtk_init(&argc,&argv);

    /* 下面开始通过文件获取布局信息了 */
    gxml=glade_xml_new("glade.glade",NULL,NULL);
    /* 信号连接 */
    glade_xml_signal_autoconnect(gxml);

    /* 获取构件 */
    window=glade_xml_get_widget(gxml,"window1");  /* window1 是 glade3 中窗口的名字*/
    button = glade_xml_get_widget(gxml,"button1");  /* button1 是 glade3 中按钮的名字*/

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

在编译代码时,加上 libglade-2.0,如:

$ gcc libgladedemo.c -o libgladedemo `pkg-config --cflags --libs gtk+-2.0 libglade-2.0`
$ ./libgladedemo

2)Glade 画 UI,注意保存为 GtkBuilder 格式,同样在 C 代码中使用该布局文件

$ cat gtkbuilderdemo.c

#include <gtk/gtk.h>

/**
 * 假设布局文件定义了一个名为 button1 的按钮,并且其 clicked 信号处理函数如下
 * 若是在 MS Windows 中,回调函数要加上修饰词 G_MODULE_EXPORT,也即
 */
/* G_MODULE_EXPORT */
void on_button1_clicked(GtkWidget* widget,gpointer data)
{
    g_print("Hello World !\r\n");
}

int main (int argc, char **argv)
{
    /* GtkBuilder 类型,用于布局 */
    GtkBuilder *gtkBuilder;
    GtkWidget *mainwin;
    /* Initialize the widget set */
    gtk_init (&argc, &argv);
    /* Create the main window */
    /* 通过 main.glade 建立布局 */
    gtkBuilder= gtk_builder_new();
    gtk_builder_add_from_file(gtkBuilder,"main.glade",NULL);

    /* 连接信号,信号名在布局文件中定义 */
    gtk_builder_connect_signals (gtkBuilder, NULL);

    /* 通过布局文件获得构件,此处为一个对话框型的窗体 */
    mainwin= GTK_WIDGET(gtk_builder_get_object(gtkBuilder,"dialog1"));
    button = GTK_WIDGET(gtk_builder_get_object (builder, "button1"));
    g_object_unref ( G_OBJECT(gtkBuilder) );
    /* Show the application window */
    gtk_widget_show_all ( mainwin );
    /* Enter the main event loop, and wait for user interaction */
    gtk_main ();
    /* The user lost interest */

    return 0;
}

在编译代码时,无须增加额外库,也即:

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

十二、GTK+ 的其他语言开发包

将 GTK+ 与 C 语言的近亲 C++ 绑定,便是 GTKmm。Glade 还是 GTKmm 的界面快速构建工具,推荐使用 Glade3。若使用 libglade 格式的界面布局文件,需额外安装相应的库。在 Red Hat 系中执行

$ sudo dnf install gtkmm24-devel gtkmm24-docs libglademm24-devel

Debian 系可执行

$ sudo apt-get install libgtkmm-2.4-dev libgtkmm-2.4-doc libglademm-2.4-dev libglademm-2.4-doc

Gentoo 中执行

$ sudo sh -c 'echo "dev-cpp/libsigc++ \
                    dev-cpp/glibmm doc \
                    dev-cpp/pangomm doc \
                    dev-cpp/cairomm doc \
                    dev-cpp/atkmm doc \
                    dev-cpp/gtkmm doc \
                    dev-cpp/libglademm doc" >> /etc/portage/package.use'
$ sudo emerge -av gtkmm libglademm

测试一下 GTKmm 开发环境,下面是一个简单 Hello GTKmm 的例子

$ cat hellogtkmm.cpp

#include <gtkmm.h>
#include <libglademm/xml.h>         /* 访问Glade文件所需的头文件 */
#include <iostream>                  /* 输出错误信息到控制台 */
#include <assert.h>                  /* assert()断言 */
 
using namespace Gnome::Glade;
 
class HelloApp: public Gtk::Main {    /* 继承“Gtk::Main”类 */
public:
    HelloApp(int argc, char *argv[]):
        Gtk::Main(argc,argv),             /* 必须在初始化列表中调用父类的构造函数 */
        main_window(0)                     /* 为主窗口的指针赋初值 */
    {
        try
        {
            /* 从同目录下的GLADE文件创建“Gnome::Glade::Xml”对象 */
            ref_xml = Xml::create("helloapp.glade");
        }
        catch(const XmlError& ex)
        {
            /*   出错时错误信息输出到控制台,并返回 */
            std::cerr << ex.what() << std::endl;
            return;
        }
   
        /*
          取得主窗口的指针,存入main_window变量中,并确保成功。
          main_window为0值表示失败,多是因为指定的控件名称不正确或不存在。
          第一个参数就是Glade文件中定义的控件名称,是字符串型。
        */
        ref_xml->get_widget("main_window", main_window);
        assert(main_window);
    }
 
    ~HelloApp()
    {
        /* 由于采用了智能指针,我们不需要管理资源的释放 */
    }
 
    show_window()
    {
        /*
          如果取得主窗口的指针成功,就调用父类的run()函数显示它,
          并进入监听事件的循环状态,当主窗口关闭时返回。
        */
        if (main_window) {
            run( *main_window );
        }
    }
   
protected:
    /* 通过它访问Glade文件的内容,是一种智能指针,能自动释放占用的资源 */
    Glib::RefPtr<Gnome::Glade::Xml> ref_xml;
   
    Gtk::Window* main_window;         /* 存储主窗口的指针 */
};
 
int main (int argc, char *argv[])
{
    /*
      可以看到,这跟“最简单的Gtkmm程序”非常相似。HelloApp继承自
      “Gtk::Main”类,也需要通过 argc 和 argv 两个参数进行实例化。
    */
    HelloApp app(argc, argv);
   
    /*
      在这个函数中调用Gtk::Main::run函数来实现与上面相同的功能。   
      这里不需要窗口对象作参数,因为它已封装在HelloApp类中了。
    */
    app.show_window();
   
    return 0;
}

$ g++ hellogtkmm.cpp -o hellogtkmm `pkg-config --cxxflags --libs gtkmm-2.0`

$ ./hellogtkmm

将 GTK+ 与 Python 语言绑定,便是 PyGTK。Glade 仍是 PyGTK 的界面快速构建工具,若使用 libglade 格式的界面布局文件,需额外安装相应的库。在 Red Hat 系中执行

$ sudo dnf install pygtk2-devel pygtk2-docs pygtk2-libglade

Debian 系可执行

$ sudo apt-get install python-gtk2-dev python-gtk2-doc python-glade2

Gentoo 中执行

$ sudo sh -c 'echo "dev-python/pygtk doc \
                    dev-python/pygobject doc \
                    dev-python/pycairo doc" >> /etc/portage/package.use'
$ sudo emerge -av pygtk pygtk2-libglade

还需测试一下 PyGTK 开发环境,下面是一个简单 Hello PyGTK 的例子

$ cat hellopygtk.py

from gtk import * 
 
window = GtkWindow(WINDOW_TOPLEVEL) # 创建一个顶层窗口 
window.set_title("Hello, world!") 
window.connect("destroy", mainquit) # 将注销事件与mainquit处理连接 
 
window.show() # 显示主窗口 
mainloop() # 进入事件循环 

$ python hellopygtk.py

附录

作为跨平台的图形用户界面开发工具,GTK+ 程序在非 Unix/Linux 平台上并非原生样式。若有这种需要,wxWidgets 便是一个更好的选择,它是由 C++ 开发的跨平台的图形用户界面开发包。另外,基于 wxWidgets 工具包,还开发了 C/C++ 集成开发环境 Code::Blocks,它是跨平台的集成开发环境。该集成开发环境还为 wxWidgets 图形界面开发提供了快速开发插件 wxSmith。在 Linux 平台上,为了让图形用户界面显示 GTK+ 样式,wxWidgets 封装了 GTK+ 包,因此 wxWidgets 在 Linux 平台上也被命名为 wxGTK。好了,要在 Linux 平台上开发 wxWidgets 程序,安装所有的工具包了。在 Red Hat 系中执行

$ sudo dnf install wxGTK-devel wxGTK-docs codeblocks codeblocks-contrib

Debian 系可执行

$ sudo apt-get install libwxgtk3.0-dev wx3.0-doc wx3.0-headers wx3.0-i18n wx3.0-examples codeblocks codeblocks-contrib

Gentoo 中执行

$ sudo sh -c 'echo "dev-util/codeblocks doc contrib " >> /etc/portage/package.use'
$ sudo emerge -av wxGTK codeblocks

在 Gentoo 上使用 wxWidgets,还需要用 eselect 的选择合适的版本

$ sudo eselect list wxWidgets
$ sudo eselect wxWidgets set 1

在 Linux 使用 wxWidgets 的方法非常简单,它提供了 wx-config 工具帮助 GCC 查找头文件与库文件。例如

$ wx-config --cppflags
$ wx-config --libs

再来看一个编译 wxWidgets 程序的实例

$ g++ hellowx.cpp -o hellowx `wx-config --cppflags --libs`
$ ./hellowx

与 GTK+ 类似,wxWidgets 也有其他语言的绑定,这里介绍一下 wxWidgets 与 Python 的绑定,也即 wxPython 开发包,对应的界面快速设计工具是 wxGlade。它们的安装非常简单,Red Hat 系中执行

$ sudo dnf install wxpython wxpython-devel wxpython-docs wxGlade

Debian 系可执行

$ sudo apt-get install python-wxgtk3.0 python-wxtools python-wxversion python-wxglade

Gentoo 中执行

$ sudo emerge -av wxPython wxglade

测试一下 wxPython 开发环境,下面是一个简单 Hello wxPython 的例子

$ cat hellowxpy.py

import sys, os
from wxPython.wx import *
 
class main_window(wxFrame):
      def __init__(self, parent, id, title):
      	  wxFrame.__init__(self, parent, -1, title, size = (200, 100),style=wxDEFAULT_FRAME_STYLE|wxNO_FULL_REPAINT_ON_RESIZE)
      	  self.control = wxTextCtrl(self, -1, style=wxTE_MULTILINE)
      	  self.Show(true)
 
class App(wxApp):
      def OnInit(self):
      	  frame = main_window(None, -1, "wxPython: (A Demonstration)")
      	  self.SetTopWindow(frame)
      	  return true
 
app = App()
app.MainLoop()

$ python hellowxpy.py
Aug 23

Unix/GNU Linux 上的 X11 图形程序会从系统的 /etc/profile 以及用户目录的 $HOME/.bashrc、$HOME/.bash_profile、$HOME/.xprofile 等处读取环境变量。例如 GNU Emacs、Code::Blocks、CodeLite、Eclipse 等均可从系统的环境变量中获取需要的环境变量。尽管 Mac OS X 是类 Unix 系统,但毕竟它的图形界面不是由 X11 提供的,因此,它上面的图形程序获取环境变量的方式也是不同的。由于 Mac OS X 中图形程序有多种运行方式,例如从终端调用命令行运行、在 Finder 、Launchpad 以及 Dock 上点击图标运行或者用 SpotLight 调用,这几种运行方式下图形程序的环境变量读取以及生效方式也不尽一致。

首先,谈谈终端环境以及由终端调用的程序(在 Bash Shell 中)如何读取环境变量。根据 /etc/profile 中的设置可以看到,Mac OS X 系统会借助 /usr/libexec/path_helper 程序设置路径变量 PATH。path_helper 会分别读取 /etc/paths、/etc/paths.d/ 与 /etc/manpaths、/etc/manpaths.d/ 中的设置来获取 PATH 与 MANPATH 的环境变量。之后 /etc/profile 还会综合 /etc/bashrc 给出 Shell 中所有系统级环境变量的设置。因此,系统级的环境变量 PATH 的设置可通过修改 /etc/profile、/etc/bashrc 与 /etc/paths.d/ 等相关文件来实现,除此而外的系统级环境变量则可在 /etc/profile、/etc/bashrc 中设置。有一点值得指出,那就是 MANPATH 变量,通常它是不需要设置的,这是因为手册页的搜索除去用 MANPATH 变量配置之外,还可以在 PATH 的相对路径中搜索。至于 Shell 中用户级环境变量的设置,与 Linux 中一样,首先继承来自 /etc/profile 中的系统级环境变量,再根据 Shell 是不是登录 Shell 决定是否读取用户家目录中的 $HOME/.bashrc 或 $HOME/.bash_profile 中的环境变量。一般为了避免配置重复出现,无论登录还是非登录 Shell,均做如下配置

$ cat > ~/.bash_profile << EOF
if [ -f ~/.bashrc ]; then 
    source ~/.bashrc
fi
EOF

相关环境变量的设置都在 $HOME/.bashrc 中进行。这就给出了 Shell 中所有用户级环境变量的配置方法。对于从终端调用的(无论是否是图形界面的)用户级程序而言,环境变量直接继承自 Shell 中所有用户级环境变量。例如从终端打开 Code::Blocks 程序

$ open -a CodeBlocks.app

随后在 Settings -> Compiler...,Global Compiler settings -> Compiler settings -> Other options 中填入 `echo $PATH`,接着随便写一段简单的 C/C++ 程序并点击 Build,在 Code::Blocks 日志框中便会看到环境变量 PATH 的输出。很容易验证 Code::Blocks 确实是从 Shell 中读取了环境变量。先退出 Code::Blocks,记得退出时选择保存设置,因为还要用它进行同样的测试。请记得在测试完了之后,删掉 Code::Blocks 中全局的编译选项设置。为了做一个比较,可以尝试从 Launchpad 中点击 Code::Blocks 图标启动,做同样的尝试,会发现此时 echo $PATH 没有在日志窗口中输出,这表明从 Launchpad 中启动程序不会继承终端程序的环境变量。

那么除了 Terminal.app 之外,对于那些从 Finder、Launchpad 以及 Dock 处启动或者用 SpotLight 调用的程序,是如何读取环境变量的呢?在 Mountain Lion 之前,从那些位置启动的图形界面程序会从 $HOME/.MacOSX/enviroment.plist 文件继承环境变量;但是自 Mountain Lion 之后,~/.MacOSX/enviroment.plist 失效,只有通过 launchtl setenv 命令设置的环境变量才有可能被它们继承,而且设置完必须重启 Finder、Dock 以及 Spotlight 才能生效,具体取决于程序从哪儿被调用?

先来谈谈 ~/.MacOSX/enviroment.plist 的配置,尽管 Mountain Lion 版本已经过时,但难保不会有一些老的机器会停留在那些版本上。通常,~/.MacOSX/environment.plist 文件并不存在,需要先创建再编辑。例如

$ mkdir ~/.MacOSX && touch  ~/.MacOSX/enviroment.plist

根据 ~/.bashrc 中设置的环境变量情况,利用 defaults 命令操作该文件,例如

$ defaults write $HOME/.MacOSX/environment.plist PATH "$PATH”
$ defaults write $HOME/.MacOSX/environment.plist JAVA_HOME "$JAVA_HOME”
$ defaults write $HOME/.MacOSX/environment.plist ANDROID_HOME "$ANDROID_HOME"

注意上述 defaults 命令是将当前终端中的 PATH、JAVA_HOME 与 ANDROID_HOME 写入了 ~/.MacOSX/environment.plist。至于编辑 ~/.MacOSX/environment.plist,最好用 Xcode.app,例如

$ open -a Xcode.app ~/.MacOSX/enviroment.plist

设置调整完成,可以再试试从 Launchpad 中点击 Code::Blocks 图标启动,做前面一样的测试,会发现此时 echo $PATH 在日志窗口中输出正常。

现在来谈谈 Mountain Lion 版本以后,用 launchctl setenv 设置图形界面程序的环境变量的方法。从当前终端运行

$ launchctl setenv PATH “$PATH"

别急着作测试,因为从 Mac OS Yosemite 开始,光这样设置,从 Finder、SpotLight、Launchpad 以及 Dock 点击图标运行的程序还是不会继承这些变量。对于从 Launchpad 或 Dock 中启动的程序,需重启 Dock 才能让设置生效:

$ killall Dock

对于从 Finder 中启动的程序,需重启 Finder 才能让设置生效:

$ killall Finder

对于从 Spotlight 中启动的程序,需重启才能让设置生效:

$ killall Spotlight
$ killall SystemUIServer

好了,有了这些操作之后,再试试从 Launchpad 中点击 Code::Blocks 图标启动,做前面一样的测试,会发现此时 echo $PATH 在日志窗口中输出正常。

但是可以看到,这样的操作无比麻烦。为了省去这些麻烦,可以建立一个用户级的服务来替代这些操作过程。先创建

$ mkdir -p ~/.local/bin
$ cat > ~/.local/bin/environment.sh <<EOF
#!/bin/sh

set -e

syslog -s -l warn "Set environment variables with ~/.local/bin/environment.sh \$(whoami) - start"

launchctl setenv PATH "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/local/android-sdk-manager/platform-tools:/usr/local/android-sdk-manager/tools:/usr/local/apache-ant/bin:/usr/local/mysql/bin:/usr/local/MacGPG2/bin:/usr/texbin:"
launchctl setenve JAVA_HOME "/Library/Java/JavaVirtualMachines/jdk1.8.0_45.jdk/Contents/Home"
launchctl setenv ANDROID_HOME "/usr/local/android-sdk-manager/"

osascript -e 'tell app "Dock" to quit'
osascript -e 'tell app “Finder" to quit'
osascript -e 'tell app “Spotlight" to quit'
osascript -e 'tell app "SystemUIServer" to quit'

syslog -s -l warn "Set environment variables with ~/.local/bin/environment.sh \$(whoami) - complete"
EOF
$ chmod +x ~/.local/bin/environment.sh

接着再创建服务配置文件

$ nano -w ~/Library/LaunchAgents/org.easior.environment.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>org.easior.environment.plist</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Users/easior/.local/bin/environment.sh</string>
    </array>
    <key>KeepAlive</key>
    <false/>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>

并启动它

$ launchctl load -w ~/Library/LaunchAgents/org.easior.environment.plist

现在来看看以上设置对于环境变量 PATH 带来了怪异现象。首先,第三方终端程序 iTerm.app 作为图形程序,将继承自 launchctl setenv 设定的环境变量 PATH;其次,Mac OS 自带的终端程序 Terminal.app 不会继承来自 launchctl setenv 设置的环境变量,它仍按 Shell 的标准方法设置 PATH 变量;再次,由 XQuartz 提供的 X11.app 中终端程序 xterm 将既会继承由 launchctl setenv 设定环境变量 PATH,还会再次执行登录 Shell,也即会再次继承系统级的 PATH 变量。如此一来,若既在 ~/.bashrc 或 ~/.bash_profile 中设置 PATH,也在 ~/.local/bin/environment.sh 中设置 PATH,且这两个 PATH 设置不一致的话,例如

$ cat >> ~/.bashrc << EOF
~/.local/bin
EOF
$ launchctl setenv PATH "$HOME/bin"
$ killall Dock

那么从 Launchpad 启动的这三个终端的 PATH 变量将会各不相同。为了避免这种情况的发生,建议在 ~/.bashrc 或者 ~/.bash_profile 中设置

if [ ! `launchctl getenv PATH` == "" ]; then
   export PATH=.:`launchctl getenv PATH`
fi

这样一来,每个终端启动的 PATH 均是一致的。还有一点需要指出:launchctl setenv PATH 设置为空与不设置它,这是两码事。例如

$ launchctl getenv PATH ""
$ killall Dock

现在试试从 Launchpad 或 Dock 调用 iTerm.app,会有意想不到的事情发生。

除了 launchctl setenv 的方法之外,修改 Mac OS X 图形程序的 Info.plist 文件中的 LSEnvironment 变量也可以设置需要读取环境变量。例如,在 Finder 的 /Applications 目录鼠标右击 Codeblocks.app 图标 -> Show package contents,接着用 Xcode.app 打开 Contents 目录的 info.plist 文件,在其中增加一个新的 key/dict 对:

<key>LSEnvironment</key>
<dict>
     <key>PATH</key>
<string>/Users/James/.rvm/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:</string>
</dict>

保存完改动并在命令行中执行

$ /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -kill -r -domain local -domain system -domain user 
$ killall Finder

再试试从 Launchpad 中点击 Code::Blocks 图标启动,还是做与前面一样的测试,看看 echo $PATH 在日志窗口中输出。为了 Code::Blocks 能正常工作,测试完成后请去掉 Info.list 中的 LSEnvironment 设置。

另外,还有些图形程序,本身提供了设定环境变量的方法。例如对于 GNU Emacs 而言,要继承环境变量可直接在 Emacs 中配置。例如

$ cat >> ~/.emacs <<EOF
(setenv “PATH” (concat (getenv “PATH”) “:” “/opt/local/bin” “:” “/opt/local/sbin”))
EOF

能让 GNU Emacs 的子进程直接读取 PATH 变量。

Aug 21

MinGW、Code::Blocks 和 wxWidgets 是三个著名的开源项目,分别是编译器(GNU Compiler Collections)、集成开发环境(IDE)和图形界面开发库(GUI Library)。由这三个工具搭建起来的全开源 C++ 开发环境,功能不逊色于 VC,由于使用的开源软件,这样的开发环境是免费的,并且是跨平台的。下面说一下在 MS Windows 下的搭建过程。

一、编译器

MinGW 是指只用自由软件来生成纯粹的 Win32 可执行文件的编译环境,它是 Minimalist GNU on Windows 的略称。实际上,MinGW 并不是一个 C/C++ 编译器,而是一套 GNU 工具集合在 MS Windows 系统上的一个移植。MinGW 官方网站为 http://www.mingw.org

1、MinGW 的安装

安装 MinGW 有两种安装方式:自动安装与手动安装。下面分别来介绍它们。

1.1、自动安装。

下载自动安装程序(http://sourceforge.net/projects/mingw/files/Installer/)mingw-get-setup.exe,然后一步步的来就行了。大致如下:设置安装目录,默认安装到 C:\MinGW;选择安装组件,这个根据大家需要选择安装组件( C 编译器、C++ 编译器、Fortran 编译器、ObjC 编译器、Ada 编译器等),一般选择 C/C++ 编译器即可,看各位用途确定,组件列表中还有 MSYS 基本系统等可供选择;等待下载并安装完成。

实际上, MinGW 提供了一个非常方便的包管理工具 mingw-get。在 MinGW 安装完成并适当的配置环境变量之后,可以在命令行窗口(cmd.exe)中或者 MSYS 基本系统中通过 mingw-get 命令来进行包管理。例如在命令行窗口(cmd.exe)中可以使用

> mingw-get --help

查看详细的使用方式,主要命令与各种 Linux 包管理器如 apt-get、dnf 等的操作类似,常用的几个包括

> mingw-get update
> mingw-get install packagename
> mingw-get remove packagename
> mingw-get upgrade packagename
> mingw-get list

1.2、手动安装。

简言之就是本来自动安装的内容要自己下载并且复制到自己设定的目录中。根据 MinGW 官方网站 http://www.mingw.org/wiki/InstallationHOWTOforMinGW 介绍,到 http://sourceforge.net/projects/mingw/files/MinGW/Base/ 下载保证 MinGW 正常工作所需要的下述文件

  • binutils (bin)
  • mingw-runtime (dev and dll)
  • w32api
  • Required runtime libraries for GCC:
    • mpc (dll)
    • mpfr (dll)
    • gmp (dll)
    • pthreads (dev and dll)
    • iconv (dll)
    • zlib
    • gettext
  • gcc-core (bin and dev and dll)

作为 C++ 开发工具,还需再到 http://sourceforge.net/projects/mingw/files/MinGW/Extension/ 下载下述文件

  • gcc-c++ (bin and dev and dll) for c++
  • mingw-gdb and libexpat for debugger
  • mingw32-make for make
  • mingw-utils for MinGW Utilities

然后将这些文件解压到同一个目录下,例如 C:\MinGW。实际上,利用 mingw-get 也可实现半自动化安装,也即将所有包放入它的缓存目录,也即 C:\MinGW\var\cache\mingw-get\packages,然后正常安装 mingw-get-setup.exe 便可利用已下载的包完成安装。半自动安装方法适用于那些暂时或者长时间网络条件比较差的电脑主机。

2、设置 MinGW 的环境变量

MinGW 环境的设置也有好几种方法,但通常的做法是要么类似 Linux 终端起一个 MinGW Shell,要么类似 Visual Studio 用 batch 脚本起一个配置好环境变量的命令行窗口(cmd.exe)。

2.1、若前面安装了 MSYS 基本系统,则在 MS Windows 的开始菜单中会有一个 MinGW Shell 子菜单,也即开始菜单 ->程序 -> MinGW -> MinGW Shell,运行它可在 MS Windows 中打开 MinGW Shell。需要注意,第一次运行 MinGW Shell 时,记得在打开的 MinGW Shell 中执行下述命令

$ /postinstall/pi.sh

它可以将 MinGW 与 MSYS 绑定在一起。以后每次使用 MinGW 时,只需点击该子菜单便可自动在命令行窗口中配置好 MinGW 的环境。如果 MinGW 没有安装在标准位置,那还可适当调整 MinGW Shell 的配置

> notepad.exe C:\MinGW\msys\1.0\etc\fstab
C:\MinGW /mingw

根据此配置,来设定 MinGW Shell 的根目录位置;MinGW Shell 的用户家目录由环境变量 %HOME%、%USERPROFILES% 等决定;还有,MS Windows 中盘符如 C:\、D:\ 等对应的是 /c/、/d/。实际上,安装 MSYS 基本系统的好处也在这里,它将 Linux 系统中那套 Bash Shell 工具搬到了 MS Windows 中来。特别是,在 MinGW shell 中能执行那些只能在 Bash Shell 中执行的 Linux 命令行。若 MinGW 安装过程中没有事先选择安装 MSYS 基本系统的话,可自行通过 C:\MinGW\msys\1.0\msys.bat 脚本创建 MinGW Shell 子菜单;当然,也可以在 MS Windows 桌面或者状态栏创建名为 MinGW Shell 的快捷方式。

2.2、若想全局的设置 MinGW 环境变量,可依次鼠标点击桌面“我的电脑”->选择左侧的“高级系统设置”,选择“高级”->“环境变量”,然后在 PATH 里增加 ;C:\MinGW\bin 声明。也可将 MSYS 的环境变量全局话,也即在 PATH 设置的最后加入 ;C:\MinGW\msys\1.0\bin;C:\MinGW\msys\1.0\local\bin。当然,也可在 MS Windows 的命令行窗口(cmd.exe)中执行

> setx PATH "%PATH%;C:\MinGW\bin;C:\MinGW\msys\1.0\bin;C:\MinGW\msys\1.0\local\bin"

请注意路径 C:\MinGW\msys\1.0\local,它是用户在 MinGW Shell 中安装第三方源码包编译执行 make install 时的安装目录。

2.3、若不想全局启用 MinGW 同时也没有安装 MSYS 基本系统,那么可在命令行窗口(cmd.exe)中临时启用 MinGW,也即

> SET PATH=C:\MinGW\bin;%PATH%

当然,每次都要执行这样的命令还不如建一个环境变量配置脚本来得简单。例如在命令行窗口(cmd.exe)中执行

> notepad.exe mingw32setvar.bat
SET PATH=C:\MinGW\bin;%PATH%

要启用 MinGW 时,可打开命令窗口(cmd.exe)执行

> mingw32setvar.bat

2.4、为了看到前述 MinGW 环境配置是否成功,做一些简单地测试。这里为了简单起见,就以环境变量的配置脚本为例,在命令行窗口(cmd.exe)中执行

> mingw32setvar.bat
> notepad.exe test.cpp
#include <iostream>
using namespace std;

int main(){
    cout << “hello world!”; 
}
> gcc test.cpp
> test.exe

3、为 MSYS 基本系统安装手册页

由于 MSYS 基本系统中没有手册页,一旦遇到不熟悉的 API 就麻烦了,下面介绍一下给 MSYS 基本系统安装手册页的方法。首先打开 MinGW Shell,然后执行

$ mingw-get install msys-groff msys-man

MSYS 基本系统中安装好了手册页工具,试试执行

$ man --help

测试一下。若一切正常,可到 http://www.kernel.org/pub/linux/docs/man-pages/ 下载手册 man-pages-4.02.tar.gz。下载后放到 MS Windows 的用户目录,例如 C:\Users\james,也即 MSYS 基本系统的用户家目录,故直接执行

$ cd ~
$ tar -xzvf man-pages-4.02.tar.gz

进行解压。进入目录手册页源码目录编译

$ cd man-pages-4.02
$ make && make install

大概几分钟后,man 手册就安装成功了。最后,执行

$ man printf

测试一下。

二、Code::Blocks 的安装与配置

1、Code::Blocks 的安装

Code::Blocks 是一个集成开发环境(IDE),本身不含编译,它支持多种编译器,界面近似于 VC。官方网站:http://codeblocks.org。到其官方网站下载最新版本并安装,目前是 codeblocks-13.12-setup.exe,例如安装到 C:\CodeBlocks 目录。

若不想单独配置 MinGW 环境(也即第一步 MinGW 的安装过程),可到 http://sourceforge.net/projects/codeblocks/files/Binaries/13.12/Windows/ 下载安装带有 MinGW 的 Code::Blocks,例如 32 位的 MS Windows 系统选择 codeblocks-13.12mingw-setup.exe、64 位的 MS Windows 系统选择 codeblocks-13.12mingw-setup-TDM-GCC-481.exe。若想在命令行状态下单独使用 MinGW 环境,则须配置系统的环境变量 PATH,例如

> setx PATH "%PATH%;C:\CodeBlocks\MinGW\bin;C:\CodeBlocks\MinGW\libexec\gcc\mingw32\4.7.1"

若第一次运行 Code::Blocks,会要求选 MinGW 为默认编译器。在主菜单 Settings -> Compiler..., Global Compiler settings 子标签页中将 Setected compiler 修改为 GNU GCC Compiler 编译器(就是 MinGW)。在同一个页面中选择 Toolchain executables 选项卡中先修改 Compiler's installation directory 为 MinGW 安装路径,这里就是 C:\MinGW。再在 program files 选项页中选择 C Compiler 为 gcc.exe、C++ Compiler 为 g++.exe、Links for dynamic libs 为 g++.exe、Links for static libs 为 gcc-ar.exe、Debugger 为 gdb.exe;Rescource Compiler 为 windres.exe 以及 Make Program 为 mingw32-make.exe。这样,集成开发环境和编译器就搭建好了。

2、字符编码的设置

自从上帝摧毁了人类建造巴别塔的企图之后,人类的语言就因地域不同而不能直接交流了。计算机中也同样存在着“语言”交流问题,这就是字符编码问题。计算机刚问世的一段时间内,只存储和显示由 8 位字长的二进制数表示的字符,这便是 ASCII 码表示的 ASCII 字符。随着计算机越来越普及,支持多国语言文字提上日程,由各个地区制定的由 ASCII 码衍生出来的表示各地字符的编码统称为 ANSI 编码。例如 GB18030、GB2312、GBK、Big5 等均是中文字符的 ANSI 编码,且前三个是超集关系。ANSI 编码规定:编码位于 0x00——0x7F 之间的字符,依旧是 1 个字节代表 1 个字符。除此而外,不同 ANSI 编码之间不一定兼容。由于这种混乱,Unicode 编码应运而生,它的目标是废除所有的地域性编码方案,重新搞一个包括地球上所有文化所使用的字母和符号的编码。Unicode 编码规定必须用两个字节表示字符(后来还有扩充,可以用以 4 个字节等表示字符),换而言之,所有的字符必须用 16 位字长的二进制数表示。同时,对于原先的 ASCII 字符,Unicode 保持其原编码不变,只是将其字长由原来的 8 位扩展为 16 位,而其他文化、语言的字符则全部重新统一编码。这一点是 ANSI 编码与 Unicode 编码之间的众多区别之一。另外,Unicode 只是规定了字符编码,但没有规定怎么在计算机中存储这些编码。如果直接存储这些编码的话,对于 ASCII 字符来说,第一个字节均是 0,浪费存储资源。这就产生了 Unicode 编码在存储的实现问题。一般,字符编码的存储实现也直接称为编码。UTF-8 编码、UTF-16 编码等都是 Unicode 编码的存储实现。前面的 GB18030 编码、GB2312 编码、GBK 编码、Big5 编码等中文字符的 ANSI 编码既是编码也可以是 ANSI 编码的存储实现。更一般地,ANSI 编码可以与其存储实现一致。在 Linux 世界中,使用 UTF-8 编码是共识。但在 MS Windows 中,字符编码的存储由 Windows 代码页实现。例如,Big5 编码在 MS Windows 中的存储实现是代码页 Windows 950 或者 cp950,GB18030、GB2302、GBK 对应的代码页是 cp54936、cp20936、cp936。另外,微软公司根据中国政府的规定在中国境内销售的中文版 Windows 系统必须设定 GB 编码。这在互联网时代之初带来了困扰中文用户很久的乱码问题,原因在于 GB 编码与 UTF-8 编码除去 ASCII 字符等很少一部分字符重合之外,两者并不完全一致。如此一来,若不对文本做一些额外的处理,跨平台文本交流就会出现乱码。

由于前面的编译器采用的是 MinGW 提供的 GCC,它在编译时默认编译源代码是由 UTF-8 编码编写,编译而成的可执行文本输出时也是 UTF-8 编码。因此,为了让程序正常工作,推荐 Code::Blocks 编辑器中的编码设定为 UTF-8。因此,需要确认一下该编码设置是否正确,也即 Setting -> Editor... -> General Setting -> Other setting 中 Encoding 栏的 Use encoding when opening files 复选框中选择 UTF-8 编码。

如果要用 Code::Blocks 打开久远年代用 GBK 编码写成的工程文件时,特别是打开那些带有久远的库文件的工程时,为了防止乱码,好的选择是再次修改 Code::Blocks 编码设置并更改编译选项设置。例如,老工程是 GBK 编码所写,那么先在 Setting -> Editor... -> General Setting -> Other setting 中 Encoding 栏的 Use encoding when opening files 复选框中选择 GBK 编码,代码页为 Windows 936;以保证 Code::Blocks 编辑器中源代码显示正常。其次,在 Setting -> Compiler... -> Global Compiler Settings -> Compiler Settings -> Other options 框中输入两个选项:-finput-charset=GBK-fexec-charset=GBK。总之,要做到源代码文件的编码和编译时输入、输出编码统一。

3、定制 F1 帮助系统

在主菜单 Settings -> Environment... -> Help files 子标签页中新建一个条目,名为 manual Pages,接着的对话框选 no,然后手动在下面的路径框中填写:man:/usr/share/man;再将使用 F1 快捷键勾上。这样就可以使用 man pages 了。在 Editor 中,将光标停在想要查询的内容上面,按 F1,就会弹出一个 panel,里面就有查询内容的 man 手册,非常的方便。比如 光标位于 printf,按 F1,弹出的 panel 中就显示 printf 的 man 手册。建议安装如下手册页:C API 手册页、posix 函数手册页、C API 手册页 glibc-doc、C++ 标准类库手册页、C++ API 手册页等。

4、变量声明、函数原型等自动提示

每个 Code::Blocks 工程都可在主菜单 Project -> Properties...,Project/Target options -> C/C++ parser options 中添入头文件所在目录,这样信息提示功能才可正常工作,如下是标准库头文件的目录:

  • C:\MinGW\include
  • C:\MinGW\include\c++\4.8.1
  • C:\MinGW\include\c++\4.8.1\mingw32
  • C:\MinGW\include\c++\4.8.1\backward

若项目中还使用了其他库,也可添加更多的头文件路径。例如将后面马上要提到图形界面开发库 wxWidgets 的文件路径添加进去

  • C:\wxWidgets-3.0.1\include
  • C:\wxWidgets-3.0.1\lib\gcc_dll\mswu\

需要注意,添上后不是立即起效,需要过一段时间后才能起作用!

5、自动补全与缩写

首先,打开主菜单 Settings -> Editor... -> Code-completion 标签页,点击 Code-completion 选项卡

  • 将 Keyword sets to additionally include 中1到9都勾上,1 ~ 9 对应的关键字可在 Settings -> Editor... -> Syntax highlighting 的 keywords... 按钮中设置,默认其中 1 是 C++ 关键字,3 是 Doxygen 关键字;
  • 将 Delay for auto-kick-in when typing [.::->] 拉到 200ms,这样快点出来提示;
  • 将 Automatically launch when typed # letters 中的 4 改成 2,这样打两个字母就会有提示了;
  • 将 Case-sensitive match 的勾去掉,它会帮你纠正大小写。

其次,在主菜单 Settings -> Editor... -> Abbreviation 标签页中定义了许多缩写(还可以自定义),只要输入这些缩写,并按 Ctrl+J,就可以自动完成常用的代码框架,并可将光标放在恰当的地方(自定义时用|表达),常用的有:guard、class、switch 等。

6、Code::Blocks 的汉化

Code::Blocks 的本地化(翻译)项目在 https://translations.launchpad.net/codeblocks/+translations 上,有兴趣的用户可以加入。Code::Blocks 的语言包也在该主页上,下载对应的语言包,不过下载前需要注册一个账号。能下载的压缩包有两种:mo 和 po 格式,本地化需要的是 mo 格式文件,下载并打开 mo 压缩包,解压得到里面的文件,找到对应语言的 mo 文件。接着在 Code::Blocks 安装目录下的建立相应语言的文件夹,例如

> mkdir C:\CodeBlocks\share\CodeBlocks\locale\zh_cn

并复制刚才找到的 mo 文件到该位置。启动 Code::Blocks,找到主菜单上的 Settings -> Environment...,Environment -> View 选项卡,在第二行 internationalization 上打钩,在右边选择 chinese(simplified),点 OK。重启 Code::Blocks,界面就汉化了。

安装 Code::Blocks 每日提示双语文件

本文件仅仅用于实现启动 Code::Blocks 时,出现的“今日提示”内容的汉化。请妥当备份本文件,因为在每次进行升级时,该文件都有可能被原英文提示文件覆盖掉。所在以每次升级之后,都需要进行本操作。下载 Code::Blocks 每日提示中英双语文件。请打开 Code::Blocks 安装目录下的 C:\CodeBlocks\share\CodeBlocks 子目录,找到 tips.txt,如有必要,请先备份原文件。然后解压上面下载的文件,覆盖原有文件。

7、为 Code::Blocks 创建新的工程向导文件

Code::Blocks 提供了很多工程向导模板,但是还有很多图形工程没有模板,例如 GTKmm。实际上,在 Code::Blocks 增加工程向导模板是非常容易的,这里以 GTKmm 模板为例。首先,在用户的 Code::Blocks 应用程序数据目录中新建工程向导文件目录 GTKmm,也即 %APPDATA%\codeblocks\share\codeblocks\templates\wizard\gtkmm,通常要在这个目录中创建如下几个文件 wizard.scriptlogo.pngwizard.png 以及模板文件夹 files;这些文件的创建方法可到 Code::Blocks 安装目录中查看相应例子。接着,向 Code::Blocks 注册新建的工程文件,也即编辑 %APPDATA%\codeblocks\share\codeblocks\templates\wizard\config.script,向其中写入如下的行 RegisterWizard(wizProject, _T("gtkmm"), _T("GTKmm project"), _T("GUI"));

8、Code::Blocks 配置文件备份与还原

Code::Blocks 的配置文件位于 %APPDATA%\codeblocks\default.conf 之中,用户可以修改或者保存它。不过 Code::Blocks 不建议直接备份它,而是在 Code::Blocks 的安装目录中提供了一个可执行文件 cb_share_config.exe,它可以用来导出、导入配置:

> C:\CodeBlocks\cb_share_config.exe

9、Code::Blocks 语法高亮配置

Code::Blocks 的知识百科上提供了 16 中语法高亮配置包,请看 http://wiki.codeblocks.org/index.php?title=Syntax_highlighting_custom_colour_themes,也即文件 colour_themes。使用方法非常简单,首先将该文件解压,接着关闭 Code::Blocks,用文本编辑器打开 %APPDATA%\codeblocks\default.conf,将解压文件中介于 <colour_sets></colour_sets> 之间的内容替换成原来内容。

10、Code::Blocks 的外部工具(Tools、Tools+)以及社区插件需要的外部程序

Code::Blocks 有内建插件、社区插件( contrib plugin)。有些插件直接就可以使用,例如代码补全、帮助文件等;有些插件在 MS Windows 平台直接可用,但在其他平台需要配置,例如代码格式化(Astyle)、拼写检查(Spellcheck);还有一些插件是需要安装外部程序才能使用的,例如

再有些插件只有特定平台才能使用,例如 Dev-pack 插件只能在 MS Windows 上使用,而检查内存泄漏的 Valgrind 插件无法在 MS Windows 上使用。

Code::Blocks 的 Tools、Tools+ 具有插件的功用,但那些功能还有没有插件实现。例如,代码的版本库功能对于管理代码非常有效,但目前 Code::Blocks 还没有完全实现这些插件。于是可以利用外部工具 Tools、Tools+ 与版本管理工具 git、svn 来实现一些简单的功能。当然,对这些强大的功能,还是希望有比较完善的插件来实现。

三、wxWidgets 界面库的编译与安装

wxWidgets 是一个由 C++ 编写的用来提供图形界面的开发框架。它最早是由 Julian Smart 于 1992 年开发的,包含了一个可以支持现今几乎所有操作系统(MS Windows、Unix/Linux with X11/GTK+/QT/Motif、MacOS、OS/2)的图形界面库,提供了类似微软基础类库(MFC)的功能。但是,wxWidgets 不仅仅是一个图形界面开发库,同时它也内置了基于 ODBC 的数据库、线程库以及网络通信库等类库。除此而外,wxWidgets 还有很多第三方库的支援,例如 wxSQLite、wxMathPlot、wxHTTPSever 等。特别要说一下,wxWidgets 库的后续版本还提供了对掌上电脑(SYMBIAN、iOS)的支持,还打算在新版本中对 Android 设备提供支持。wxWidgets 宣称使用其库所开发的软件只需要对源代码做少量更改(或者完全不用更改),就能在各种平台上编译并运行。wxWidgets 库使用了大量的宏,使用它开发的代码编译后尽量使用目标操作系统的本地图形界面样式。换而言之,wxWidgets 开发的程序界面,在 MS Windows 中显示 MS Windows 的样式,在 Linux 的 GNOME 桌面(KDE 桌面)显示的 GTK+ 样式(QT 样式),在 Mac OS X 中则显示 Aqua 样式。

虽然 wxWidgets 库本身使用 C++ 语言开发,但也有其它不同编程语言的绑定,例如:Python(wxPython)、Lua(wxlua)、Perl(wxPerl)、Ruby(wxRuby)、Smalltalk(wxSmalltalk)、Java(wx4j)、C#(wx.NET)甚至是 JavaScript(wxjs)等。

wxWidgets 的授权许可证是经过开放源代码促进会认证的,等同于 GNU 宽通用公共许可证(LGPL)。特别地,wxWidgets 授权允许修改者以自己的许可证发布。它的官方网址:http://www.wxwidgets.org/

wxWidgets 的安装方法大概有三种:稳定版源码包编译安装、开发版源码编译安装以及二进制版类库安装,下面开始逐一介绍。

1、到其官方网站下载最新版本的 wxWidgets 源码,目前最新版本为 3.0.1,推荐下载 wxMSW-3.0.1-Setup.exe,默认安装在 C:\wxWidgets-3.0.1 目录下。在 MinGW shell 中依次执行如下命令

$ cd /C/wxWidgets-3.0.1
$ ./configure
$ make
$ make install

请注意 make install 会将 wxWidgets 库文件安装到 MSYS 基本系统的根目录,此地便是 C:\MinGW\msys\1.0\local。这样的好处是,若在 MinGW Shell 中执行

$ wx-config --cxxflags
$ wx-config --libs

便可看到使用 wxWidgets 库文件的编译参数信息。

前述编译方法是 Linux 中源代码编译、安装的标准方法,甚至来编译参数设定也可以用典型的 Linux 方法,也即 wx-config 命令,当然,这一切以安装了 MSYS 基本系统为前提。但在 MS Windows 上,对于 wxWidgets 库文件来说,一般并不推荐安装,直接在源码的编译目录中使用它即可。鉴于这种处理方式,给出 wxWidgets 库文件在 MS Windows 平台的正常编译方法,也即在 MS Windows 的命令行窗口(cmd.exe)中执行:

> cd C:\wxWidgets-3.0.1\build\msw

里面有很多 Makefile,它们用来控制 wxWidgets 库文件的编译过程。实际上,wxWidgets 可被分别编译成 Release/Debug、ANSI/Unicode 与动态/静态版本的库。动态版本库的扩展名是 .dll,静态版本库的扩展名是 .lib 或者 .a;用 d 表示 Debug 版本,没有 d 则表示 Release 版本;u 表示 Unicode 版本,没有 u 表示 ANSI 版本。选择不同的版本库,编译出来的库文件路径及其头文件路径也是不一样的,例如 msw、mswd、mswu、mswud 分别对应采用 ANSI 字符集的 Release 版本、采用 ANSI 字符集的 debug 版本、采用 Unicode 字符集的 Release 版本、采用 Unicode 字符集的 Debug 版本;再如 wxbase30.lib、wxbase30d.lib、wxbase30u.lib、wxbase30ud.lib 分别对应采用 ANSI 字符集的 Release 版本、采用 ANSI 字符集的 Debug 版本、采用 Unicode 字符集的 Release 版本、采用 Unicode 字符集的 Debug 版本,且它们都是静态库。版本库在调试起见,使用 Debug 版;正式发布时,肯定需要 Release 版,这也是 Code::Blocks 源代码中的 cbp 工程文件所需要使用的 wxWidgets 版本;至于程序源代码字符集采用的是 ANSI 还是 Unicode,可在工程“属性”->“常规”->“字符集”里面查看与设置。例如采用 Unicode 字符集的 Release 版本动态库的编译方法

> mingw32-make -f makefile.gcc MONOLITHIC=0 SHARED=1 UNICODE=1 BUILD=debug

采用 ANSI 字符集的 Debug 版本静态库的编译方法

> mingw32-make -f makefile.gcc MONOLITHIC=0 SHARED=0 UNICODE=0 BUILD=debug

无论哪种编译结果,可分别在 C:\wxWidgets-3.0.1\lib\gcc_dll\mswudC:\wxWidgets-3.0.1\lib\gcc_lib\mswd 及其上层目录看到很多 wxWidgets 头文件与库文件。若想把 wxWidgets 库文件变成一个,可启用 MONOLITHIC 参数。另外一个参数 vendor 值得一提,它是指编译或者发布 wxWidgets 库的作者或机构,默认是 custom,而由 Code::Blocks 团队发布的 wxWidgets 库的 vendor 则是 cb。若重新调整编译参数之后再编译,最好先清理先前编译留下的残余。于是可尝试

> mingw32-make -f makefile.gcc MONOLITHIC=1 SHARED=1 UNICODE=1 BUILD=release vendor=juk clean
> mingw32-make -f makefile.gcc MONOLITHIC=1 SHARED=1 UNICODE=1 BUILD=release vendor=juk

编译之后可在 C:\wxWidgets-3.0.1\lib\gcc_dll\mswu 及其上层目录中看到编译好的头文件与库文件。 有关编译过程的详细参数说明请看文件 C:\wxWidgets-3.0.1\build\msw\config.gcc;详细安装说明看文件 C:\wxWidgets-3.0.1\docs\msw\install.txt

2、若想编译最新的 wxWidgets 源码,可从 wxWidgets 项目的源代码仓库中拖源代码。目前,该仓库由版本管理工具 git 管理,故先在 msysgit 提供的 Git Shell 中拖源代码:

$ cd /c/
$ git clone https://github.com/wxWidgets/wxWidgets.git wxWidgets_git

然后可在 MinGW Shell 中执行编译过程

$ cd /c/wxWidgets_git
$ ./autogen.sh
$ mkdir mswbuild && mswbuild
$ ../configure
$ make

当然,这是 Linux 中的开发版源代码的典型处理方法。如果没有安装 MSYS 基本系统,也可采用 MS Windows 平台的典型编译方法,例如在命令行窗口中执行

> cd C:\wxWidgets_git\build\msw
> mingw32-make -f makefile.gcc MONOLITHIC=1 SHARED=0 UNICODE=0 BUILD=release vendor=juk

编译之后可在 C:\wxWidgets_git\lib\gcc_lib\msw 及其上层目录中看到编译好的 Debug 版的头文件与静态库文件。如果有兴趣的话,自行比较一下 MS Windows 平台中不同编译参数下生成的库文件的大小。

3、如果不想自己编译,可以到 http://sourceforge.net/projects/wxpack 下载已经编译好的库(wxPack)的最新版本,目前是 wxPack_v2.8.12.01.exe。一般 wxPack 的版本相比源码编译的版本要老。wxPack 的安装目录可选在 C:\wxWidgets-2.8。值得一提的是,wxPack 包提供了供 VC、GCC 使用的各种版本的库文件,还提供了图形界面快速构建工具 wxformbuilder。wxformbuilder 对于本身没有为 wxWidgets 提供快速构建工具的集成开发环境(例如 Visual C++、Visual Studio、Xcode 等)提供了极大的方便。它的官方主页 http://www.wxformbuilder.org

4、现在,可以利用 wxWidgets 库创建 wxWidgets 项目应用程序。不过,由于库文件的使用是个比较复杂的问题,此地为简单起见,以稳定版源码包编译并安装到 MSYS 基本系统的 wxWidgets 库为例,测试一下前面编译的 wxWidgets 是否能够正常使用。在 MinGW Shell 中执行

$ notepad.exe sam.cpp
#include <wx/wx.h>

class Simple: public wxFrame
{
public:
    Simple(const wxString& title);
};

class MyApp: public wxApp
{
public:
    virtual bool OnInit();
};

Simple::Simple(const wxString& title) :
    wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(250, 150))
{
    Centre();
}

IMPLEMENT_APP (MyApp)

bool MyApp::OnInit()
{
    Simple *simple = new Simple(wxT("Simple"));
    simple->Show(true);
    return true;
}

接着编译并运行它

$ g++ sam.cpp `wx-config --cxxflags --libs`
$ sam.exe

当然,如果没有安装 MSYS 基本系统的话,在 MS Windows 命令行窗口(cmd.exe)执行也可以,只要配置好了环境变量,也即配置好全局 PATH 保证能够找到 wx-config;不过最好还是等到将 wxWidgets 整合到集成开发环境之后再来试比较好。实际上,wxWidgets 自带了 demo 程序用来测试 wxWidgets 库是否正常工作,例如

> cd C:\wxWidgets-3.0.1\demos\bomb\
> mingw32-make -f makefile.gcc

不过由于 demo 程序很久没人维护,必定会报错。不过不用担心,编译报错的原因只是 makefile.gcc 中的 CXXFLAGS 需要增加 -I..\..\lib\gcc_dll\mswu\wx\include\msw-unicode-3.0,可试试下面的命令

> mingw32-make -f makefile.gcc
> bomb.exe

四、整合 MinGW、wxWidgets 与集成开发环境

使用 wxWidgets 开发库是个头疼的问题,首先,系统中可能同时有多个版本的 wxWidgets 开发库;其次,编译出来的开发库可能是静态库,也可能是动态库;再次,在程序编译与运行过程中,动态库的加载有顺序问题。此类问题对于如何在 Code::Blocks 使用 wxWidgets 开发库造成了一些麻烦。不过,稍微留意一下附录中提到的 MS Windows 系统中动态库的加载顺序,就会有很多解决方案。例如将库文件放在程序可执行文件的目录中;或者放在通过环境变量 PATH 指定的某个路径中。为了避免因设置全局 dll 路径而引起 dll 灾难(dll hell),MS Windows 程序发布往往采用前者。

刚刚指出了运行程序时如何解决库文件的搜索问题。但是,由于开发过程不仅牵涉到程序运行,还要保证程序的正常编译,这就要保证不仅库文件要被找到,还有与库文件关联的头文件也要被找到。要解决库及其头文件的放置能够让程序正常编译,比较好的办法是通过 GCC 编译器的编译参数。以下就来谈谈将 MinGW、wxWidgets 与集成开发环境整合成整体。

先来说说全局配置 wxWidgets 库,主要介绍 5 种方法。当然,这些方法之间还能相互共用产生更多的方法,特别是再结合后面要谈到的工程相关(局部)配置方法。作为日常使用 Code::Blocks 来说,只需要掌握工程相关(局部)配置方法就可以了。话说回来,集成开发环境的库配置方法所涉及的原理不仅限于 Code::Blocks,同样适用于众所周知的集成开发环境(例如 Visual C++、Visual Studio、Dev-C++、QT-Creator、CodeLite、Xcode 等)。

1、由于 GCC 编译器可以通过 GCC 编译参数的编译参数找到它们所需要的库文件与头文件,于是通过设置编译选项与链接选项即可达成目的。

1.1、设置编译选项。先在 MinGW Shell 中执行

$ wx-config --cxxflags
-I/usr/local/lib/wx/include/msw-unicode-3.0 -I/usr/local/include/wx-3.0 -D_LARGEFILE_SOURCE=unknown -DWXUSINGDLL -D__WXMSW__ -mthreads

然后开启 Code::Blocks,将 -IC:\MinGW\MSYS\1.0\local\lib\wx\include\msw-unicode-3.0 -IC:\MinGW\MSYS\1.0\local\include\wx-3.0 -D_LARGEFILE_SOURCE=unknown -DWXUSINGDLL -D__WXMSW__ -mthreads 拷贝到主菜单 Settings -> Compiler...,Global Compiler settings -> Compiler settings -> Other options;

1.2、设置链接选项。在 MinGW Shell 中执行

$ wx-config --libs
-L/usr/local/lib -Wl,--subsystem,windows -mwindows -lwx_mswu_xrc-3.0 -lwx_mswu_webview-3.0 -lwx_mswu_html-3.0 -lwx_mswu_qa-3.0 -lwx_mswu_adv-3.0 -lwx_mswu_core-3.0 -lwx_baseu_xml-3.0 -lwx_baseu_net-3.0 -lwx_baseu-3.0

然后将 -LC:\MinGW\MSYS\1.0\local\lib -Wl,--subsystem,windows -mwindows -lwx_mswu_xrc-3.0 -lwx_mswu_webview-3.0 -lwx_mswu_html-3.0 -lwx_mswu_qa-3.0 -lwx_mswu_adv-3.0 -lwx_mswu_core-3.0 -lwx_baseu_xml-3.0 -lwx_baseu_net-3.0 -lwx_baseu-3.0 拷贝到 Settings -> Compiler...,Global Compiler settings -> Linker settings -> Other linker options。

实际上,若在 Linux 上配置 wxWidget 库的话,那么编译选项与链接选项的设置更为简捷,只需主菜单 Settings -> Compiler...,Global Compiler settings -> Compiler settings -> Other options 编译选项设置为 `wx-config --cxxflags`;Settings -> Compiler...,Global Compiler settings -> Linker settings -> Other linker options 链接选项设置为 `wx-config --libs`。这种设置方法在 Linux 中很有优势,不过在 MS Windows 中无法使用。

另外,以上设置方法对于在非工程模式下编译源代码文件非常有效。例如按上法配置完 wxWidgets 库之后,在 Code::Blocks 中新建空文件,也即 File -> New -> Empty File,然后将前面 sam.cpp 的内容复制进来,并保存重命名为 sam.cpp。试试 Build。

2、通过分离 GCC 编译选项中的路径选项也可配置 wxWidgets 库。

2.1、设置编译选项。开启 Code::Blocks,进入主菜单 Settings -> Compiler...,Global Compiler settings -> Compiler settings -> Other Options,将 -D_LARGEFILE_SOURCE=unknown -DWXUSINGDLL -D__WXMSW__ -mthreads 加入。

2.2、设置链接选项与库文件。在 Settings -> Compiler...,Global Compiler settings -> Linker stettings 标签页中的 Other linker options 文本框中加入 -Wl,--subsystem,windows -mwindows;接着在 Global Compiler settings -> Linker stettings 标签页的对话框 Link libraries 下,点击 ADD 按钮,将 C:\wxWidgets-3.0.1\lib\gcc_dll\mswu\ 中所需要的库一一选中加入即可。

2.3、设置搜索目录。Settings -> Compiler...,Global Compiler settings -> Search directories -> Compiler 设置头文件目录为 C:\wxWidgets-3.0.1\includeC:\wxWidgets-3.0.1\lib\gcc_dll\mswu\wx\include\msw-unicode-3.0。注意,这里没有设置库文件的搜索路径,原因在于前一步中装入的库文件名是绝对路径。如果上一步加入的库文件只有文件名没有路径的话,那么这一步还要设置库文件的搜索路径,也即 Settings -> Compiler...,Global Compiler settings -> Search directories -> Linker 设置库文件目录为 C:\wxWidgets-3.0.1\lib\gcc_dll\mswu\

这种设置方法与第一种全局设置方法的差异在于 wx-config 输出中的 -I、-L 以及 -l 参数(也可部分地)替代成了路径与文件名。

3、通过环境变量配置全局设置库文件及其头文件的路径也是一种途径。

全局设置库文件及其头文件的路径是 Linux 的常用方法,例如 Linux 系统常常将库文件放入 /usr/lib 而头文件则放入 /usr/include。此地利用 MSYS 基本系统来做类似的事情,假设已将 wxWidgets 库装入 C:\MinGW\msys\1.0\local

3.1、设置编译选项。开启 Code::Blocks,进入主菜单 Settings -> Compiler...,Global Compiler settings -> Compiler settings -> Other Options,加入 -D_LARGEFILE_SOURCE=unknown -DWXUSINGDLL -D__WXMSW__ -mthreads

3.2、设置链接选项。在 Settings -> Compiler...,Global Compiler settings -> Linker stettings 标签页中的 Other linker options 文本框中加入 -Wl,--subsystem,windows -mwindows;接着在 Global Compiler settings -> Linker stettings 标签页的对话框 Link libraries 下面,点击 ADD 按钮后弹出的文件选择对话框中将 C:\MinGW\msys\1.0\local\lib\ 目录下的文件全选就可以将所有选中的文件一次性加。

3.3、设置全局环境变量来替代搜索目录的设定,例如

> SETX LIBRARY_PATH "%LIBRARY_PATH%;C:\MinGW\lib;C:\MinGW\msys\1.0\local\lib"
> SETX C_INCLUDE_PATH "C:\MinGW\msys\1.0\local\include"
> SETX CPLUS_INCLUDE_PATH "C:\MinGW\msys\1.0\local\include"

需要注意,这种方法毕竟与 MS Windows 习惯不同,故而在 MS Windows 平台很少使用。

4、通过 Code::Blocks 的全局变量也可配置 wxWidgets 库。

Code::Blocks 的全局变量可在工程创建期间使用,也可在各个配置选项卡中使用,下面通过全局变量配置 wxWidgets 库给出引用这些变量的方法。

4.1、设置全局变量。点击主菜单 Setting -> Global Variables...,会弹出一个 Global Variable Editor 的窗口, 在该窗口的 default 变量集下新建立一个 wx 变量,在左下 Build-in fields 的 base 中填入 C:\wxWidgets-3.0.1、include 中填入 C:\wxWidgets-3.0.1\include、lib 中填入 C:\wxWidgets-3.0.1\lib\gcc_dll\mswu、cflags 中填入 -D_LARGEFILE_SOURCE=unknown -DWXUSINGDLL -D__WXMSW__ -mthreads、lflags 中填入 -Wl,--subsystem,windows -mwindows -lwx_mswu_xrc-3.0 -lwx_mswu_webview-3.0 -lwx_mswu_html-3.0 -lwx_mswu_qa-3.0 -lwx_mswu_adv-3.0 -lwx_mswu_core-3.0 -lwx_baseu_xml-3.0 -lwx_baseu_net-3.0 -lwx_baseu-3.0。 除去 Build-in fields 之外,还可以有 user-defined fields(自定义域)。

4.2、使用全局变量。从主菜单 Settings -> Compiler...,Global Compiler settings -> Compiler settings -> Other Options 中填入 $(#wx.clfags);Global Compiler settings -> Linker settings -> Other linker options 中填入 $(#wx.lflags);在 Global Compiler settings -> Search directories -> Compiler 中填入 $(#wx.include)$(#wx.lib)\wx\include\msw-unicode-3.0.1;在 Global Compiler settings -> Search directories -> Linker 中填入 $(#wx.lib)

从配置过程可以看到引用 Code::Blocks 的全局变量 wx 的方法是 $(#wx),引用 Build-in fields(或 user-defined fields)lib 的方法是 $(#wx.lib)。另有一点,带域的全局变量,如 $(#wx.include)、$(#wx.lib) 等,有缺省值。例如不给定 $(#wx.lib) 的值,则默认为 (#wx)\lib。但是,带域的全局变量不能缺省,例如 $(#wx) 本身是不能缺省的,否则会报错。

实际上,也可以在 Code::Blocks 中定义全局定制变量(custom variable),也即打开主菜单 Settings -> Compiler...,Global Compiler settings -> Custom variables,点击 ADD 添加按钮增加变量,供 Global Compiler settings 子选项卡中的各个设定使用。例如设置定制变量(custom variable) VENDOR,引用的方法是 $(VENDOR)。一般而言,定制变量的使用不要太依赖于外部程序。

5、Code::Blocks 的全局变量结合系统环境变量也是一种方法。

由于 Code::Blocks 的全局变量可以引用系统的全局变量,因此可通过系统环境变量配置 wxWidgets 库。例如,在环境变量里添加一个 wxWidgets 根目录环境变量,这里命名为:WXWIN,值为 C:\wxWidgets-3.0.1,例如

> setx WXWIN "C:\wxWidgets-3.0.1"

然后再打开主菜单 Settings -> Global Variables...,在 Global Variables Editor 窗口的 default 变量集下新建立一个 wx 变量,在左下 Build-in fields 的 base 中填入 ${WXWIN},include 中填入 ${WXWIN}\include${WXWIN}\lib\gcc_dll\mswu\wx\include\msw-unicode-3.0.1,lib 中填入 ${WXWIN}\lib\gcc_dll\mswu。由此,可以看到 Code::Blocks 引用系统的环境变量方法类似与 Bash Shell 中的变量引用。

为了与前一种设定方法相比较体现出新意,这里将全局变量用在 wxWidgets 工程向导里。设置完成后,尝试建立 wxWidgets 工程。先通过菜单 File -> New -> Project...,选择最后面的 wxWidgets 工程。点击 Go 进入工程配置向导,首先会出来一个欢迎窗口,在这里直接下一步。在选择已安装的 wxWidgets 版本时请注意,由于安装的 wxWidgets 版本是 3.0.1,所以选择它。接下来的就是 wxWidgets 环境的一些设置了,这里根据刚才设置的 wxWidgets 根目录,直接填入 $(#wx)。剩下几步很容易自行完成。另外,该工程引用的全局变量是来自 default 变量集;如有需要,可在 Setting -> Global Variables... 弹出一个 Global Variable Editor 的窗口定义更多的变量集,同时可在主菜单 Project -> build options,Project build options -> EnvVars options 中选择要使用的变量集。

全局配置 wxWidgets 库的方法谈到这里。前 4 种全局配置方法(第 5 中全局变量配置已针对 Code::Blocks 工程了)对于使用 wxWidgets 库的非工程模式的源代码编译很有帮助。但是可以看到弱点,那就是很多用不到 wxWidgets 库的源代码也在不必要的使用这些编译选项、链接选项,这可能造成错误。因此,有必要说说如何避免全局配置 wxWidgets 库了。首先,对于非工程模式的(特别是单文件)源代码,尽量在命令行环境中而不要在集成开发环境中开发;如果非要在集成开发环境中进行的话,将非工程模式的源代码导入集成开发环境的工程中来。而对于那些由 Code::Blocks 工程管理的使用 wxWidgets 库的源代码,配置使用 wxWidgets 库的方法最好是基于项目的,也即局部配置。此种配置方法只需要使用 Code::Blocks 的工程属性便能实现。

下面仅给出局部配置 wxWidgets 库的一种方法。借鉴前面的全局配置方法,很容易给出更多的局部配置方法。 先启动 Code::Block,打开或者创建一个 wxWidgets 工程,随后点击主菜单 Project -> build options,打开的 Project build options 子标签。

1、设置编译选项。在 Compiler settings -> Other options 子标签页中加入编译选项 -D_LARGEFILE_SOURCE=unknown -DWXUSINGDLL -D__WXMSW__ -mthreads

2、设置链接选项与库文件。在 Linker settings -> Link libraries 中将 wxWidgets 库的所有库文件(动态库或者静态库)加入,也即点击 ADD 按钮后弹出的文件选择对话框中将 C:\wxWidgets-3.0.1\lib\gcc_dll\mswu\ 目录下的文件全选一次性加入;在 Linker settings -> Other Linker options 子标签页中加入链接选项 -Wl,--subsystem,windows -mwindows

3、设置搜索目录。在 Search directories -> Compiler 子标签中加入 wxWidgets 头文件的路径:C:\wxWidgets-3.0.1\includeC:\wxWidgets-3.0.1\lib\gcc_dll\mswu\wx\include\msw-unicode-3.0.1;若上一步的 Link libraries 已带有绝对路径,就不必设库文件的搜索路径了。不然还需在 Search directories -> Linker 子标签中加入库文件路径:C:\wxWidgets-3.0.1\lib\gcc_dll\mswu\。 同样地,也可以在工程属性中定义定制变量,也即 Project -> build options,Project build options -> Custom variables,点击 ADD 添加按钮增加变量,供 Project build options 的子选项卡中的各个设定使用。例如定义工程相关的定制变量 RELEASE,引用的方法是 $(RELEASE)。

五、编译 Code::Blocks 源代码

源码编译 Code::Blocks,也有两种方案:稳定版源码编译以及开发版源码编译。值得提到的是三点:一、不借助 MSYS 直接在 Windows 平台上编译 Code::Blocks 源代码,最好使用 Code::Blocks 二进制文件来完成源代码的编译,这就是所谓的自举;二、用 Code::Blocks 编译 Code::Blocks 源代码,需要使用 Unicode 字符集 Release 版的 wxWidgets 库,除非修改 cbp 工程文件;三、用 Code::Blocks 编译 Code::Blocks 源代码,还需确保 zip.exe 在环境变量 PATH 指定的路径中,最好还能保证 svn.exe 也在其中。

先来说说稳定版源码的编译过程,先到 Code::Blocks 的官网下载最新的稳定版源码,当前是 codeblocks_13.12-1.tar.gz,将它下载后解压到 C:\CBSC。现在用 Code::Blocks 打开 C:\CBSC\src\ 目录中的 CodeBlocks_wx30.cbp(根据 wxWidgets 库的版本以及 CPU 的类型选择),打开过程中会弹出两次 Global Variable Editor 窗口;第一次弹出的 Global Variable Editor 窗口中 Current Variable 项中的内容应该是 wx,在左下 Built-in fields 的 base 中填写 wxWidgets 库所在目录,例如 C:\wxWidgets-3.0.1,然后点 Close 按钮;第二次弹出的 Global Variable Editor 窗口中 Current Variable 项中的内容应该是 cb_release_type,左下 Built-in fields 的 base 中要填写 Code::Blocks 的编译版本类型,若是 Debug 版,请填写 -g,若是发布版本,请填写 -O2,一般选择 Debug 版本,然后点 Close 按钮;随后点主菜单上的 Build 开始编译 Code::Blocks。如果一切正常,会在 C:\CBSC\src\devel30 目录中出现编译好的 codeblocks.exe,试试运行它。如果没有发现任何问题,修改 cb_release_type 的值为 -O2,再次编译,并在命令行窗口中执行

> cd C:\CBSC\src
> update30.bat

这样会在 C:\CBSVN\src\output30 目录中出现正式发布的 codeblocks.exe。为了 Code::Blocks 运行不受动态库版本与路径的干扰,可复制 wxWidgets 库文件至该目录。当然,刚刚编译好的 Code::Blocks 是没有插件的,不过编译插件的步骤大同小异。用 Code::Blocks 打开 C:\CBSC\src 目录下的 ContribPlugins.workspace,根据需要选择要编译的插件项目,过程与 Code::Blocks 编译相仿。

若想编译最新的开发版代码,用 TortoiseSVN 或者在它的可执行文件路径下可执行

> cd C:\
> svn co svn://svn.code.sf.net/p/codeblocks/code/trunk CBSVN

从 SVN 版本库编译可在 MinGW Shell 中执行下述命令

$ cd /c/CBSVN
$ ./bootsrap
$ mkdir mswbuild && cd mswbuid
$ ../configure --with-contrib-plugins=all
$ make

需要注意这是以 Linux 的方式编译 Code::Blocks,一定要保证 wxWidgets 库已被安装到 MSYS 基本系统,且 wx-config 有正常输出。

对于没有 MSYS 基本系统的用户,可在 MS Windows 命令行窗口(cmd.exe)中设置环境变量

> cd C:\CBSVN\src
> set CB_ROOT=C:\CodeBlocks\
> set GCC_ROOT=C:\MinGW\bin\

主要是指定二进制版 Code::Blocks 的安装位置以及 GCC 编译器(这里是指 MingW)的可执行文件目录位置。接着执行

> batch_build_core_30.bat

之后与前面一样会两次弹出 Global Variable Editor 窗口;第一次弹出的 Global Variable Editor 窗口中 Current Variable 项中的内容应该是 wx,在左下 Built-in fields 的 base 中填写 wxWidgets 库所在目录,例如 C:\wxWidgets-3.0.1,然后点 Close 按钮;第二次弹出的 Global Variable Editor 窗口中 Current Variable 项中的内容应该是 cb_release_type,左下 Built-in fields 的 base 中要填写 Code::Blocks 的编译版本类型,填写 -g,然后点 Close 按钮;随后点主菜单上的 Build 开始编译 Code::Blocks。如果一切正常,会在 C:\CBSVN\src\devel30 目录中出现编译好的 codeblocks.exe,试试运行它。如果没有发现任何问题,那么可执行

> update30.bat

这样会在 C:\CBSVN\src\output30 目录中出现正式发布的 codeblocks.exe。当然,编译好的 Code::Blocks 是没有 contrib 插件的,要编译插件编译只需接着执行

> batch_build_all_30.bat
> update30.bat

即可。

若想发布刚刚编译好的 Code::Blocks,可以采用 zip 包的形式。当然,对于普通用户来说还是为它提供一个打包的安装程序比较合适。MS Windows 上收费的打包安装程序有很多,例如 Visual Studio 自带的安装程序、InstallShield 等;也有很多免费的打包安装程序,例如 NSIS 或者 Inno Setup 等。根据自己的需要选用它们,官方推荐使用的是 NSIS 脚本,如想使用可用下述方式检出

> svn co svn://svn.code.sf.net/p/codeblocks/code/setup

自行修改其中的 setup.nsi 脚本。

附录

MS Windows 中动态链接库(dll)的搜索顺序

MS Windows 系统可以包含同一个动态链接库(dll) 的多个版本。应用程序能够通过使用动态链接库重定向或清单文件指定要加载的 DLL 的全路径。下面谈谈具体的动态库搜选顺序,详细内容请参考 http://msdn2.microsoft.com/en-us/library/ms682586.aspx

一、标准的搜索顺序

DLL 的搜索顺序取决于是否安全 DLL 搜索模式是启用或禁用。安全 DLL 搜索模式在默认状态下是启用的。通过创建 HKLM\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode 注册表项并将它的值设为0可以关闭这个属性。直到 Windows XP 与 Windows 2000 with SP4,安全 DLL 搜索模式在默认状态下是禁用的。通过创建 HKLM\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode 注册表项并将它的值设为1可以启用这个属性。

假如安全 DLL 搜索模式启用,搜索顺序如下:

  • 应用程序所在的路径;
  • Windows SYSTEM 目录。通过调用 GetSystemDirectory 函数可以获取这个目录的路径;
  • 16 位系统的目录。并没有函数可以获取这个目录的路径,但是它会被查找;
  • Windows 目录。通过调用 GetWindowsDirectory 函数可以获取这个目录的路径;
  • 当前目录;
  • PATH 环境变量指定的路径。请注意,这并不包括每个应用程序的应用程序路径注册表项中指定。在应用程序路径注册表项的键值并不作为 DLL 的搜索路径。

假如安全 DLL 搜索模式禁用,搜索顺序如下:

  • 应用程序所在的路径;
  • 当前目录;
  • Windows SYSTEM 目录。通过调用 GetSystemDirectory 函数可以获取这个目录的路径;
  • 16 位系统的目录。并没有函数可以获取这个目录的路径,但是它会被查找;
  • Windows 目录。通过调用 GetWindowsDirectory 函数可以获取这个目录的路径;
  • PATH 环境变量指定的路径。请注意,这并不包括每个应用程序的应用程序路径注册表项中指定。在应用程序路径注册表项的键值并不作为 DLL 的搜索路径。

二、预备的搜索顺序

由系统指定的标准搜索顺序可以通过调用 LoadLibraryEx 函数加上 LOAD_WITH_ALTERED_SEARCH_PATH 参数值得到改变。标准搜索顺序也可以通过调用 SetDllDirectory 函数得到改变。如果您指定一个备用的搜索顺序,程序将按备用的搜索顺序进行搜索,直到所有相关的可执行模块被找到。系统启动后,DLL 初始化例程处理,该系统将恢复为标准的搜索顺序。

LoadLibraryEx 函数通过指定 LOAD_WITH_ALTERED_SEARCH_PATH 属性和 lpFileName 参数指定一个绝对路径支持一个预备的搜索顺序。请注意:标准搜索顺序和通过调用指定 LOAD_WITH_ALTERED_SEARCH_PATH 属性的 LoadLibraryEx 函数来设置的预备搜索顺序只是有一点不同:标准搜索顺序开始于搜索应用程序所在的路径而预备搜索顺序开始于 LoadLibraryEx 函数所要加载的可执行模块的所在目录。

假如安全 DLL 搜索模式启用,搜索顺序如下:

  • lpFileName 参数值所指定的目录;
  • Windows SYSTEM 目录。通过调用 GetSystemDirectory 函数可以获取这个目录的路径;
  • 16 位系统的目录。并没有函数可以获取这个目录的路径,但是它会被查找;
  • Windows 目录。通过调用 GetWindowsDirectory 函数可以获取这个目录的路径;
  • 当前目录;
  • PATH 环境变量指定的路径。请注意,这并不包括每个应用程序的应用程序路径注册表项中指定。在应用程序路径注册表项的键值并不作为DLL的搜索路径。

假如安全 DLL 搜索模式禁用,搜索顺序如下:

  • lpFileName 参数值所指定的目录;
  • 当前目录;
  • Windows SYSTEM 目录。通过调用 GetSystemDirectory 函数可以获取这个目录的路径;
  • 16 位系统的目录。并没有函数可以获取这个目录的路径,但是它会被查找;
  • Windows 目录。通过调用 GetWindowsDirectory 函数可以获取这个目录的路径;
  • PATH 环境变量指定的路径。请注意,这并不包括每个应用程序的应用程序路径注册表项中指定。在应用程序路径注册表项的键值并不作为DLL的搜索路径。

假如 lpPathName 参数指定了一个路径,SetDllDirectory 函数支持一个预备的搜索顺序。这个预备的搜索顺序如下:

  • 应用程序所在的路径;
  • lpFileName 参数值所指定的目录;
  • Windows SYSTEM 目录。通过调用 GetSystemDirectory 函数可以获取这个目录的路径;
  • 16 位系统的目录。并没有函数可以获取这个目录的路径,但是它会被查找;
  • Windows 目录。通过调用 GetWindowsDirectory 函数可以获取这个目录的路径;
  • PATH 环境变量指定的路径。请注意,这并不包括每个应用程序的应用程序路径注册表项中指定。在应用程序路径注册表项的键值并不作为DLL的搜索路径。

如果 lpPathName 参数为一个空字符串,当前目录将会从搜索顺序中删除。

SetDllDirectory 有效地禁用安全 DLL 搜索模式,而在搜索指定的目录路径。要恢复安全 DLL 搜索模式的 SafeDllSearchMode 注册表值的基础和恢复当前目录到搜索顺序,调用 lpPathName 的参数值为 NULL 的 SetDllDirectory 函数。