2004年05月27日

1.0 Release 1版正式发布,欢迎大家使用。安装很简单,解开.zip压缩包到一个目录下即可。安装信息请参阅README.TXT。使用文档在doc目录下。运行NewEdit后按F1会在浏览器中打开帮助文档。大家在使用中有什么问题和建议可以和我联系。


希望这个软件可以帮助你更方便地编程。


下载链接:http://pyrecord.freezope.org/download/newedit_1.0r1.zip/down

2004年05月26日

Python 程序中,如果你需要,可以打开一个URL或一个邮件地址,使用 webbrowser 模块即可。


如:



import webbrowser
webbrowser.open(”http://limodou.donews.net/limodou”, 1)   #打开我的Blog页面
webbrowser.open(”mailto:chatme@263.net”)             #给我发信


这样会打开缺省的浏览器访问你的网址或打开缺省的邮件客户端给指定的邮件地址发信。很方便。 

2004年05月24日

NewEdit 现在应该已经可以很好的处理Unicode了,这其中也有一些波折。


wxPython 现在一个版本号上有两个发布包,一个支持Unicode,另一个不支持。支持Unicode的全部都是Unicode。原来NewEdit对Unicode的支持仅限于打开文件和保存文件时可以正确地进行码制转换(因为这时中文的处理已经正常了),以为这样就可以了。而且我的源码中没有使用一个汉字,主要想与国际接轨嘛,现在对NewEdit的开发已经全部使用自已进行了,因此竟然也没有发现什么问题。但当我打开ChangeLog.txt(用中文写的)时,想试一试其中的部分功能,这才发现,中文处理有问题,字符串的长度与位置全不对了。于是赶紧找原因。主要出错原因如下:


GetText() 这个函数返回编辑器的全部文本,但在Unicode版本下它返回的是Unicode,这样一个汉字的长度为1,而不是2。因此使用len()函数得到的长度是Unicode字符的个数,而不是真正的文本的长度。


GetCurrentPos() 取得当前插入点位置的函数(还包括其它一些与位置有关的函数)不是在GetText()中的偏移量,而是实际字节的偏移量,我发现一个汉字要占3字节。因此,如果插入点放在第一个汉字后面,它的值不是1,而是3。


GetLength() 返回的不是Unicode的字符个数,而是实际字节的个数。


这下问题大了,有些情况下,我是先取到整个编辑器的全部文本,再使用 Python 的字符处理得到相应的位置值,再对编辑器进行相应的处理。通过上面可以得知,我得到的全部的文本是Unicode,这样我处理时并不会按我的意图进行精确的定位,也就是说偏移量计算有问题,这样我根据这个偏移量去显示选择文本的位置和长度可能都不对。如果能够仍然象半角字符一样地去处理Unicode,那么就可以解决这个问题。还有我发现了GetTextRange()函数,可以返回指定区间的字符串,返回值为Unicode,但如果传入的区间参数不是正好在Unicode字符的边缘,返回为空串。还有一个函数是GetCharAt()可以得到指定位置的字节数,为一个整数,如果想把它用作字符,要使用chr()函数进行转换。那么GetCharAt()可以精确地处理任意的位置,哪怕是在一个Unicode中间。


那么为什么一个汉字要占三个字节呢?这是因为wxStyledTextCtrl保存文本使用的是UTF-8编码的,根据UTF-8编码规则一个汉字需要使用三个字节。这一点通过测试也可以得知。那么对UTF-8编码的处理可以按照半角字符的处理,比如:一个汉字转成UTF-8要占三个字节,那么我计算它的长度不再是1,而是3。很好,我得到了一个处理思路:对于要使用GetText()的函数,首先将其转换成UTF-8编码。这样,以前按ASC(半角)编码处理的查找代码都不用修改了。还有一点很重要的就是,在NewEdit的处理有些只支持英文,如对于单词匹配的处理。查找、替换是可以支持中文的。对于要显示、替换时,根据位置值,使用GetRangeText()或其它由编辑器提供的函数来得到Unicode的字符串,对编辑器进行修改。


简单地说:查找一个串、或进行匹配时使用UTF-8得到位置值。再根据位置值调用相应的函数得到Unicode字符串。


在Unicode环境下对中文进行处理时,的确要非常小心。我发现,wxStyledTextCtrl所提供的CallTip功能,当遇到中文Unicode时会退出,不得已,我自已实现了一个支持中文的CallTip。


上述对Unicode问题的解决都是测试出来的,不一定是最好的解决方法。如果有更好的,更合理的方法请告诉我,我好加以改进。

2004年05月20日

NewEdit 现在的基本功能已经大部分实现,在发布相对正式的版本之前,先发一个截屏。再等几个编辑功能实现后将作为初始版本发布。



我是在Windows下开发的,因此Linux下没有测试过。


运行环境:Python 2.3.3 + wxPython 2.4.2.4

2004年05月18日

因为 NewEdit 中要设置字体,而且想根据系统的不同取得系统的当前字体,这样就与系统保持一致。在 wxPython 中有一个函数可以得到系统的字体信息。



SystemSettings_GetFont(SystemFont index)


有几种索引值:



SYS_OEM_FIXED_FONT
SYS_ANSI_FIXED_FONT
SYS_ANSI_VAR_FONT
SYS_SYSTEM_FONT
SYS_DEVICE_DEFAULT_FONT
SYS_DEFAULT_GUI_FONT


那么SYS_DEFAULT_GUI_FONT是用户界面所用的字体,使用它就可以了。下面是如何得到字体名称和大小:



import wx
font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
name = font.GetFaceName()
size = font.GetPointSize()

2004年05月16日

在用过的一些编辑软件中也许你会注意到这么一个现象:当我选中一段文本时,剪切、拷贝菜单或工具条按钮会生效,如果没有选中文本,则它们会被禁止。如果粘贴板中有可供使用的内容时,粘贴菜单或工具条按钮生效,如果没有则被禁止。还可能有其它一些菜单或工具条按钮会随着文档的状态而发生改变。这个特性很有趣,表现出程序的一种智能。那么如何实现它呢?在 wxPython 中很简单。


首先要绑定事件:



 wx.EVT_UPDATE_UI(win, id, win.OnUpdateUI)


将需要实现这种功能的菜单ID或工具按钮ID与一个事件处理函数绑定。


然后在OnUpdateUI事件处理函数中根据文档或环境的变化设置菜单或工具条按钮的状态。


 下面是我在 NewEdit 中的一段处理代码:



def init(win):
    wx.EVT_UPDATE_UI(win, win.IDM_EDIT_CUT, win.OnUpdateUI)
    wx.EVT_UPDATE_UI(win, win.IDM_EDIT_COPY, win.OnUpdateUI)
    wx.EVT_UPDATE_UI(win, win.IDM_EDIT_PASTE, win.OnUpdateUI)
    wx.EVT_UPDATE_UI(win, win.IDM_EDIT_UNDO, win.OnUpdateUI)
    wx.EVT_UPDATE_UI(win, win.IDM_EDIT_REDO, win.OnUpdateUI)
    wx.EVT_UPDATE_UI(win, win.IDM_FILE_SAVE, win.OnUpdateUI)
Mixin.setPlugin(‘mainframe’, ‘init’, init)


def OnUpdateUI(win, event):
    eid = event.GetId()
    if eid in [win.IDM_EDIT_CUT, win.IDM_EDIT_COPY]:
        event.Enable(len(win.document.GetSelectedText()) > 0)
    elif eid == win.IDM_EDIT_PASTE:
        event.Enable(win.document.CanPaste())
    elif eid == win.IDM_EDIT_UNDO:
        event.Enable(win.document.CanUndo())
    elif eid == win.IDM_EDIT_REDO:
        event.Enable(win.document.CanRedo())
    elif eid == win.IDM_FILE_SAVE:
        event.Enable(win.document.GetModify())
    win.callplugin(‘onupdateui’, win, event)
Mixin.setMixin(‘mainframe’, ‘OnUpdateUI’, OnUpdateUI)


在init函数中进行绑定,在OnUpdateUI中进行处理。处理时,首先取得传入事件的id号,eid = event.GetId()。然后根据eid进行不同的处理。event是eid所对应的菜单或工具条按钮,因此根据不同的类型,可以设置禁止或有效(使用Enable函数);设置选中或未选中(使用Check函数)。


根据 wxPython 的文档介绍,wxUpdateUIEvent 是一个伪事件,它是 wxPython 专门用来让应用程序更新用户界面元素用的。那么当应用程序空闲时,wxPython 会自动对每个用户界面元素调用这个事件。因此,尽管两个不同的元素可能拥有相同的id,但 wxPython 仍然会每一个元素都调用一次事件处理。这样,如果功能相同的菜单和工具条按钮使用相同的id,在更新时两个都可以得到处理。这样写程序就很方便了。

2004年05月14日

wxStyledTextCtrl有一个FindText函数,它可以查找指定的串。函数原型为:



FindText(startpos, endpos, text, flags)


其中它有一个特性是可以反向查找,只要startpos小于endpos即可。于是我在 NewEdit 中使用这个函数实现Find Previous功能,但是发现一个问题。比如我要查找”Find”这个串,如果起始位置是在Find的中间,那么当前这个Find也会被找到,而不是找到上一个。也就是说,即使当前位置分隔开了Find,但wxStyledTextCtrl仍然认为当前串就是要找的串。因此如果想真正按我的想法找到前一个串的话,你需要将起始位置放在Find的前一个位置。

2004年05月13日

Python 2.3版中已经可以从zip文件中导入模块了。一个zip文件相当于一个目录,因此只要将zip文件加入到sys.path变量中即可以从其中导入模块。举 Python 文档中的例子如下:

amk@nyman:~/src/python$ unzip -l /tmp/example.zip
Archive:  /tmp/example.zip
  Length     Date   Time    Name
 ——–    —-   —-    —-
     8467  11-26-02 22:30   jwzthreading.py
 ——–                   ——-
     8467                   1 file
amk@nyman:~/src/python$ ./python
Python 2.3 (#1, Aug 1 2003, 19:54:32)
>>> import sys
>>> sys.path.insert(0, ‘/tmp/example.zip’)  # Add .zip file to front of path
>>> import jwzthreading
>>> jwzthreading.__file__’/tmp/example.zip/jwzthreading.py’
>>>

如果只想从子目录中导入,则可以使用/tmp/example.zip/lib方式,这样只会从zip文件中的lib子目录下导入模块。

很有趣的一个新特性!

NewEdit 项目中使用了wxNotebook来放置多文档,一个文档将占用一个page。在某些情况下需要手动切换当前page,那么它提供了SetSelection方法来实现这一功能。同时wxNotebook还提供了一个事件可以对页面的切换进行响应:EVT_NOTEBOOK_PAGE_CHANGED。于是我这样设计:统一在EVT_NOTEBOOK_PAGE_CHANGED的处理函数中处理文档的变化。需要程序切换页面时,只是调用了SetSelection方法。这样按我的想法,当我需要切换到某个页面时,我只需要先找到此页面的索引,然后调用SetSelection,这样自然会进入到页面改变的事件处理函数中,然后即可实现我需要的处理。想法不错,但实际情况并非如此,当切换的页面索引没有发生变化时,响应函数根本不会调用,也就是说事件是不可能生成的。因为这个问题,使我检查程序,检查了半天。这样我将程序改为:在需要程序切换页面时,先判断要改变的页面索引是否为当前面,如果是的话,则执行切换后的处理;如果不是,则执行SetSelection,触发页面切换事件,由响应函数来执行切换后的处理。

在作 NewEdit 时发现一个问题。我需要对打开的文件判断是Unix格式、DOS格式还是Mac格式,因此需要判断换回符是:’\n’、’\r\n’还是’\r’。但发现不管我打开Unix格式文件还是DOS格式文件,从 Python 中读到的都是’\n’,这是怎么回事?原来,Python 自动将三种格式的换行符全都自动转成了’\n’,因此处理时总是Unix格式。那么造成这个问题的原因就是我打开文件时没有指定’rb’(按二进制打开)模式,而是使用缺省方式。缺省方式下,Python 2.3 版本支持universal newline support,因此不管什么换行符都变成了’\n’。使用’rb’模式再处理就正确了。