2008年03月11日

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

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

2008年02月18日

Review Board 是一个使用Python + django 开发的代码复审工具,看上去不错。

2008年01月10日

在前面的Blog中我已经介绍了关于让按键处理更精确,并且如何当按下一个键后延迟一段时间的处理,那么基本想法就是:在向Queue中添加按键信息的同时,增加当时的时间戳,然后在线程中循环读取这个Queue,直到没有数据为止,然后比较按键时间与当时时间,如果时间间隔不到预设值,则进行循环。当下一次循环时首先从Queue再进行读取,如果有新的按键信息则重新开始计算时间,如果没有,则继续判断是否到了预设时间,如果已经到了,则进行相应的按键的处理。那么为了在读Queue时不阻塞,我是使用了非阻塞的读取,因此需要自已来做循环。

让我们再整理一下需求:需要一个Queue,用来放按键信息,然后当用户录入时,可以不停地放到这个Queue中。有一个线程可以读这个Queue,如果Queue中有从个消息,则全部读出,然后只保留最后一个。当存在最后一个消息时,需要等待一个时间段,如果时间段到达,并且再没有新的消息时,执开始执行处理。

整个处理代码都在modules/AsyncAction程序中。上一次改进后的处理代码为(有删节,去掉了些不是重点的代码):

    def __init__(self, timestep=.1, interval=0.05):
        super(AsyncAction, self).__init__()
        self.q = Queue.Queue(0)
        self.setDaemon(True)
        self.lock = thread.allocate_lock()
        self.stop = False
        self.running = False
        self.timestep = timestep
        self.last = None
        self.interval = interval
       
    def put(self, obj):
        self.q.put((obj, time.time()))
       
    def run(self):
        try:
            while not self.stop:
                if Globals.app.wxApp.Active and not self.q.empty() and not self.running:
                    self.lock.acquire()
                    while 1:
                        try:
                            obj = self.q.get_nowait()
                            if obj:
                                self.last = obj
                        except:
                            break
                    self.lock.release()
                if self.last:
                    if not self.running:
                        if time.time() – self.last[1] < self.timestep:
                            continue
                        self.running = True
                        try:
                            self.do_action(self.last[0])
                            self.last = None
                        except:
                            pass
                        self.running = False
                time.sleep(self.interval)
        except:
            pass
           
    def do_action(self, obj):
        pass

从上面可以看出,在调用put放置消息时,会自动加上当时的时间,然后在线程中会对这个时间进和检查。同时对于Queue的读取是采用非阻塞方式,因此需要自行做循环。

那么我发现,如果考虑使用Queue的带超时时间的阻塞处理方式的话,可以更加简化代码,并且由于阻塞,可以减少不必要的线程的循环,应该可以对资源的占用更少,那么改进后的处理代码为:

class AsyncAction(threading.Thread):
    def __init__(self, timestep=.1):
        super(AsyncAction, self).__init__()
        self.q = Queue.Queue(0)
        self.setDaemon(True)
        self.stop = False
        self.timestep = timestep
        self.last = None
       
    def put(self, obj):
        self.q.put(obj)
       
    def run(self):
        try:
            while not self.stop:
                self.last = None
                while 1:
                    try:
                        obj = self.q.get(True, self.timestep)
                        self.last = obj
                    except:
                        if self.last:
                            break

                if self.last:
                    try:
                        self.do_action(self.last)
                        self.last = None
                    except:
                        pass

        except:
            pass
           
    def do_action(self, obj):
        pass

从上面可以看出,put时不再计算时间了,因为真正的时间的作用是对最后一个消息有效,并且它的作为是为了延迟,所以只要在读取Queue时,采用阻塞方式,并且设置超时时间为预设值,当已经没有可读取的数据时,读取会阻塞一段时间后返回,这时就是一个合适的处理时机了。如果用户没有输入,那么线程将阻塞一段时间,然后再次阻塞。如果有输入,会立刻读出来,减少了循环处理,同时减少了循环所带来的开销。所以现在在UliPad中就是使用这种方式了。

2008年01月08日

昨天与ygao的交流中还谈到关于模板的处理问题。在UliPad中,在输入特殊的内容时,如果有相应的录入模板的配置,它会显示一段模板,如输入def空格,会出现:

def (${2:}):
    ${0}

这样的东西。这就是一个模板。那么在前面我认为ygao的对录入速度的计算会有效提高处理效率和防止不必要的处理。而模板在ulipad与自动补全是在一起处理的,即先处理模板,再处理自动补全相关的内容。具体的可以参见InputAssistant.py程序。那么在模板出现的问题上我们产生了分歧,ygao认为模板与自动补全功能不同,处理也不应该相同,对于模板应该是必定出现,不必要考虑录入速率。而我认为模板的处理与自动补全虽然功能不同,但处理模式相同,用户的体验应该相同,所以统一考虑即可,这样也不用做什么优化。因为要独立出来的话,需要修改代码,反而比统一处理改动要多。

不过我俩的观点很难融合,所以ygao提出能否做一个分支,至少不要让想法只停留在想法上,先做出来再说。我同意了。于是建议他成为ulipad的开发者。这样ygao终于成为ulipad的第一个除我之外的开发者。感谢ygao对ulipad的支持。

昨天又对UliPad的自动完成进行了完善。之所以要完善它,还是起源于ygao的一个patch。那天在网上碰到ygao(他可是为ulipad做了不少的事情,而且都很深入),他说到有几个优化的地方,其中有一个就是当输入速度比较快时,可以不弹出自动完成窗口,当时我就解释说ulipad已经支持了。直到昨天看到了他提供了patch,明白了他的处理方式。

我原来的做法是这样考虑的:用户录入速度有时不希望被打断,于是ulipad最好可以支持,当用户录入比较快时则不显示弹出窗口,直到用户停顿一下,表示他在等待,这时再进行处理。于是我通过线程方式来进行处理。首先有一个队列,然后在所有按键的事件是都会向这个队列插入按键的信息,然后有一个线程一直在运行,它会间隔一个比较短的时间。当它执行时,如果在队列中存在内容,它会取出最后一个,从而丢掉前面所有的信息,这样前面的按键信息将不会被处理,而最后一个会进行处理。可是这样的确存在问题,最重要的就是最后一个键出现后,没有一个延迟时间的判断,这样要么此时线程活过来,很快就执行这个按键的处理,要么正好线程在睡眠,需要等线程苏醒才能处理。也就是延迟时间根本不可控。

那么ygao的patch解决了这个问题。他是在每个事件处理中记录下执行的时间,通过精确比较两次按键的间隔来判断是否要向队列时插入按键信息。但是他的方案有一个问题就是当你输入很快时,即使我停下来,最后仍然不会弹出窗口,必须要在最后放慢录入速度。但是这种录入很难控制。我想最关键的问题就是他的方式只比较了这次与上次的间隔,但是最后一次按键的延迟没有进行处理。为此事,我俩在gtalk上还进行了争论,甚至有些激烈,不过总归是技术讨论。最后我的想法是将两种处理进行合并,我想这样就会好多了。同时ygao的方案因为要计算时间间隔,同时增加了一个输入速度的参数值,更可以精确控制,而且减少了不必要的线程执行开销,速度会更快一些。

最终我的修改是也增加一个时间的计算,就是使用time模块的time()函数来记录当时的时间。但与ygao的方法不同,我将时间的间隔不是放在键盘事件中进行判断,而是仍然放在线程,通过队列的方式进行处理。这样一方面可以仍然丢弃掉过快的按键信息,只保留最后一个键,同时在线程中可以计算当前时间与按键的时间,从而精确控制延迟时间,而不是只要激活就执行。这样会减少一些线程的执行,从而使整体录入速度加快。

现在svn中就是改后的版本了。

2008年01月07日

现在使用好看簿的次数明显的多了起来,主要是可以方便上传一些关于UliPad的图片,并且配以录音进行讲解。在使用中也发现好看簿的日志写起来还是很方便,虽然功能很简单,但是随手记录一些想法还是有意思的。这个日志就象是Twitter的功能。那么这就产生一个问题,存在两个内容源,怎么办。于是我想到了rss的烧制功能。那么Feedburner好象是用不了了,而FeedSky不知道谁把我的名字给用了,于是试着使用了下yahoo的pipes服务。功能的确很强,还有可视化的操作界面,不过感觉就是速度慢了点。结果做出一个rss的feed:

http://pipes.yahoo.com/pipes/pipe.run?_id=qN22A8_83BGS8bHN8SvLAg&_render=rss

很不直观,也不容易记忆,不过可以通过它同时阅读我在不同地方的日志了。

2008年01月05日

目前UliPad的一些功能截图已经开始放在好看簿上去了。好看簿的一项特别重要而又有特色的功能就是图片可以录音,我想这绝对比简单的文字和图片要更方便和有用得多。不过一直还没有尝试过,今天特意录制了一些说明,感觉还不错,有兴趣的可以去听一下。 http://www.haokanbu.com/group/4/

有些还没有录制。

2008年01月04日

What is Indent Moving Functionality?

Sometimes we need to move cursor through class methods or functions
quickly, and how to
do that? I found I can do this according the indent level. So I’v
finished this funcationality according
the indent level to quick move cursor.

Alt + Up Moving cursor to previous same indent level line
Alt + Down Moving cursor to next same indent level line
Alt + Left Moving cursor to parent indent level line, that’s the
indent level should less than current indent level
Alt + Right Moving cursor to child indent level line, that’s the
indent level should great than current indent level

And I think this feature maybe useful.

You should update from svn.

地址在: http://hi.baidu.com/lix%5Fxu/blog/item/16137ac793b6e0d8d000600f.html

这篇blog介绍了如何让UliPad在Linux下运行时也可以显示内存大小的技巧,不过要修改程序,我已经给作者留言,希望可以加入UliPad中。记得前不久,在ChinaUnix上也看到有一篇Blog是作者扩展了php.acp的功能,这样更方便php的代码提示,我给作者留言后,作者同意将他的成果放在ulipad中,非常感谢他的工作。Blog地址在:

http://blog.chinaunix.net/u/8570/showart.php?id=382744

现在使用UliPad的人多了一些,也有一些开始对ulipad进行定制,这是一个好现象,希望有更多的人把ulipad作为工具,并且把他们的心得分享出来,也使得ulipad更好用。

2007年12月23日

新版的Snippet功能要比老版的功能好太多了。

首先从编辑上,老版的分类与内容是分别录入的,而且需要进入专门的界面进行编辑,很不方便。而新版的是可以一边使用,一边编辑,分类与内容可以一起编辑。老版在使用和编辑上不能同时使用,而新版可以,而且可以支持拖拽,拷贝,粘贴和一些快捷键,如删除使用Del。

其次从处理方式上,老版是将分类存为一个文件,分个分类下的内容存为一个文件。而新版可以是多个文件,每个文件包含完整的分类和内容。因此共享起来会更方便。并且你可以同时打开多个snippet文件,并且可以在多个文件之间拖动文件夹(分类)和结点(片段),非常方便。

再有支持一些简捷操作,如从文本编辑器中选中文本,然后拖拽到snippet窗口中的某个结点上,可以是文件夹或内容结点,这样会自动创建新的内容结点。同时在进行结点和文本拖拽时,如果按下了象Ctrl, Shift键还会插入不同的位置。

如果目标结点是文件夹,则什么都不按时,拖动的结点会追加到目标文件夹子结点的最后。如果按下了Ctrl,则加到目标文件夹的后面,而不是作为子结点,如果按下了Shift则加到目标文件夹的前面,而不是作为子结点。

如果目标结点是内容,则按下Ctrl或什么都不按都是加到目标的后面。如果按下了Shift,则加到目标的前面。

当你作出修改后,顶层文件夹结点会有一个带叹号的图标显示,你需要保存你所做的修改。可以在右键弹出菜单选择保存或全部保存,也可以在工具条上选择相应的保存按钮。

你可以点击结点的标签进行标题的修改,也可以在弹出菜单中选择编辑标题的菜单。

你可以在弹出菜单中添加文件夹和内容。

当点中内容结点时,在底部面板会显示一个编辑器,显示它的内容,并且你可以随时进行修改。修改的内容会自动缓存,如果想在下次启动时生效,请保存它。当点击非内容结点时,编辑器将自动关闭。

你可以设置内容结点的语法高亮属性。选中一个内容结点,点右键,在弹出菜单中选择[Preferences...],将可以让你修改语法高亮的设置。缺省为Python。

一些截屏我放在了好看簿上 http://www.haokanbu.com/story/4755/