星期四, 五月 31, 2007

python、Windows与dbm

python的标准库里面dumbdbm,dbm,gdbm,dbhash都提供差不多的功能,提供类似于字典的数据结构,但和内置的字典不同的是,这些dbm的字典是存在磁盘上的,而不是内存里。

python手册里是这样说的。。。
dbm是早期UNIX下dbm程序继承下来的,dumbdbm是一个简化的便于移植的实现,gdbm是GNU对dbm的实现,bdhash是基于bsddb的实现。

可我还是不知道用哪个,遵循我一贯的原则,我喜欢带g的那个,于是我用gdbm,简单易用,确实不错(我还顺便测了一下性能,发现gdbm比dbm要快一倍左右,更加坚信我的选择)。

直到我试图将代码移植到win32下的时候,我才知道,原来dbm,gdbm在windows下都是没有的,原来别人早想到这个问题,并提供了anydbm模块通吃这几种dbm,原来windows下什么字典存储都没提供,全部都是移植过来的。

anydbm可以自动在四种dbm中做选择,这样不论是Linux还是Windows,anydbm都能选择正确的dbm,对使用者来说,无需任何判断。对于已经创建的dbm文件,也能自动匹配格式并打开,anydbm似乎优先使用dbhash,而不是我喜欢的gdbm,我总觉得一个字典数据结构去劳bsddb的大驾有点。。。。不过反正都已经缺省安装了,就随它吧。

一个缺点是,一般dbm文件在删除记录时,文件都保持大小不变,以便以后增加记录继续使用,这样会使得dbm文件越来越大,gdbm提供了一个reorganize接口可以"瘦身"dbm文件,可惜其它几种dbm不支持,所以anydbm也就没有提供这个接口。换成anydbm之后,我只好将就用saveas-rename来代替reorganize了。

星期三, 五月 30, 2007

glade的类型替换

gtk的TextView是很强大的控件,基本上类似于Windows下的RichEditor,但是TextView有一个我认为是非常致命的缺陷,不支持undo,一个没有undo的编辑控件是很难用在比较成熟的应用上的。

我有个程序在使用TextView,所以我一直想给它加上undo,但又找不到简单好用的方法,最近发现可以使用gtkSourceView来代替gtkTextView,gtkSourceView是用于显示和编辑源码的编辑控件,它比gtkTextView又要强大了(比如支持语法高亮),并且支持了undo/redo。

我很兴奋的去尝试gtkSourceView,却发现glade不支持这个控件(晕了,glade到3.0版本了居然还有很多gtk控件不支持),有两个方法:
- 使用glade的custom widget,然后在代码里面来手动创建这个SourceView控件。
- 使用glade加载时的类型替换功能
第二个方法看起来非常的Coooool,它可以在glade文件加载时,通过一个参数传递一个类型替换表,表示将glade中定义的某些类型替换为另一种类型,也就是说,可以从TextView,替换为SourceView,当然缺省是什么也不替换的。

看看python的代码,是这个样子的。。。(超级简单啊)
override = {}
override["GtkTextView"] = gtksourceview.SourceView
override["GtkTextBuffer"] = gtksourceview.SourceBuffer            
self.m_uixml = gtk.glade.XML (RESOURCEFILE, "app", "", override)
这样就OK了,所有的GtkTextView在加载后都变成了GtkSourceView,强悍。

相应的,原来使用TextBuffer的地方也要改成SourceBuffer。

星期二, 五月 29, 2007

pyGtk程序如何写?

我收集了很多Linux下的pyGtk程序,并做了一个有趣的比较,目前比较关注三个地方
- 使用galde还是不使用?
- 使用何种方法安装到系统?
- 如何做i18n?

下面是统计出的一个列表:
glade install i18n
gmail-notify √ × xml
gjosts × ? gettext
disksearch √ makefile gettext
comix × setup.py gettext
christine √ autotool gettext
emesene × × gettext
meld √ makefile gettext
quod libet × makefile gettext
revelation × autotool gettext
scribes × autotool gettext
gourmet √ setup.py gettext
penguintv √ setup.py gettext
mirage × setup.py gettext
deluge √ setup.py gettext
straw √ setup.py gettext
gpodder √ makefile gettext
jbrout √ × gettext

(?表示没有搞清楚,makefile表示手工写makefile,autotool表示使用GNU的autoconf和automake,setup.py表示使用python提供的distutils)

这样的统计可以得出一些有意义的结论:
- 如果要做i18n的话,不要犹豫,使用gettext不会错。
- 是否使用glade,可能要根据应用考虑一下,但似乎使用glade的应用稍多一些。
- 使用python的distutils/setup是个不错的选择。

星期四, 五月 24, 2007

c标准库为什么要分成多个部分?

C标准库有15个头文件,实现大部分是放在libc.a中,但有一些实现却不包括在libc.a中,比如数学库在libm.a中,为什么不合在一起?

原因是math库和其它库不太一样,math库包含大量的算法,有些项目不希望用标准math库,可能需要自己实现math库,将math单独放在libm.a就可以满足这种需求了,链接的时候直接用非标准math库替换libm.a就可以了。如果libc.a和libm.a放在一起,就做不到了。

星期三, 五月 23, 2007

GNU build系统 I

提到automake,autoconf我就头大,但还是需要还是要理解这些东东的,否则永远也不能理解GNU developer world。

GNU build系统是一组工具的集合,用于简化开发人员的build工作,将开发人员的精力集中在代码实现上,而不是花费在烦人的build script上(比如makefile,install script。。。),这一组工具主要是autoconf和automake,使用他们除了简化build script外,还额外得到了更好的跨平台特性,因为GNU build系统会生成一个cofngure脚本,这个脚本会根据平台特性生成相应的makefile,而且这个makefile可以很好的兼容各种 make版本。

例子,例子。。。。,建立三个文件:
`hello.c'
#include <stdio.h>
main()
{
printf("Hello, world!\n");
}

`Makefile.am'
bin_PROGRAMS = hello
hello_SOURCES = hello.c

`configure.in'
AC_INIT(hello.c)
AM_INIT_AUTOMAKE(hello,0.1)
AC_PROG_CC
AC_PROG_INSTALL
AC_OUTPUT(Makefile)

然后,演出开始,依次执行
$ aclocal
$ autoconf
$ touch NEWS README AUTHORS ChangeLog
$ automake -a
$ ./configure
$ make
$ make distcheck

如 果这些工具都已经安装的话,一阵滚屏之后,hello程序生成了,甚至连第一个发布的版本hello-0.1.tar.gz也准备好了。如果我们修改了 hello.c,要再发一个版本,也很简单,先用make distcheck检测一遍,保证没有问题。然后修改configure.in中的版本号,修改说明文档NEWS,ChangeLog,再调用make dist,新的gz生成了,一切搞定(命令行有时真是爽啊)。

爽过之后,来理性的分析一下过程,大致的过程是这样的
*.m4, configure.in ------------->{aclocal}-----> aclocal.m4
aclocal.m4, configure.in -----> {autoconf} --------> configure
Makefile.am --------> {automake} -----------> Makefile.in
Makefile.in ----------> {configure} ------------> Makfile

m4 是macro的缩写(因为m后面有4个字符),这里定义的是autoconf需要看到的宏,aclocal可以自动生成这些宏,并写入到 aclocal.m4中,后面的三个步骤比较明显。其中漏掉的那个touch命令,是用来建立GNU规范所需要的几个文档文件,否则automake会失 败。

这样生成的makefile在调用编译器时会加上很多-D参数(来自于configure),过多的参数可能会导致make失败,可 以统一将这些参数放在一个头文件中(config.h),为了使用和产生config.h,需要改动几个地方,首先需要改动configure.in,在 AC_INIT下面加上AM_CONFIG_HEADER(config.h),然后所有的源文件前要加上:
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
并在执行autoconf之后,执行autoheader,这一步相当于:
configure.in -----> {autoheader} --------> config.h.in
这样就可以了,后面的configure将生成两个文件
config.h.in,Makefile.in ------> {configure} -------> config.h,Makfile

如果觉得这样的hello,world太麻烦(我也是这么想的),可以下载autotoolset这个工具来做自动化生成,(这个工具是rpm格式,在debian/ubuntu下需要用alien -i安装),安装完后,执行acmkdir dirname就可以按照向导生成一个目录框架了。

星期二, 五月 22, 2007

GNU的ar和ranlib

GNU工具中ar是用来制作库文件.a的,但同时还提供了一个ranlib,从手册上看ranlib相当于ar -s,为什么这样呢?

这是由于最早在Unix系统上ar程序是单纯用来打包多个.o到.a(类似于tar做的事情),而不处理.o里的符号表。Linker程序则需要.a文件提供一个完整的符号表,所以当时就写了单独的ranlib程序用来产生linker所需要的符号信息,也就是说那时,产生一个对linker合格的的.a文件需要做ar和ranlib两步 。

很快,Unix厂商就发现ranlib做得事情完全可以合并到ar里面去,于是ar程序的升级版本就包括了ranlib的功能,但早期的很多项目的Makefile都已经是按照两步式的方法生成.a,所以为了保证这些早期文件的兼容性,ranlib被保留下来了。

如今,GNU/Linux系统上,ranlib依然存在,当然大部分项目已经不使用它了,因为ar -s就做了ranlib的工作。
历史通常是进步和妥协的混合!

星期日, 五月 20, 2007

代码中的版本信息

很多项目的版本信息是定义在单独的一个存放常量的文件中的,甚至和其他常量都不放在一起,而存放在一个单独的文件中,为什么版本信息能享有这么高的待遇呢?

这是我的理解,很简单,如果我们使用版本控制系统,比如Subversion等,当我们使用分分支的时候,不同的分支经常需要merge,比如trunk和一个release分支就可能因为修改同样的bug而merge,而修改代码经常需要同时修改版本号,那么如果将版本信息和源代码放在一起的话,这份包含版本信息的代码就不能自动merge了(因为不同的分支版本号肯定是不同的),所以将版本号单独放的话,其它的源码代码文件就可以采用自动merge了。

星期六, 五月 19, 2007

gtk的default widget

gtkWindow的default widget和Windows下作用一样,是用来接管回车键的。但在细节上有很多和Windows下不一样:
- 在代码里可以用set_default去设置default widget(不一定是按钮,也可以是其他widget),也可以在glade里面设置这个widget的has_default为True,还可以通过 gtkDialog的set_default_response去按response设置,几者是等价的。
- 从显示上我们可以很容易看出哪个控件是default,当然这和theme有关,对于Ubuntu缺省的Theme,default按钮上会套一层黄色的边框,随着focus的移动,这个黄色的边框也会移动(因为有时focus到一个别的按钮上,它会接管回车),总之这个黄色的边框套在哪里,哪个控件就处理回车,很直观。
- 控件能不能成为default的,还要取决于控件的一个can_default选项,如果不设置这个选项为true,调用set_default就会失败。
- 有些控件在获得焦点时,回车可能会自己处理,这样就不能激活default按钮了,典型的就是输入框gtkEntry,不过gtkEntry提供了选项可以设置这种行为,允许回车激活default,在glade下只要设置按钮的"Activates Default"为True就可以了。

总的来看还是比较清晰,最后一条曾经害的我翻遍了手册。

星期三, 五月 16, 2007

UIManager和glade

glade和UIManager都可以创建UI,我之前没有用过UIManager,使用glade对于固定的GUI资源要比UIManager方便一些,但要是有大量需要动态变化的GUI部分,glade就没有用武之地了,这种情况下通常需要UIManager,UIManager只能处理菜单和工具栏。剩余的界面部分还是要交给glade,或者也可以交给gtk api来处理。

结合UIManager和gtk api的python程序还是不少的(当然没有用glade的多),比如revelation(一个PIM程序)和comix(一个看漫画的软件),也有glade和UIManager都不用的,只用api,比如emesene(一个Linux下的msn客户端)。



星期二, 五月 15, 2007

GtkWidget的realize与show

pygtk中,几乎每个widget都可以调用show来显示,但除了show之外,还有一个realize接口,realize叫做实例化,负责分配窗口资源,但不在屏幕上显示。如果没有调realize,顶层窗口的show也会自动调用所有子控件的realize,所以一般情况下不需要调用realize,当在show一个控件之前需要这个用到这个控件的window时,才需要提前realize。

show这个名字似乎起的不好,它对于顶层控件和子控件含义是不同的,对于顶层控件,show就是字面含义,但对于子控件,show只是标记它"将会被显示",并不真的显示它,这样的好处是整个窗口是一次性显示的,而不论show的先后调用关系,虽然子控件的show并不能显示控件,但还是必须调用的,否则它的缺省状态会导致它被hide。

还有一个接口是show_all,意义是显示所有子控件,但它有个明显的副作用,就是即使你设置了某个子控件为不显示,show_all会覆盖这些设置,所以我不喜欢用这个接口,除非是比较简单的对话框。

星期六, 五月 12, 2007

GtkMenu的动态创建

使用glade多了,就不太会动态创建菜单,但有时又是必须的,比如recent files菜单就不能用glade创建,下面是大概的python代码示例:
recentmi = gtk.MenuItem("Recent Files")
ui_recentfile = gtk.Menu()
recentmi.set_submenu( ui_recentfile )
filemenu.insert(recentmi, 8)
这是创建了一个子菜单,并插入在file菜单下的一个固定位置,下面是往子菜单里面加入菜单项:
mi = gtk.MenuItem( onefile)
mi.connect( "activate" , self.on_recent_file_open,  onefile)
ui_recentfile.add(mi)

connect时候,这里用了一个user参数来传文件名,这个参数就可以在菜单点击时传给消息处理函数,这样这个消息处理函数就可以很容易区分是点击了哪个文件了。

如果想删除菜单项的话,可以调gtkMenu的remove接口,但是似乎不能按位置删除。


星期一, 五月 07, 2007

pyGtk下设置对话框的图标

为了设置每个对话框的图标,可以调用dlg.set_icon_from_file,这样用的话需要给每个对话框都调用,刚刚发现,使用gtk.window_set_default_icon(gtk.gdk.pixbuf_new_from_file(ICONFILE))
之后,就不需要一次次的调用set_icon_from_file了,方便!