2006年09月29日

有时在处理时,我们会有许多以空格分隔的行,如下面:

        aaaaa   bbbbbbbbbbb       dddddddddddddd
        bbbbb      cccccccc             pppppppppppppppp

很明显,你会看到它没有对齐,那么经过N次的手工对齐之后我开发了这个名叫rerange.py的Script。它目前已经放在了 UliPad 的 scripts 目录下。你可以在 [Tool]->[Scripts Manager...] 中增加这个 Script。然后使用时,先选中上面两行,然后执行这个Script,你会看到它们被对齐了。

要注意的是,这个Script是以第一行为参照,所以在执行前可以先把第一行调整到合适的位置再执行。还有它是要区分空格的,因此如果aaaaa中有空格就认为是两个串了。但对于’或"中间的空格会忽略。

2006年09月28日

Today I also add other new feature, that’s the multi view. For now, you can add other view of current document and it’ll can be displayed in left side or bottom side. With multi-view you can see different part of one document at the same, and also you san see different documents at the same time. Because multi-view can be different for different document, and there will be multi tag page for different document. So you can switch from document and their views. Because the multi-views can be displayed in left side or bottom side, so you can see max 3 view at the same time, that means that you can see 3 different document at the same time. Of cause if you want to view a document in multi-view, you should open it first.

How to work?
===========

1. The default way

Open an document which you want to apply multi-view to it. And choose [Window]->[Open Multi View Window], and the multi-view of current document will be shown in bottom side.

2. The advanced way

Open an document which you want to apply multi-view to it. Then open left side window or bottom side window, at the notebook tabs, right-click mouse button, you’ll find a menu "Open Multi View Window". So according to which notebook (left or bottom) you selected, the multi-view will display in different side.

Others
=========

Closing the document will auto close the multi-view. And for now the multi-view also can be edited, so if you change something in multi-view, the main document also be changed.

Hope you enjoy it.

Commands Searching is a new feature of UliPad.

What’s it?
=========

I found something similar from textmate (an editor of MAC platform, and it’s not a free software) that you can open a window, and almost all inner commands in it will be found in the list of the window, and if you type something, it’ll auto search the most matched command, and you can run the command from the candidates. I think it’s a cool feature, and I borrow it to UliPad. And I also extend it in some ways. There is a impact mode in Commands Searching feature. In this mode, the popup window will not displayed, and you can see a green bar at the statusbar of the mainframe. So if you input something, you’ll find it’ll be display in the bar, and if there is a matched command, it’ll be executed at once. So using this tech, you can define keystrokes just like vim, for example, you can use "j", "k", "h", "l" to move the cursor.

Do you want to know the details?

How to use the feature?
===================

So you can press Ctrl+K, and a popup windows will be shown. And there is a text control, two checkbox, and a commands list. Commands Searching can be run at two mode: normal mode and impact mode. In normal mode, as you press Ctrl+K, the popup window will be popupped, and in impact mode, the popup window will not display at all, and you’ll find a green bar will be shown in the statusbar of mainframe. And these two modes are a little different. Because in different mode, the searching method is different. In normal mode, as you type some text, Commands Searching will search the caption info or description info of the command, so you should input more text, and the search will be inaccurate match, for example, you type "line", and if the commands info are:

Match selection enlarge (matched l i n e)
Select current line(matched line)

So you can see many lines can be matched. So you can move the selected line using cursor key: up and down. And press enter key when you want to run it. Or double-click the line to run the command.

But in impact mode, it will use impact info to match. And the match will be accurate. So impact info and caption info is different for a command.

How to describe a command
=======================

For now, you can write the commands info in a config file, it’s name is commands.ini, you can find it in UliPad/conf folder. There is a example for the file:

[default]
#key=target,caption,shortcut,impact keys,func_name
cursor_up = editor,"Cursor up","Up",k,execute_key:Up
cursor_down = editor,"Cursor down","Down",j,execute_key:Down

You can find something in the commend line.

For now, target is ‘mainframe’, or ‘editor’. Shortcut of a command is just display. Impact is short string, it can be more than one character. The func_name is the method of the target object, and there are many function style, for example:

def funca(self)  #with one parameter, and will be passed to target object
def funcb(self, param) #with two parameter, the first should be passed to the target object, and the second will be passed None or a string parameter. And you can give the second parameter with ":string", just like above example.

Auto close option
==============

If this option is on, so after you run a command, the popup window or green bar will be closed automatically. If the option is off, you can run many commands and the window or bar will not be closed. So if you are in impact mode, it’ll be very like in VIM.

Todo list
========

I also want to implement commands combination, just like:

[default]
match_sel_left = mainframe,"Match selection left first","Ctrl+E",,OnEditSelectionMatchLeft
match_sel_right = mainframe,"Match selection right first","Ctrl+Shift+E",,OnEditSelectionMatchRight
match_sel_enlarge = mainframe,"Match selection enlarge","Ctrl+Alt+E",,OnEditSelectionEnlarge

[commands]
cmd_select_enlarge = "Matched select text and auto enlarge",,match_sel_left|enlarge

So you can find the last command description is different from the default section, and it combine match_sel_left command and enlarge command. So you can use this way to combine many new commands also.

But this feature is not implemented yet.

2006年09月24日

会课结束了,页面在这里

今天有10人参加。原以为别人还有一些东西要讲,结果只有我要讲。先是做了UliPad的使用介绍,结果有一些原本说是要演示的,到后来东西讲得多了,就忘了。还是边讲边演示的好。另外还出现了一些小bug,结果现在解决了。接着是进行了jQuery的介绍。

录音还没有传上去,等Zoom.Quiet了。我自已听着,真是感觉:不专业啊。呵呵。

地址在这里

从图上可以看出prototype.js的使用是位列第一。而dojo为第三。jQuery是第6,Mochikit是第10。感觉还是轻量级从整体上使用得要多一些。不过我很看好jQuery,从它的设计哲学,它的易用性,它的文档,还是日益丰富的插件,以及在将近一年的时间发展得非常速度。

另一幅图可以看到web server开发语言的情况,python相对还是很低,看来还有许多路要走。

2006年09月23日

这是在jQuery社区酝酿了一段时时间了,我是在Visual jQuery网站上看到了,今天终于出来了,可以从这里下载。杂志做得非常精美,太棒了。使我不由得又想向大家推荐这个好东西。

Visual jQuery和本期杂志的作者是 Yehuda Katz。在杂志的开头,他讲述了如何发现了Scriptaculous和Prototype.js,并且开始以Dom Scripting为主。后来他发现了jQuery的interface库,然后找到了jQuery,从此爱上了jQuery。经历和我类似,在知道jQuery之前,我先知道的Prototype.js。不过从学习和文档上讲,jQuery的确要胜出。

并且jQuery的一些设计很出色,象chainability, 以Dom元素集合为主心的批量处理。在jQuery的影响大起来之后,prototype也随之做了不少相似的改动,这些都是从jQuery学来的。记得在刚看到jQuery时,还有一个宣传的重点就是它的体积,当时在1.0时还是10K,不过现在1.0.1已经是17K了。当然随着以后的发展体积还会更大。就是这么大的体积,它已经足可以完成一般ajax的功能,甚至还包括一些特效。

目前可以用在jQuery上的插件已经非常多了,而且使用很简单。希望大家喜欢上jQuery,并且可以将其用在自已的项目中。

2006年09月22日

Someone has submited a great tool for a long time, and I’v disscused with the author also. I want to merge it into djangoproj plugin for a long time also, and now here it’s. This tool can create a dot format file for certain app of django, then you can use this dot file to create an image also. And now you can use it in UliPad to create a dot file for an app, then you can see the image in a popup window also.

How to use it?

1. You should install graphviz tool. You can download it from http://www.graphviz.org/Gallery.php
2. Install djangoproj plugin and restart UliPad
3. Open directory browser window, and add some django project directory(F2)
4. Set "django" projectname to the directory in context menu(right-mouse click)
5. In an app directory, popup context menu, in the botton "Django

Command"->"Create Dot", then it’ll go

Enjoy it.

Please update the newest version from svn.

Found the info at : http://code.djangoproject.com/wiki/DjangoGraphviz

2006年09月21日

最近一直在做Ajax作为前端界面的生成处理,Django作为后端处理。我的想法是让Django更多的关心数据,让Ajax更多的关心展示。这样的好处就是django的处理会简单化,不用考虑过多的显示处理。这一这程还在摸索中。这种方式越来越让我感受到c/s开发的风格。记得在Ajax in Action一书中说过,Ajax做到极致是脱离对某个后端的依赖,但目前我并不想脱离Django,只是希望简化。

Ajax前端处理

使用Ajax生成界面比使用简单的Html生成界面要复杂得多,调试也要麻烦一些,因此它并不象单纯靠模板来生成页面要快速。但是对于界面要求一些特殊处理的话,使用Ajax是最好的方式。目前我的研究主要集中在这么一个应用模式上:

我有批量的数据需要维护,那么希望一进入是一个列表方式的显示。然后你可以对这个列表增加、删除、修改。并且不离开这个页面。因此这样就需要使用Ajax来实现。使用简单的HTML的方式是无法不离开这个页面的,因为每一次表格的提交都会引发页面刷新,只能通过Ajax才可以实现无页面刷新的功能。

在这个页面上,我首先创建了一个js的表格,它允许你指定表头,表头有许多的字段构成,每个字段由冒号分为三段,如:

['Column1:column:20%', 'Column2:column2:40%']

其中第一段为显示名,第二段为对应的字段名,第三段为宽度。如果宽度省略,则可以不写。

然后这个表格可以允许你追加或插入数据。数据有两种表示方法:数组或字典。如果是数组,则按照表头字段的顺序排列。如果是字典,则需要给出表头字段的数据项。同时你还可以增加其它的数据,也不会影响处理,比如id值。同时这个表格还提供了其它的一些方法和配置项。为了实现ajax处理,要解决以下的问题:

1. 第一次装载时的数据装入

通过指定一个load的参数,表格可以自动根据这个load提供的url通过ajax方式向后台请求数据,这个数据组织为一个数组,第一个元素是表头数据,后面是记录。这样就可以在启动时装入数据了。

2. 增加的处理

需要配置一个增加时的Form参数,这个参数可以是一段html代码,或是一个element,也可以是一个函数,可以返回一个element。表格在得到这个信息之后将显示出这个Form。对于Form我又编写了一个Form的js代码,用于Form的生成和Ajax的处理,及一些处理的事件。并且允许外部函数来取代缺省的处理,以增强灵活性。这样在表格中,将对Form的一些事件进行接管以处理象数据返回,取消之类的事情。当数据返回,将隐藏或删除Form并将返回的数据插入到表格中,实现数据的支态增加。

3. 修改的处理

与增加的处理类似,重用了Form类,但是需要单独定义,与增加的区别是对于ID的处理。在增加中不需要考虑ID,但在修改时需要考虑,因此它还需要挂接一个对url的处理,以正确返回一个ID值,它将用在请求的URL中。因此上,从这里看出,这是与整个后台的处理是绑定的,它约定了一种调用的方式和URL的组织规则。同时对于Ajax的数据交互格式有一种约定。也需要挂接Ajax成功或Form取消的事件。

4. 删除的处理

不需要Form支持,但支持单个删除和批量删除。对于单个删除,直接通过链接调用后台的处理,在处理成功后删除相应的表格行。对于批量删除,首先需要在每个表格行前面增中一个CheckBox,然后通过表头的CheckBox来控制其余的CheckBox的全选或不选功能。而这一功能是可以根据配置项来自动生成的。

Ajax传输数据格式

在前端的处理基本完成后,还需要规范后端及前后端的通讯要求。目前对于通讯格式来说:上传我没有特殊的要求,对于下传来说格式为:

{’response’:'ok|fail’, ‘data’:约定, ‘error’:约定}

对于数据和出错信息要根据不同的应用来规定。比如增加后,将返回增加的对象信息,那么data就是一个对象的字典数据。而error在出错时也是一个字典,它的信息是由Django的Manipulator来提供的,每个key对应一个输入字段,返回值是一个错数信息的数组,这是Manipulator的错误信息的组织格式。对于不属于某个字段的错误,你可以使用’_'作为key来描述。

然后我增加了一系列用于ajax数据处理和json格式返回的函数。比如:

ajax_data(response_code, data=None) 它可以把一个结果封装为上面的要求。response_code可以是True或False,它应被处理为’response’:'ok’或’response’:'fail’。同时根据response_code的值,将data处理为’data’或’error’。

json(data) 可以将一个处理好的ajax格式数据通过simplejson来转为json格式,然后通过HttpResponse()来返回给浏览器。同时,它还会调用一个数据转换函数,这个函数可以将一个复杂的数据结构中的字符串,一起转为Unicode。

simple_ajax(*args) 可以根据参数不同,先转为ajax的格式数据,然后再调用json()返回。这样,如果是只有一个参数,则相当于(True, data),即一个参数时默认response_code为True。

后台数据处理的简化

目前Django是通过Manipulator来实现数据的校验,一个标准的数据标验的处理包括几个步骤,这里不再描述。为了简化,我开发了一个叫EasyModelManipulator的类(它的父类是EasyManipulator),它有几个主要的方法:

1. 初始化

你需要提供一个Model,和一个object_id值。如果object_id不提供缺省为None。object_id将用来区分是增加还是修改。

2. validate_and_save(request, post_bind=True)

你需要传入一个request对象,然后它会根据Model自动生成一个Manipulator,再对request中的数据进行校验。如果成功,则保存入数据库,并返回(True, obj)。如果失败则返回出错信息为(False, error)。可以看到,第一个值为成功还是失败的标志。post_bind参数是用来控制数据是通过GET方法来是POST方法来提交的。True表示必须要通过POST来提交。

这样对于简单的Model的处理就很简单了,比如增加和修改可的View代码可以写为:

def _get_permission_data(o):
    return {‘id’:o.id, ‘name’:o.name, ‘description’:o.description}

def permissions_ajax_add_and_edit(request, object_id=None):
    m = EasyModelManipulator(Permission, object_id=object_id)
    f, obj = m.validate_and_save(request)
    if f:
        obj = _get_permission_data(obj)
    return Ajax.simple_ajax(f, obj)

其中_get_permission_data是用来将一个对象转为dict。其实django提供了serialization方法,但是无法指定字段,因此上面的方法足可以了。增加和修改共用一个方法,并且这里不用考虑第一次装入的问题,因此那是属于load方法要做的,如:

def permissions_ajax_load(request):
    result = []
    objs = Permission.objects.order_by(‘name’)
    result.append([_('Name')+':name:30%', _("Description")+':description:70%'])
    for o in objs:
        result.append(_get_permission_data(o))
    return Ajax.simple_ajax(result)

也不复杂。

修改不需要使用Manipulator:

def permissions_ajax_delete(request, object_id):
    user = Permission.objects.get(pk=int(object_id))
    if user:
        user.delete()
        return ajax.simple_ajax(True)
    else:
        return ajax.simple_ajax(False, _(‘Permission is not existed!’))

总的来说,目前上面的模式是针对于批量数据修改,而且还不是涉及复杂的Model关系,因为自动处理关系的确很麻烦,处理单个表要简单得多。

2006年09月20日

说是问题,也许算是浏览器之间的差异吧,如果不注意就会出错。

1. 对于数组和关联数组,后面结束时一定不要有’,'。但在Firefox下不会出问题。

2. 对于关联数组,key不能是class。其它的我没有试过。比如我们定义一个关联数组:

var a = {class:’hello’};

然后使用a.class来引用,在Firefox下没有问题,但在IE下就不行。需要定义为:

var a = {"class":’hello’};

但是如果你使用a.class来引用,仍然不行,需要使用a["class"]来引用。

不过在Javascript标准中,class虽然不是关键字,但它是保留字。还有许多其它的保留字。但是我想,是不是其它的保留字也如此呢?如何一试,出乎意料,我试了几个象byte, abstract, boolean都不存在问题。不知道为什么对于class这个单词这么特殊呢。搞不懂。有兴趣的你可以自已试一试,测试代码如下:

<html>
<head>
<title>Test</title>
</head>
<body>
<script type="text/javascript">
var a = {class:’aaaaa’};
document.write(a.class);
</script>
</body>
</html>

注意,你如果直接拷贝代码,需要把花括号改为半角的。

新版本发布了。今天在 Python.list 邮件列表中看到有人开始讨论字符串中的一些新功能。

在2.5版中新增加了一个叫partition的函数,它可以做什么呢?举一个小例子:

>>> ‘http://www.donews.net/limodou’.partition(‘://’)
(‘http’, ‘://’, ‘www.donews.net/limodou’)
>>> ‘file:/a.html’.partition(‘://’)
(‘file:/a.html’, ”, ”)

从第一个例子可以看出,它用来根据指定的分隔符将字符串进行分割,如果字符串包含指定的分隔符,则返回一个3元的tuple,第一个为分隔符左边的子串,第二个为分隔符本身,第三个为分隔符右边的子串。第二个例子说明,如果找不到指定的分隔符,则返回仍然是一个3元的tuple,第一个为整个字符串,第二和第三个为空串。

那么有人要问,它与split(sep, 1)有什么区别呢?首先split返回的可能不是固定长度的返回值,它返回的是一个list,如果找到,则返回一个2元list,如果没找到,则返回一个1元的list,如:

>>> ‘a.b.c’.split(‘,’, 1)
['a.b.c']
>>> ‘a.b.c’.split(‘.’, 1)
['a', 'b.c']

同时在找到的情况下,它并不返回分隔符。

在某些情况下partition(sep)和rpartition(sep)(从右向左匹配)与split(sep, 1)和rsplit(sep, 1)的功能是类似的。不过partition其实是为了替换find,index而产生的,并不是为了替换split而产生的。在许多情况下,我们需要先通过find来找到一个位置,再进行分割。而使用partition就方便得多。如:

>>> a = ‘http://www.donews.net’
>>> pos = a.find(‘://’)
>>> if pos > -1:
…     print a[:pos], a[pos+1:]
http www.donews.net

而使用partition:

>>> a = ‘http://www.donews.net’
>>> left, sep, right = a.partition(‘://’)
>>> print left, right
http www.donews.net

是不是简单一些呢。

同时在2.5版中,startswith和endswith有变化,它的第一个参数可以是一个tuple了。这样用在判断几种情况的时候非常方便。比如要判断文件名后缀,原来只支持一个值时,可能要先拆分,再判断,用不上endswith,如:

>>> a = ‘a.gif’
>>> import os.path
>>> ext = os.path.splitext(a)[1]
>>> if ext in ['.gif', '.png', '.bmp']:
…     print ‘found’

found

而现在可以:

>>> a = ‘a.gif’
>>> if a.endswith((‘.gif’, ‘png’, ‘.bmp’)):
…     print ‘found’

found

是不是简单多了。注意,上面的tuple我换成list就是不行,看来是强制的。

许多有趣的东西等着你我来发现。