2005年08月28日

会议从下午1:00一直开到快5点,地址在新浪会议室。非常感谢。

会议是按议程进行的:

  1. HD进行了《开源的发展与企业创新》的演讲。其中讲出了企业对开源软件的一些要求和希望,同时指出企业与开源社区如何互相促进共同发展,为开源软件产品在企业的应用提供了一些很好的思路。并对Python的可为之处提出了建议,明确表示新浪在开源社区的发展一定会大力支持。非常好的鼓舞。
  2. nEO在Skype中对于开源社区的标准的支持提供了自已的想法,并对于一些开源软件的发展提供了一些例子。
  3. 由我进行 EasyGui 的项目介绍
  4. 自由讨论。简单地介绍了一下目前社区的资源整合的情况,及今后要对文档进行完善,希望更多志愿者为大家贡献力量。并且开始考虑 Python 方面的培训。此提供由 TOY 发起,并将于下星期日进行。

到场20人,有一些是新面孔,有一些有事没有来。还有一些通过网络参加。

我的 EasyGui 的语音MP3可以从这里下载。HD的演讲由于我的技术问题没有录制下来,等ZoomQuiet的吧。

2005年08月24日

大家有兴趣可以下载最新的 EasyGuider 程序。目前已经支持 Yaml 格式的数据存储了。

另附 Yaml 的简单示例:

>>> a = {’a':1, 11:’b'}
>>> import yaml
>>> yaml.dump(a)
‘—\n11: b\na: 1\n’
>>> b = ‘—\n11: b\na: 1\n’
>>> yaml.load(b).next()
{’a': 1, 11: ‘b’}

那么到这里我有一个疑问,数字与数字类型的字符串如何区分?经测试,数字不加引号。而数字类型的数字要加引号。

不过再仔细测试,发现boolean类型出错。比如True会转换为字符串。因此建议在使用时要小心。

>>> yaml.dumpToFile(file(‘a.yaml’, ‘wb’), a)
>>> yaml.loadFile(‘a.yaml’).next()
{’a': 1, 11: ‘b’}

Python下好象有好几个Yaml的包,不知道哪个更好一些,有时间再找一找。

为了发布文章,我将半角大括号改为了全解,请注意。

2005年08月23日

具体信息在 wiki 上,下面是内容转贴:

 时间: 2005-08-28 13:00–15:00
地址: 北京海淀区北四环西路58号,理想国际大厦
     新浪北京 20层1号会议室
     (海淀图书城东100米;中关村海龙商厦西200米;
      北京大学南门以南四环南侧)
具体交通:http://wiki.woodpecker.org.cn/moin/MapSinaBus
主题:
1. HD 《新浪的自由之心–新浪与Open Source之缘》
2. Limodou 《EasyGUI 演示》– 从newEdit 到 EasyGui 的wx 应用开发
3. 汇报CPUG 的近期活动,成果
4. 自由交流

本次会课是CPUG 成立后的第一次,期望更多的在京Python 爱好者积极参与哪!
http://wiki.woodpecker.org.cn/moin/BPUG/2005-08-28

会课经过大家的投票已经定为每月最后一个星期的周末,可能为星期六或星期日。有兴趣的并且在北京对 Python 感兴趣的都可以参加。

2005年08月22日

让我们进入中文的处理过程吧。

原本看到 ReportLab 的 test 目录下有一个 test_multibyte_chs.py ,试了试效果不错,就以为中文问题照猫画虎应该没什么问题呀。但等我再仔细深入后,发现问题不那么简单。

问题一:无法使用Paragraph。因示例中使用的是底层的API,因此对于字体要求不高。而使用Paragraph需要知道粗体、斜体、粗斜体这些字体,而示例中并没有这样的处理。

问题二:在解了上面的问题之后,发现中文只有一种字体,实现不了斜体和粗体效果。

现在我已经基本解决了上面的问题,但是无法实现斜体和粗斜体,这个以后再看一看吧。

中文处理方法一

这种方法采用示例中的方式,即使用CID字体,代码如下:

#coding=gbk
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.cidfonts import CIDFont, CIDTypeFace, findCMapFile
enc = ‘GB-EUC-H’
findCMapFile(enc)
face = CIDTypeFace(‘STSong-Light’)
pdfmetrics.registerTypeFace(face)
pdfmetrics.registerFont(CIDFont(‘STSong-Light’,enc))
from reportlab.lib import fonts
fonts.addMapping(‘STSong-Light’, 0, 0, ‘STSong-Light’)
fonts.addMapping(‘STSong-Light’, 0, 1, ‘STSong-Light’)
fonts.addMapping(‘STSong-Light’, 1, 0, ‘STSong-Light’)
fonts.addMapping(‘STSong-Light’, 1, 1, ‘STSong-Light’)

import copy

from reportlab.platypus import Paragraph, SimpleDocTemplate, PageBreak
from reportlab.lib.styles import getSampleStyleSheet
stylesheet=getSampleStyleSheet()
normalStyle = copy.deepcopy(stylesheet['Normal'])
normalStyle.fontName =’STSong-Light-GB-EUC-H’
normalStyle.fontSize = 20
story = []
story.append(Paragraph("<b>你好</b>,中文", normalStyle))
doc = SimpleDocTemplate(‘hello.pdf’)
doc.build(story)

使用CID字体只有STSong-Light一种字体可用,而我查看资料看到其它的如Big5都有两种字体,真是气人。前面说到问题一,需要使用fonts.addMapping来解决,即分别注册四种字形。addMapping共有四个参数,第一个是以后引用的字体名,第二个是粗体标志,第三个是斜体标志,第四个是对应的字体名称。因此上面增加了四种字形。而所用到的字体应该调用pdfmetrics.registerFont()进行字体的注册。而且在实际运行时发现,光注册字体还不够,还要注册TypeFace。在所有字体注册完毕后,下面可以真正中文的测试了。

normalStyle = copy.deepcopy(stylesheet['Normal'])
normalStyle.fontName =’STSong-Light-GB-EUC-H’
normalStyle.fontSize = 20

这里是设置段落将使用的字体名称。很奇怪,字体名称是前面注册的字形名与编码的合并。

运行上面的示例中文文件就生成了。但如果加入英文,也是使用中文字符进行显示的。注意,上面的文件编码设置为gbk

这种方法目前来说我认为并不好,那么好在还有第二种方法。

中文处理的第二种方法

这种方法使用直接处理TrueType字体的方法,但在测试过程中速度有些慢。但好处是可以使用中文的字体文件,当然需要是ttf格式的文件。这样可以引入更多的字体了。代码如下:

#coding=utf-8
import reportlab.rl_config
reportlab.rl_config.warnOnMissingFontGlyphs = 0
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfgen import canvas
pdfmetrics.registerFont(TTFont(’song’, ‘SURSONG.TTF’))
pdfmetrics.registerFont(TTFont(‘hei’, ‘SIMHEI.TTF’))

from reportlab.lib import fonts
fonts.addMapping(’song’, 0, 0, ’song’)
fonts.addMapping(’song’, 0, 1, ’song’)
fonts.addMapping(’song’, 1, 0, ‘hei’)
fonts.addMapping(’song’, 1, 1, ‘hei’)

import copy

from reportlab.platypus import Paragraph, SimpleDocTemplate, PageBreak
from reportlab.lib.styles import getSampleStyleSheet
stylesheet=getSampleStyleSheet()
normalStyle = copy.deepcopy(stylesheet['Normal'])
normalStyle.fontName =’song’
normalStyle.fontSize = 20
story = []
story.append(Paragraph(‘<b>你好</b>,中文’, normalStyle))
doc = SimpleDocTemplate(‘hello.pdf’)
doc.build(story)

处理方法与方法一差不多,主要差别在红色的字部分。文件编码注意是utf-8,而不是gbk了。我是在运行时发现的,使用utf-8,然后直接使用中文就可以,不然就没东西。字体还是要注册,但不是使用CIDFont了,而是使用TTFont来处理了。而且不需要注册TypeFace了。再注册字形,这里我对于粗体字形使用了黑体字。最后对于样式使用字体时,只要使用简单的字形名即可,不再象第一种方法还需要加入编码名称。

上面的处理可以解决正常和粗体的字形,但斜体还是无法支持,但已经好多了。而且好处是可以使用其它的中文字体比如仿宋了,彩云体了。

不过在处理英文时还是存在问题。如果不加特殊处理英文会转变为汉字,的确不好看。那么你可以在段落中加入<font>标签,设置name为想用的英文字体即可。与HTML的标签差不多。但发现所有英文自动变成斜体了。不知道为什么。现在知道一个解决的方法就是不修改样式中的字体名,而是在文本中加入<font>标签来设置中文字体,但这样就比较麻烦。当然通过程序来做麻烦也不怕。比如:

normalStyle = copy.deepcopy(stylesheet['Normal'])
#normalStyle.fontName =’song’
normalStyle.fontSize = 20
story = []
story.append(Paragraph(‘<font name="hei">你好</font>,<font name="song">中文</font>’, normalStyle))
doc = SimpleDocTemplate(‘hello.pdf’)
doc.build(story)

大家有兴趣可以试一试。

2005年08月21日

在前一篇我们已经知道大概 ReportLab 的处理过程,下面我们进入如何输出简单的文档。如果你看到用户手册,你会发现我跳过了许多的东西。是的,因为我认为那些与我们的目标暂时没有关系,许多东西更基础,而且是侧重于如何使用绘制的方法来画东西,而不是如何将文档转变为 PDF 文件。

ReportLab 为了方便生成文档提供了一种叫PLATYPUS(Page Layout and Typography
Using Scripts)的一系列的模板类,它用于控制文档的布局,可以让我们不用关心具体的坐标,绘制命令,甚至分页的处理,而将注意力集中在布局与内容上。

下面是一个应该算是最简单的生成Hello, ReportLab的例子吧。

from reportlab.platypus import Paragraph, SimpleDocTemplate
from reportlab.lib.styles import getSampleStyleSheet
stylesheet=getSampleStyleSheet()
normalStyle = stylesheet['Normal']
story = []
story.append(Paragraph("Hello, ReportLab", normalStyle))
doc = SimpleDocTemplate(‘hello.pdf’)
doc.build(story)

代码要比上一个多几行,但理解并不困难。

第1行我们导入Paragraph和SimpleDocTemplate类。Paragraph是用于生成文本段落的。SimpleDocTemplate是文档布局模板。

第2行导入将要用在Paragraph中的样式。也就是说ReportLab已经为我们预先定义了一些样式,如标准,标题1,标题2,黑体等等,用于生成特殊效果。与Word中的样式有些象。

第3行是得到样式集。

第4行是得到标准样式,它将用在Paragraph的生成中。

第5行是生成一个空列表。这里叫story,在用户手册中你会看到这个单词。它将用来存储象段落这样的对象,其它的还可以是图片等。而这些东西是有顺序的,因此使用了list来保存。因此在用户手册中看到这个单词不要以为是什么特别的对象。

第6行生成我们期待的"Hello, ReportLab"段落,并且它的样式是普通。将生成的结果放在story中。

第7行创建SimpleDocTemplate对象,指定将要生成的文件名。 

第8行执行文档模板的build方法最终生成文件。所以有文档模板类都有build方法。

从上面的例子可以看到通过文档模板及样式可以让我们方便的创建面向对象的应用,而不用再关心坐标、绘制命令等底层的东西,从而可以方便我们的文档生成。打开hello.pdf看一看效果吧。这回就象是真正的文档,"Hello, ReportLab"放在上面了。

这几天都在学习 ReportLab 这个优秀的生成 PDF 的软件包,特别是它的使用手册也都是使用自身的模块生成的,效果很让我满意,因此这段时间也许会把精力放在 PDF 文档生成上面。因此也希望写一个入门的教程方便大家同我一起学习。这就是写本教程的起因。由于有些东西是边学边写很可能会有错误,希望大家帮助我改正。那么学习的目的就是希望可以写出象 ReportLab 的用户手册一样效果的文档,并且希望尽可能的方便。

安装很简单,大家只要下载最新的 1.20 包即可。如果需要处理更多的图像格式还应该下载 PIL 软件包。这块就不细说了。

那么下面开始我们的学习,首先还是从写一个最简单的Hello, World开始,当然这里把Word改为 reportlab 了。

先看代码吧。

from reportlab.pdfgen import canvas
def hello(c):
    c.drawString(100,100,"Hello, ReportLab")
c = canvas.Canvas("hello.pdf")
hello(c)
c.showPage()
c.save()

这段代码是从用户手册改造而来。它的运行结果就是在你的当前目录生成一个hello.pdf的文件,打开它你会看到只有一页的内容,而"Hello, ReportLab"是显示在页面的左下角的。下面解释一下。

使用 ReportLab 来生成 PDF 你可以认为就象是在画图一样,因此第一行从pdfgen中导入了canvas,准备开始绘画。

hello方法是简单地调用Canvas的方法在指定的起始坐标来绘制文本。要注意,在 ReportLab 中坐标的原点缺省是在页面的左下角。因此最后的效果是在页面的左下角。这一点要注意。

第4行生成一个Canvas对象,它指定一个文件名,那么最终的结果将存放在这个文件中。在Canvas的构造方法中还有一些缺省值,比如纸张的大小缺省为A4纸等,现在我们不去关心它,一切从最简单开始。

第6行可以产生一个分页。但由于后面没有更多的输出了,因此它并不起作用。你可以在它的后面再加一个hello(c)的语句,看一看是不是变成两页了。

最后一行很重要,它会将内容保存到文件中。因此如果忘记掉用了,你什么也得不到。

这个例子很简单,但你也许已经注意到了,如果我想生成的是长篇的文档,让我象画画一样来生成不是太难了吗?的确是这样,但这里只是基本的介绍一下使用,ReportLab 已经为了想好了,它有更方便的东西供你使用,可以让你从一个更高的起点开始。但是需要注意,一些相对低层的东西可以实现一些绘图之类的东西,在你的 PDF 上作画也是一个很有趣的事情。

下一篇我将介绍使用文档布局的一个最简单的例子。

2005年08月19日

今天看到 YAML 这个东西,以前似乎看到过,但没有关注过是个什么东西。今天到网上查一查,才知道它是一个类XML格式的数据序列化语言,是专门为象 Python, Ruby, Perl 这类的语言准备的。它的说明原文为:

YAML™ (rhymes with “camel”) is a human-friendly, cross language, Unicode based data serialization language designed around the common native data structures of agile programming languages.

那么它主要的特点就是格式简单,比XML更易读,支持脚本语言的序列、字典之类的数据结构。关于它的文章你也可以在网上找到一些中文的,比如 IBM 开发网站上的 《XML问题:YAML对XML的改进》。上面有关于 Python 和 Perl 的示例。那么我简单地试了试,支持简单对象,支持 Unicode,但生成的结果是 "\uXXXX" 这种字符表示。同时也支持 Python 对象的序列化。那么它就可以完全替换掉 pickle,也可以替换掉我写的obj2ini了。

想要下载可以去 http://python.yaml.org 。但好象有好几个yaml的网站搞不太清楚,能用就行了。

在运行 Dabo 的 ReportDesigner.py 时报告说没有安装 PIL 和 reportlab 包,因为我以前一直使用 Python 2.3 但后来升到了 2.4 因此一些包就没有装。于是下载了 PIL 和 reportlab 包分别装上。这里又对 reportlab 感起了兴趣,于是想再看一下汉字的示例。在 test 目录下找到 test_multibyte_chs.py 然后运行,但什么都没有。出问题了。其中有几行代码:

try:
    findCMapFile(enc)
except:
    #they don’t have the font pack, return silently
    return

我想可能是它有问题,于是将return改为raise,再次运行,结果发现是没找到相应的CMAP编码文件。于是仔细查了源代码,最后发现在 replorlab 目录下的rl_config.py中有关于Acrobat资源的一些设定,而最高版本是6.0,而我现在安装的是7.0,因此它找到到,将所有包含6.0的行复制一下,把6.0改为7.0。再试成功了。

不过最新的版本是1.20,它的测试代码与以前的好象不同了,生成的东西在我的用户临时文件夹下面:

c:\docume~1\liying~1\locals~1\temp\ReportLab_tmp\reportlab_test\

所以你在test目录下是找不到生成的pdf文件的。

2005年08月18日

在上一篇Blog我使用 EasyGuider 生成了一个简单的 wxPython 程序框架,但它非常简单。今天在昨天的基础上增加了菜单和工具条的生成。全部代码和示例模板已经包含在最新的EasyGuider包中了。

操作方式见《使用EasyGuider来生成wxPython代码框架

目前菜单与工具条是写在一个配置文件中,运行时由MainFrame.py来导入。配置文件为:resource.py,内容为

actions = [
   ('ID_FILE', '&File', None, '', 'File', None, None),
   ('ID_FILE_OPEN', '&Open\tCtrl+O', None, 'open', 'Open a file',
'images/blog.gif', None),
   ('ID_FILE_EXIT', 'E&xit\tAlt+F4', None, 'exit', 'Exit the
program', None, 'OnExit'),
   ('ID_EDIT', '&Eile', None, '', 'Eile', None, None),
   ('ID_EDIT_UNDO', '&Undo', None, 'undo', 'Undo last command',
'images/undo.gif', None),
   ('ID_EDIT_REDO', '&Redo', None, 'redo', 'Redo last command',
'images/redo.gif', None),
]

menubar = [
   (None,
       [(100, 'ID_FILE'),
        (110, 'ID_EDIT')]),
   (‘ID_FILE’,
       [(100, 'ID_FILE_OPEN'),
        (110, '-'),
        (120, 'ID_FILE_EXIT')]),
   (‘ID_EDIT’,
       [(100, 'ID_EDIT_UNDO'),
        (110, 'ID_EDIT_REDO')]),
]

toolbar = [
   (100, 'ID_FILE_OPEN'),
   (110, '-'),
   (120, 'ID_EDIT_UNDO'),
   (130, 'ID_EDIT_REDO')
]

actions为最基本的功能集,象Delphi和Qt中都有这样的东西。在这里我借用了这个东西。因为菜单与工具按钮功能很类似,因此为了减少冗余需要先定义action集,然后在它的基础之上生成menubar和toolbar。

一个action的格式为:

  • id 建议大写英文,它将作为引用的键值被menubar和toolbar所使用,当值为’-'时表示分隔线
  • caption 它可以使用标准的菜单的快捷键和加速键的定义方式。
  • type 缺省视为normal,其它可以为check、radio
  • shorttip 短提示,用于工具条
  • longtip 长提示,用于菜单、工具按钮在状态条上的提示
  • image 对应的图像,可以为文件名,image对象或使用img2py生成的函数名
  • funcname 事件方法名,如缺省会自动查找名为以ON开头后面为id的方法

menubar是一个list,由二元tuple构成。每个tuple的第一项是父菜单的id,第二项是一个list,内容为菜单项集合,也是使用二元tuple表示。id为’-’是由系统缺省提供,表示一个分隔线。内容列表中的每个tuple由(order,action_id)构成。order是用于排序。因此菜单的顺序是靠order来排列的,可以随便摆放

toolbar是一个list,由二元tuple构成(order, action_id)。同菜单,order也是用于排序。id为’-’将生成一个坚线。

说明写得很简单,有时间再细化。有兴趣的可以试一试。

2005年08月17日

看到现在越来越多的人想要学习wxPython,但入门是一个问

题,于是我就想可以使用EasyGuider来生成一个wxPython的代码框架,这样方便大家测试和学习。

需要准备:

EasyGuider 模块 http://wiki.woodpecker.org.cn/moin.cgi/EasyGuider
Meteor 模块 http://wiki.woodpecker.org.cn/moin.cgi/Meteor

分别安装后,进行EasyGuider的test子目录,然后在命令行下执行:

EasyAdmin.py -s easyapp.script easyapp.py


按照屏幕录入相应的值,然后finish后就会在相应的目录下生成wxpython的文件。

主文件名为你起的 应用名称.py

因此你的应用名称应该是英文的。

目前生成的代码非常简单,只生成一个窗口,其它什么也没有。但支持命令行参数:-V 显示版本 -u 显示用法。

有兴趣可以试一试。以后可以不断扩展这个模板,可以自动生成复杂的程序。而且照猫画虎还可以生成其它的代码框架。