2008年03月29日

在UliPad中定义菜单项时可以同时设定Accelerator(快捷键),通常是采用:

caption\tCtrl+A

之类的形式。但是一旦你这样定义,它就变成全局的了。这样如果你定义了Ctrl+C, Ctrl+V之类的,不管你在哪个窗口按下这样的组合键,它都会被激活。那么这带来一个问题,当你有多个编辑窗口时,这此事件应该对应哪个窗口呢?一种办法是使用FindFocus()方法来查找有输入焦点的窗口,然后调用这个窗口相应的事件处理函数。一般情况下可以足够了。但是对于Ulipad有些特殊。UliPad允许自定义快捷键,同时会修改菜单上的快捷键显示。并且有些快捷键希望不是全局性的,只有在某个窗口有输入焦点时才起作用。因此从这些方面来讲,我希望菜单上的显示只是一个显示,而不是真正的定义,真正的定义我会通过SetAcceleratorTable来进行设定,所以也不需要在设置菜单项文本时自动使用快捷键自动生效。但是我试来试去,发现我无法控制它无效。只要你是按菜单的快捷键来定义的,它就会生效。

想来想去后来想到一个办法。我发现菜单文本是等宽字体,因此我可以不使用\t为分隔,而是使用空格来填充,这样快捷键右对齐,看上去显示效果同真正的快捷键,而且经过试验,的确可以不起作用。但另一个问题时,每个下拉菜单要多宽合适呢?如果每次动态计算最大的宽度很麻烦,后来想了一个偷懒的办法就是假定一个最大的宽度40。其本上够用了。同时还要对汉字以两个字符进行处理。

上面的方法虽然看上去不怎么样,不过这也是我目前能想到的办法了。:)

2008年03月23日

地址在这里 http://fy.py3k.cn/p/web2py/cookbook/cookbook.html

平台是由VCC搭建,本人翻译完成。

对web2py感兴趣的可以先从这里入门了。

2008年03月16日

最近在学习extjs,挺复杂,功能太强了,可能还是入门级的缘故。其中想实现一个表格(Grid),并且有添加,删除之类的功能。网上有不少比较全面的代码示例,如这里这里。我使用了CheckboxSelectionModel,其中看到示例代码:

grid.selModel.selections.keys

可以得到选中行的id,可是在哪里设置呢?看了半天,后来才找到原来需要在reader类中,指定id对应的字段名才可以。这下就行了。

还遇到一个问题就是,使用autoExpandColumn时一定要设置相应的column的id属性,不然会报错,好象还可以使用数字。

2008年03月15日

今天在ulipad中增加了一个新功能:当你运行在windows平台上时,它可以自动查找已经安装的python解释器,这样对于初次使用ulipad的人来说会非常方便。

在windows平台上安装python时,python会在注册表中创建相应的键值。我在effbot上找到一篇文章是关于如何将python环境加入到注册表的,正好拿来一用。具体的查找代码如下:

def check_python():
    interpreters = []
    if wx.Platform == ‘__WXMSW__’:
        from modules import winreg
        for v in (‘2.3′, ‘2.4′, ‘2.5′, ‘2.6′, ‘3.0′):
            try:
                key = winreg.Key(winreg.HKLM, r’SOFTWARE\Python\Pythoncore\%s\InstallPath’ % v)
                interpreters.append((v+’ console’, os.path.join(key.value, ‘python.exe’)))
                interpreters.append((v+’ window’, os.path.join(key.value, ‘pythonw.exe’)))
            except:
                pass
    else:
        version = ‘.’.join(map(str, sys.version_info[:2]))
        interpreters.append((version, sys.executable))
    return interpreters

这里我使用了一个winreg的模块,它已经放在了ulipad的modules目录下,当时是因为开发svn插件找到的。这是一个_winreg的封装,使用起来要更为方便。上面的处理很简单,依将尝试查找2.3到3.0的python版本。找到安装目前,然后分别按console和window两个解释器进行处理。

如果是其它的平台,一般只能是以源码运行的,所以sys.executable应该就是解释器路径了。

2008年03月12日

虽然web2py可以基本上在浏览器完成几乎所有的工作,但是就某些简单的代码测试来说还不是太方便,因此考虑到django中可以通过manage.py shell进入一个命令行,它可以预设一些工作,比如环境变量的设置,这样再进行测试会非常方便。于是研究了一下web2py的调用处理,模仿django写了这么一个工作,目前已经被web2py接受放在了svn中,不过我测试还不多,所以不知道有没有问题。

从svn中同步最新的源码,然后在web2py的文件夹下运行:

Python shell.py appname

因为web2py是以app为运行单位,不过这块我没有怎么测试,简单的示例目前没有问题,与app相关的测试还没有怎么试验。

这样你可以用它来测试controller代码。因为web2py的controller不是完整的python代码,它是在一个环境中运行的,所以直接在命令行下测试是不行的。现在有了这个工具,可以做一些简单的测试了。

2008/03/13

修改shell.py,增加对ipython的支持。缺省会自动查找是否安装了ipython。如果想强制使用缺省python命令行,可以在运行时加上-p参数。同时自动导入app的model文件,可以直接在命令行命令model中定义的内容了。

今天在web2py的邮件列表中看到的,挺有意思,地址如下:

http://mdp.cti.depaul.edu/examples/static/web2py_comics.pdf

Massimo Di Pierro欢迎人转载。

2008年03月11日

json是很方便的web数据格式,特别是用在ajax的数据处理上。许多Python的框架都是使用 simplejson 包来进行处理。不过最近发现我有这样的一个需求,比如我想把一个Python的数据结构转为javascript数据结构,也就是将python的数据结构转为json格式,然后用在模板中使用。如,我有一个模板,其中有javascript代码:

<html>
<head>
<title>Title</title>
<script type="text/javascript">
s = {{=data}}
</script>
</head>
<body></body>
</html>

上面的data我可以这样处理:

def index():
    data = {‘name’:'limodou’};
    return dict(data=simplejson.dumps(data))

这样,通过simplejson.dumps可以将一个python的数据结构转为json格式,结果为:

{"name": "limodou"}

那么,它可以正确处理基本的数据类型。使用它,要求你传入的数据应该是基本的。但是这里可能有问题:

  1. 如何处理非基本类型数据。比如从数据库中读出的日期,一般都是datetime类型,它不是基本类型,直接传入simplejson会报错
  2. 如果我不希望是"limodou",而是limodou能不能呢?为什么会有这个需求,因为在我生成的js代码中,有些可能是函数名,或执行语句,因此不是字符串,不希望自动加引号

对于第一个问题,simplejson的主页和代码中都有例子,方法就是从JSONEncoder中派生子类,然后覆盖default方法,对于特殊的类型进行处理就可以了。

对于第二个,simplejson好象没有什么支持,于是我做了扩展:

import simplejson as sj

class ComplexEncoder(sj.JSONEncoder):
    def __init__(self, classes=[], **kwargs):
        sj.JSONEncoder.__init__(self, **kwargs)
        if not isinstance(classes, (tuple, list)):
            self.classes = [classes]
        else:
            self.classes = list(classes)
       
    def _iterencode_default(self, o, markers=None):
        for _cls in self.classes:
            if isinstance(o, _cls):
                return o()
        newobj = self.default(o)
        return self._iterencode(newobj, markers)
   
class R:
    def __init__(self, obj):
        self.obj = obj
       
    def __call__(self):
        return self.obj

def dumps(obj, classes=R):
    return sj.dumps(obj, cls=ComplexEncoder, classes=classes)

我定义了一个自已的Encoder类,然后覆盖了__init__()方法和_iterencode_default()方法。这样允许对特殊类调用类的转换方法,这样ComplexEncoder就不会对特殊类进行特殊处理了,其结果为特殊类的输出。举例:

>>> print dumps({‘a’:'dddddd’, ‘b’:R(’sssssss’)})
{"a": "dddddd", "b": sssssss}

可以看出sssssss前后就没有双引号了。

因为要在web2py中生成js代码,目前采用的方式是先写一个模板,然后进行处理后进行填充。我采用的是有命的占位符方式,如:

"%(name)s"

那么在处理时有这样的情况:当某些值存在时,生成某些内容;当不存在时不生成。比如:

"hello, %(name)s"

如果name存在,则打印"hello, name"的样式,如果不存在,则不打印。那么一种做法就是把整个"hello, %(name)s"做成一个占位符,如:

"%(hellomessage)s"

if name:
    hellomessage = "hello, " + name
else:
    hellomessage = ""

这样替换,不过从模板角度就不好看了。于是我做了点扩展,可以支持%if和%else的处理。举例为:

text = """class %(classname)s:
%if init:def __init__(self):
    self.name = ‘name’
%else:pass
%fi
"""

你可以定义%if :, %else:(可选) %fi(结束)。它的处理原理是先找到%if, %else, %fi,然后进行if语句的处理,根据条件值,直接输出后面的内容,此时并不处理。然后处理完毕后,再统一进行变量的替换。所以是两步处理。它不支持嵌套,只是为了方便。%if后面的变量需要用户提供,并且可以提供一个环境。代码很简单:

import re
r_if = re.compile(r’%if\s+(?P<condition>.*?):(?P<true>.*?)(?:%fi|%else:(?P<false>.*?)%fi)’, re.DOTALL)
def template(text, vars, env={}):
    def _sub(m):
        c = m.groupdict()
        condition = c['condition']
        t_t = c['true']
        t_f = c['false']
        if eval(condition, env, vars):
            return t_t
        else:
            return t_f
    text = re.sub(r_if, _sub, text)
    return text % vars

举例如下:

>>> print template(text, {‘classname’:'A’, ‘init’:True})
class A:
    def __init__(self):
        self.name = ‘name’

>>> print template(text, {‘classname’:'A’, ‘init’:False})
class A:
    pass

其实已经不能算是开始了,学习有段时间了,不过不长。也许 django 弄得时间长了,新东西不多,有些审美疲劳了,所以想换一个试试口味。感觉 web2py 还的确很有意思,只不过它创建的时间短,而且开发者就一个人Massimo Di Pierro ,一位德国人意大利人,不过感觉很有激情。最近他要参加pycon08大会,还制作了几顶帽子,说他只能支付这么多了。他好象是一位教师,原来讲过 django ,不过现在改讲web2py了。原来这个框架不叫这个名字,原来叫gluon。

我还是初学乍练,所以许多东西还在学习中。不过已经向Massimo提交了几个patch,修复了几个bug,也提了一些建议。我会努力学习。