2005年05月27日

这是我今天在 Firefox 的扩展页面上看到的一个扩展,它的作用就是允许你订阅喜欢的分类,然后会在 Firefox 上提供一个按钮,只要你点击就可以随机提供一个网页,可能会很有趣。而且它也应该算是社会性软件的一种,因为首先你要注册,然后你的访问记录就会保存下来,同时你可以考虑喜欢还是不喜欢它所推荐的网站。如果喜欢你还可以与他人分享。有兴趣的可以下载这个扩展试一试。

2005年05月20日

因为项目中要用到 Excel 文件,一种方式是存成csv格式进行分析,这种方法需要转存一下。另一种方式就是直接读取 Excel 文件,正好在 PyPI 中看到有这样一个模块发布,于是乎下载下来简单地用一下。

它的主页在这里,上面有windows下的包和跨平台的包。要注意的是,这个包不一定要在windows平台下运行,跨平台都可以。

安装完毕后,安装目录下有一个readme.txt,其中有一个简单的例子,参考一下即会使用,非常方便。同时我注意到,取出的 Excel 中的中文信息是 Unicode 的。

2005年05月17日

详情去它的主页看一看吧。访问

这是一个重新设计的,支持 Sqlite 3.1版以上,实现了 DB-API 2.0接口。特别是许多地方是用C写的,因此上速度要比起1.X快多了。

的确是这样,我有一个使用sqlite的程序,在启动时会读入大量的数据,更新以后速度明显见快。

其它的暂时没有什么感受,大家自已去看一看吧。

几个下载地址:

Sources:
    http://initd.org/pub/software/pysqlite/releases/2.0/2.0.0/pysqlite-2.0.0.tar.gz

Windows binaries for Python 2.3:
    http://initd.org/pub/software/pysqlite/releases/2.0/2.0.0/pysqlite-2.0.0.win32-py2.3.exe

Windows binaries for Python 2.4:
    http://initd.org/pub/software/pysqlite/releases/2.0/2.0.0/pysqlite-2.0.0.win32-py2.4.exe

2005年05月14日

wxPython 中使用图片很简单。一般你可以找一个图片文件,然后在程序中使用它就行了。这样的好处是替换编辑都方便。但是在许多 wxPython 程序中,如Demo,TaskCoach中都使用了另一种方式,那就是将图片转换为 Python 的模块,这样就可以在程序中象模块一样来调用图片了。这样的好处是可以将图片模块放在 Python 路径中,在哪里都可以使用,而不是象图片文件还需要处理目录了。

将图片转换为 Python 模块在安装完 wxPython 之后,会在 Python 的安装目录下 scripts 子目录中有 img2py 的脚本,它可以做这件事。在命令行输入(Windows下执行的是img2py.bat):

img2py

可以看到它的帮助信息。其中比较有用的是:

  • -n name ,它可以给生成的函数加一个名字,以方便调用。
  • -a,它可以将生成的代码追回到指定的文件后。

不过好象一次只能处理一个文件。下面是一个简单的示意:

img2py -n Down down.gif image.py
img2py -a -n Up up.gif image.py

这个处理就是在前面的Blog(《给ListCtrl 加上列排序功能》一文)中对image.py的处理。最后生成的代码为:

#———————————————————————-
# This file was generated by C:\Python23\Scripts\img2py
#
from wx import ImageFromStream, BitmapFromImage
import cStringIO, zlib


def getDownData():
    return zlib.decompress(
‘…’ )

def getDownBitmap():
    return BitmapFromImage(getDownImage())

def getDownImage():
    stream = cStringIO.StringIO(getDownData())
    return ImageFromStream(stream)

#———————————————————————-
def getUpData():
    return zlib.decompress(
‘…’ )

def getUpBitmap():
    return BitmapFromImage(getUpImage())

def getUpImage():
    stream = cStringIO.StringIO(getUpData())
    return ImageFromStream(stream)

这样对于down.gif和up.gif可以能过getDownBitmap()和getUpBitmap()来得到两个图片。是不是很方便,而且别人也不好改你的程序,并且不再需要考虑目录的问题。以后 NewEdit 可以考虑这样的处理。

注意其中的数据代码因为发布Blog有问题我省略了,大家自行下载前面的示例程序看一下就知道,自已试一试也就清楚了。

2005年05月13日

在前一篇 Blog 中已经将我做的工作基本上介绍完成,下面给大家举一个例子。本来不想写一个完整的,不过为了大家测式方便,还是写了一个。

1       #coding=cp936
2       import wx
3       from SortedList import SortedList
4      
5       class TestApp(wx.App):
6           def OnInit(self):
7               self.frame = wx.Frame(None, -1)
8               self.panel = SortedListPanel(self.frame)
9               self.SetTopWindow(self.frame)
10              self.frame.Show()
11              return True
12             
13     
14      class SortedListPanel(wx.Panel, SortedList):
15          def __init__(self, parent):
16              wx.Panel.__init__(self, parent, -1)
17              SortedList.__init__(self, parent)
18             
19              self.load()
20     
21             
22          def init(self):
23     
24              box = wx.BoxSizer(wx.VERTICAL)
25             
26     
27              self.list = self.createlist( [
28                  (u'',     0,     'right'),
29                  (u'序号',     70,     'right'),
30                  (u'名称',     120,    'left'),
31                  (u'描述',     250,    'left'),
32                  ]
33              )
34              box.Add(self.list, 1, wx.EXPAND|wx.LEFT|wx.RIGHT, 2)
35     
36              self.SetSizer(box)
37              self.SetAutoLayout(True)
38     
39          def initdata(self):
40              datas = {} #为了发布Blog将半角改为全角
41              for i in range(100):
42                  datas[i] = (”, i, ‘name%02d’ % i, ‘desc%02d’ % i)
43              return datas
44             
45          def load(self):
46              number = self.loaddata()
47              self.SortListItems(col=1)
48     
49      app = TestApp(0)
50      app.MainLoop()

我已经把源代码放在 wiki 上,大家可以点击这里访问。这是一个最小运行集。可以看到覆盖了 init(), initdata() 这两个方法。

但这里要说明的是,为什么这里多了一列字段呢?就是上面黄色背景的语句。这是因为我发现在给 ListCtrl 赋予图片列表后,第一列总有一个图,就是你的第一个图。为什么会这样呢?在wxPython 的邮件列表上查了查,看到了Robin的回答,他说这是Windows下的一个问题,无法改变。因此也看到有人的方法就是多搞一列但列头和内容都为空,宽度设为0。我就是这样做的,这样图片就看不到了。但一定要注意,此时真正的数据是从下标1(索引从0开始,也就是第二列开始的)。

SortListItems(col=1)用来设置第二列进行排序。

以前用过 wxListCtrl 类,但一直没有实现列的排序功能,这次在公司的项目中终于实现了,下面是一个简单的记录,并不是完整可运行的源码,如果感兴趣需要自已进行测试。不过我实现的已经是为了满足我个人的要求而封装了一下,因此有些代码只能是参考一下,主要是了解基本的思路。

最主要的思路和参考要看Demo中的ListCtrl的例子,在这个例子的 OverView 中有主要的描述,主要内容是:

  1. 使用位于 wx.lib.mixins.listctrl 中的 ColumnSorterMixin 类
  2. 你的类必须有 GetListCtrl 方法,它将返回可以排序的 ListCtrl 的对象实例。这个实例必须在调用 ColumnSorterMixin.__init__ 之前已经存在,因为 ColumnSorterMixin 会调用 GetListCtrl 来得到 ListCtrl 实例。
  3. 列表中的每行数据必须要调用 SetItemData 来设置一个唯一值。
  4. 你的类必须要有一个名为 itemDataMap 的属性,它是一个字典,键值要与调用 SetItemData 时的设置一致,值就是所有列的一个列表或 tuple 。

看上去的确挺复杂的。同时在排序的 Demo 中我们还看到当点击某列时,会在列头的右边有一个小图形表示升序或降序,那么需要自已定义一个图片列表。这样为了正确返回图片列表中的索引需要再覆盖GetSortImages方法。那么为了简化这些处理,我写了一个类 SortedList :

1       #coding=cp936
2       import wx.lib.mixins.listctrl as listmix
3       import modules.Image as images
4       import wx
5       import sys
6      
7       class SortedList(listmix.ColumnSorterMixin):
8           def __init__(self, parent, id=-1):
9               self.il = wx.ImageList(16, 16)
10     
11              self.sm_up = self.il.Add(images.getUpBitmap())
12              self.sm_dn = self.il.Add(images.getDownBitmap())
13             
14              #完成组件的初始化
15              self.init()
16             
17              self.list.SetImageList(self.il, wx.IMAGE_LIST_SMALL)
18     
19              listmix.ColumnSorterMixin.__init__(self, self.columns_num)
20             
21          def init(self):
22              """组件初始化,其中生成ListCtrl时需要调用createlist方法
23             
24              用户需要覆盖此方法"""
25              pass
26     
27          def initdata(self):
28              """生成一个数据字典,值为每行记录的所有列的tuple,生成后需要返回
29             
30              用户需要覆盖此方法"""
31              datamap = {} #此处应该为半角括号
32     
33          def createlist(self, columns):
34              self.columns_num = len(columns)
35              self.list = wx.ListCtrl(self, -1,
36                                       style=wx.LC_REPORT
37                                       | wx.LC_SINGLE_SEL
38                                       )
39     
40              info = wx.ListItem()
41              info.m_mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_FORMAT
42     
43              for i, v in enumerate(columns):
44                  name, length, align = v
45                 
46                  if align == ‘left’:
47                      info.m_format = wx.LIST_FORMAT_LEFT
48                  elif align == ‘center’:
49                      info.m_format = wx.LIST_FORMAT_CENTER
50                  else:
51                      info.m_format = wx.LIST_FORMAT_RIGHT
52                  info.m_text = name
53                  self.list.InsertColumnInfo(i, info)
54                  self.list.SetColumnWidth(i, length)
55     
56              return self.list
57         
58          def loaddata(self):
59              self.itemDataMap = self.initdata()
60              items = self.itemDataMap.items()
61              for key, data in items:
62                  data = map(self.str, data)
63                  index = self.list.InsertStringItem(sys.maxint, data[0])
64                  for i, t in enumerate(data[1:]):
65                      self.list.SetStringItem(index, i+1, t)
66                  self.list.SetItemData(index, key)
67              return len(self.itemDataMap)
68     
69          def str(self, s):
70              if isinstance(s, (int, float, long)) :
71                  return "%s" % str(s)
72              else:
73                  return s
74     
75          def GetListCtrl(self):
76              return self.list
77     
78          def GetSortImages(self):
79              return (self.sm_dn, self.sm_up)

第9-12行是生成图片列表,一个是向上的三角,一个是向下的三角用来指示升序或降序。关于这里图片的生成是使用一个叫img2py的程序生成的。在安装完wxPython之后会在 Python 的安装目录的scripts目录下有相应的指行文件可以进行调用。关于它的故事我会另写Blog进行说明的。那么这个程序的作用就是把图片转成 Python 程序来使用,很方便。因此 images 就是存了两个这样的图片,并且提供了方法可以得到图片。

第15行是调用一个init的方法,这个init将由用户去覆盖。用户应该在这个方法中实现 ListCtrl 的创建。在创建 ListCtrl 用户可以调用我已经写好的 createlist() 方法,这个方法将生成的 ListCtrl 对象绑定到 self.list 上。

第17行是在组件全部初始化之后,将图片列表与 self.list 进行绑定。

第19行是调用 ColumnSorterMixin 类的初始化函数。还记得吗?要求先要生成一个 self.list 的实例,而且这个实例可以通过 第75行定义的 GetListCtrl() 来得到。

第21行是需要用户覆盖的init()方法,主要是用来生成用户界面。

第27行是生成一个数据字典,按照要求每个值是一个tuple或list,对应一条记录中的每个字段。注意在查过源代码后我知道每个字段的值可以是非字符串,这样在对数字类型进行排序时会准确一些,如果按字符串进行排序可能不准确。不过,在向 ListCtrl 添加数据时如果使用这个字典,则需要先转成字符串。这个方法最后要将生成好的数据字典返回。这个方法是需要用户根据实际情况进行覆盖的。

第33行提供了一个方便地生成 ListCtrl 对象的方法。用户只需要提供一个(名字, 宽度, 对齐方式)的tuple数组即可。名字用于显示在列头上。宽度为对应列的宽度。对齐方式为’left’, ‘center’, ‘right’三种。这里只是生成 ListCtrl 的头。

第58行定义了将数据装入 ListCtrl 对象的方法。这个方法使用了前面用户提供的数据字典,并根据每列数据的类型将其转换为字符串。同时安照要求调用了 ListCtrl 对象的 SetItemData() 方法。

第69行定义了将数据转换为字符串的方法。

第75行是实现了按要求覆盖的方法。 

第78行是返回升序和降序图片的索引。

2005年05月05日

TaskCoach 从0.35版开始支持多国语言了,我想有时间中文化一下。于是乎仔细地查看了中文化的实现过程。

当前 TaskCoach 支持荷兰语和英语。

在启动时(taskcoach.py 34),会调用 gui.init() 来初始化界面。于是我跟踪到 gui 子包中,查看了 menu.py ,看到语言的菜单是定义在 view/language 中的,最终的代码为:

class LanguageMenu(Menu):
    def __init__(self, mainwindow, uiCommands):
        super(LanguageMenu, self).__init__(mainwindow)
        self.appendUICommands(uiCommands, ['viewlanguageenglish', 'viewlanguagedutch'])

而 viewlanguageenglish 和 viewlanguagedutch 是在 uicommand.py 中定义的。

        self['viewlanguageenglish'] = ViewLanguage(value=’en’, menuText=_(‘&English’),
            helpText=_(‘Show English user interface after restart’), mainwindow=mainwindow,
            settings=settings)
        self['viewlanguagedutch'] = ViewLanguage(value=’nl’, menuText=_(‘&Dutch’),
            helpText=_(‘Show Dutch user interface after restart’),
            mainwindow=mainwindow, settings=settings)

我们可以看到,这其实是写死的,也就是说如果想扩充新的语言,只能修改程序,增加新的菜单才可以。

然后语言的切换是通过(taskcoach.py 39)来处理的:

        i18n.Translator(settings.get(‘view’, ‘language’))

那么也就是:先从配置文件中取得语言的种类(目前为en和nl),然后设置翻译函数。

查看i18n/__init__.py程序:

class Translator:
    __metaclass__ = patterns.Singleton
   
    def __init__(self, language=None):
        if language == ‘nl’:
            from nl import nl
            self.__language = nl
            #Translator.__locale = wx.Locale(wx.LANGUAGE_DUTCH) # dutch calendarctrl not working properly
       
    def translate(self, string):
        try:
            return self.__language[string]
        except (AttributeError, KeyError):
            return string

这里连Singleton模式都用上了。可以看出,当语言为nl(荷兰语)时, TaskCoach 将导入 nl.py 模块。这是一种写死的方式,新语言的话还要修改这里。打开 nl.py 可以看到是定义了一个大字典,key是英文原文,value是译文。如果想中文化,则应该对照着提供一个类似的文件。这种处理方式并不标准,它没有使用标准的 gettext() 的处理方式,并不方便。

今天加班,没事,想看一看 TaskCoach 的代码学习学习。

TaskCoach 的配置信息是保存在 ini 格式的文件中的。配置文件的读取是通过 config.Settings() 来处理的。config是一个子包,它有几个模块,其中 Settings 是从 ConfigParser 派生来的,它所使用使用的文件名在 meta 子包中的 data.py 模块中有定义。

在settings.py中的相应的代码为:

    def path(self, environ=os.environ):
        try:
            path = os.path.join(environ['APPDATA'], meta.filename)
        except:
            path = os.path.expanduser("~")
            if path == "~":
                # path not expanded: there is no home dir (as on a Mac?)
                path = os.getcwd()
            path = os.path.join(path, ‘.%s’%meta.filename)
        return path

    def filename(self):
        return os.path.join(self.path(), ‘%s.ini’%meta.filename)

可以看到filename()将得到最终的ini文件名,而它又是将self.path()和"%s.ini" % meta.filename合并起来的结果。path()函数见上面。其中可以看到 os.path.expanduser("~"),那么我在windows XP环境下试了试,得到的是:

>>> os.path.expanduser("~")
‘C:\\Documents and Settings\\limodou’

可以看到就是我的用户路径。记得以前在 Python.list 邮件列表得到过一个技巧来得到用户路径,方法要复杂一些,因为它判断的情况要复杂一些。不过在这里要简单一些,我想也是可以考虑的。其实做成函数就无所谓了。

不过后来我发现,真正的数据其实是放在 APPDATA 所定义的目录下的。

>>> os.environ["APPDATA"]
‘C:\\Documents and Settings\\limodou\\Application Data’

发起人为薛粲。现在已经在google上建议了邮件组,主要的目标是集合广大的中国wxWidgets的爱好者,提供大量的翻译与原创建文档,并将这个好的开源项目介绍给更多的软件同行。

整个网站的建站计划参见。共分为三个里程牌,到第三个milestone,站长计划爱好者人数达到10万人,这的确是一个很激动人心的目标,很值得期待。

wxWidgets我并不直接使用,我使用的是wxPython,不过目前wxPython的文档其中有一部分还是与wxWidgets是一致的(好象wxPython已经提供Epydoc格式的文档,这个我暂时还没有用到)。如果有中文的翻译文档会对wxWidgets在中国的传播很有帮助。不过翻译是很累的,希望能够坚持下去。

现在论坛的人数还是比较少,但工作正在开始,希望有兴趣,有热情的帮助加入邮件组与大家一起为推动wxWidgets在中国的发展贡献力量。

现在网站还未正式推出,不过很快了。