Skip to content
lpy edited this page Apr 30, 2012 · 11 revisions

Qucik Start

在开始之前,假设您已经对下面的东西有了一定的了解:

当然,其实你仍然可以看下去(当有需要的时候再去了解相关的东西)。强烈建议你在使用过telnet bbs后再继续 看下去,或者,随时准备调试我们例子中的代码。

你可能会觉得很多定义似曾相识。没错,Chaofeng的灵感很多来自python web framework (尤其是Flasktornado), 很多概念也在模仿HTML中的设定。

继续Hello,world

这个例子相信你见过很多次了:

from chaofeng import Frame, Server
import chaofeng.ascii as c

class Hello(Frame):

    def initialize(self):
        self.write('Hello,World!')
        self.close()

    def clear(self):
        self.write('Don leave me alone ...\r\n')

if __name__ == '__main__' :
    s = Server(Hello)
    s.run()

这段代码究竟做了什么呢?

  1. 导入了Frame类和Server类,ascii模块。
  2. 重写了Hello类的initialize方法:输出Hello,World。(事实上,当一个Frame被打开的时候,就会调用initialize方法)
  3. 重写了clear方法。(没错,当Frame被关闭的时候就会调用clear方法)
  4. 实例化了一个Server类,而且Hello类作为参数传给Server。同时调用run使服务器运作。

事实上,你可以将chaofeng中的Frame类比成web中的一个页面:telnet进bbs,进入一个Frame,和Frame交互,在Frame中跳转, 关闭Frame结束telnet。

Frame

Frame最有用的方法就是write了。你可以用write来向客户端输出信息:

self.write('Hello,World!')

想要输出彩色的字?控制客户端的光标移动?由于telnet仅能提供十分有限的功能,很多有趣的交互可能都不能进行。但是,这两个都可以,阅读下面的ascii一节来了解更多。这里有一个例子,将会输出红色的hello,并且将光标移动到(1,3)的位置:

self.write(ascii.red + 'Hello' + move2(1,3) )

除了write,现在Frame支持read方法,而且——不会引起阻塞

def initialize(self):
    while self.read() != k_c_c : pass

注意read只会读取一个字符,更复杂的输入请参加下面的UI模块。

Frame.get

你需要重写Frame类的一些方法,来让这个Frame发挥作用:

  • initialize 当访问这个Frame的时候执行的动作。一般用于初始化。
  • clear 当离开这个Frame的时候执行的动作。一般用于清理。
  • get 当发生一个键盘按键的时候的动作,用于交互。

前面两个不用多说。那么get是干什么的呢?我们来看另外一个例子:

# color.py
from chaofeng import Frame, Server
import chaofeng.ascii as c

class ColorFrame(Frame):

    def initialize(self):
        self.write('Welcome to the colorful life !')

    def get(self,data):
        if data == c.k_up : self.write( c.bg_white + c.red + 'Up' )
        if data == c.k_down : self.write( c.bg_black + c.green + 'Down')
        if data == c.k_c_c : self.close()

    def clear(self):
        self.write(c.reset + 'Happy ending ...\r\n')

if __name__ == '__main__' :

    s = Server(ColorFrame)
    s.run()

这个例子跟hello,world差不多,我们来看一下他的get函数就好了:

  1. 判断data是否等于k_up 。 k_up是一个定义在ascii里面的常量,对应键盘的向上键。这里也就 是检查data是不是k_up,然后输出一个白色底的红色字Up。
  2. 判断data是否等于k_down。 没错,K_down对应向下键。
  3. 判断data是否等于k_c_c。 k_c_c对应的是Ctrl+C。

好了,现在应该大概知道get是干什么的了吧?一般地,当客户端按下一个按键,就会调用这个get函数,并且将 按键作为data传入。

ascii

ascii模块里面封装了很多的常量,主要用来控制颜色和用户交互。直接将这些常量通过write方法即可。

这些常量是不是字符串呢?是的,完完全全就是普普通通的字符串。你完全可以像普通的字符串那样来操纵他们:

self.write( ascii.movey_1 )
self.write( ascii.movey_1 * 10 )
self.write( 'Normal%sGreen%sNormal' % (ascii.green,ascii.reset)

常用的常量包括:

  • 字体颜色: black, red, green, yellow, blue, magenta, cyan, white
  • 背景颜色: bg_back, bg_red, ...
  • 字体样式: blod, underscore, inverted, italic
  • 光标移动: movex(n),movey(n),movey_1,movex_f,movex_d,save,restore,move2(x,y)
  • 键盘按键: k_up, k_down, k_right, k_left, k_c_c, k_c_h, k_backspace
  • 其他: reset, clear

事实上,可能取得的效果取决于终端机的理解。一般来说,模拟终端机都实现了大部分的效果。如果有兴趣, 可以阅读ANSI Escape Code相关文章获得进一步的了解,wiki上面也有相关的资料。此外,还需要了解telnet协议中的一些规定。

UI

许多常见的功能被包装成ui模块,你可以来使用他们。比如, TextInput和NMenu类。

下面是一个TextInput类的例子。TextInput会自动实现输入文字的功能(默认的telnet采用a char at time)。一般的,你 可以输入一些字符,可以按退格键来删除字符。

新版本的UI模块有很大变化。主要就是UI直接继承了Frame而不是原来的BaseUI。此外,创建一个UI通过Frame的sub方法。

可以调用read_until方法,因为read_until方法读入的字符,也会传递给father frame的get方法。此外,你可以直接调用UI的get方法来与UI交互。

你可以放心地使用自己的UI。在每一个Frame调用clear方法以前,都会先调用每个UI的clear方法。

from chaofeng.ui import TextInput

class Main(Frame):

    def initialize(self):
        username = self.sub(TextInput).read_until()
        self.write(username)
        self.close()

这段代码做了什么?

  1. 实例化一个TextInput类,调用他的read函数。
  2. 输出输入的内容。
  3. 关闭页面

一般的,ui模块总是:

  • 需要将相应的Frame作为的一个函数传入构造函数来实例化。在使用以前需要先创建。
  • 实现了一个read函数。

我要输入的是密码要显示的星号的

把上面的TextInput改成Password

Blocking?

得益于eventlet,chaofeng定义了一些函数,你可以在代码中进行一些I/O,虽然‘看起来’他们是阻塞的, 但是实际上并不会。注意,除了使用这些函数,你不应该再使用任何的可能会引起阻塞的函数。这些函数包括:

  • Frame.read
  • UI模块的read方法(下面将会说明)
  • chaofeng.Timeout 定时抛出一个异常
  • chaofeng.sleep 暂停若干秒

其中Timeout函数和sleep即是eventlet中的Timeout和sleep。下面是一些例子:

from chaofeng import Timeout,sleep
#如果下面的do_something在20s内还没有结束,将会抛出异常Exception
with Timeout(20,Exception) :
    do_something()
#等待3秒
sleep(3)

而UI模块中的read方法,将会调用自身的send,直到遇见了一个termitor,然后调用fetch方法取出。

username = TextInput(self).read()

continue

Chaofeng还处于初次实现的阶段,如果你有兴趣,欢迎补丁和更好的建议 :-)

Chaofeng还有许多的工作要做,包括实现其他常见的UI,进一步提高代码的效率等等。

最后, Just for fun。

Clone this wiki locally