====== 13、 Curses ======
本月我们讨论在Python中使用Curses。我的意思不是谈论用Python来说脏话,但是当你感觉需要的时候也是可以的。我们的重点是使用Curses库来输出一些非常炫的屏幕效果。
如果你的年纪足够大且有机会接触早期计算机的话,你应该记得在商用领域使用的计算机都是带有哑终端(只有键盘和显示器)的大型机。你可以用很多终端连接到同一台主机上。问题是终端相当愚蠢,没有窗口,没有彩色,大部分东西都没有,唯一有的东西就是24行80个字符(最多)的显示能力。在个人电脑开始流行的时候,操作系统还是古老的DOS和CPM,不过这已经是当时最好的了。
当程序员们为了得到漂亮的显示效果(当时的情况),特别是用于数据输入和显示的目的,他们使用绘图纸设计屏幕。图纸上的每个块代表显示器上一个字符的位置。对于在终端中运行的Python程序来说,我们要处理的还是40*80的屏幕。然而,这样的限制可以通过合理地规划和准备来解决。那么现在到离你最近的办公用品店买些绘图纸回来吧。
好了,现在我们开始创建第一个Curses程序。看下面的代码,稍后我会进行解释的: -->
#!/usr/bin/env python
# CursesExample1
#-------------------------------
# Curses Programming Sample 1
#-------------------------------
import curses
myscreen = curses.initscr()
myscreen.border(0)
myscreen.addstr(12, 25, "See Curses, See Curses Run!")
myscreen.refresh()
myscreen.getch()
curses.endwin()
非常简单吧,我们一行一行来看。首先我们先导入,现在这个你应该已经非常熟悉了。接下来我们创建一个Curses屏幕对象,进行初始化后将其命名为 myscreen.(myscreen=curses.initscr())。这就是我们进行创作的画布了。然后使用 myscreen.border(0)命令在花布周围画上边框。这虽然不是必须的,但有了之后却可以让我们的窗口看起来更漂亮。接下来再使用addstr 方法在画布上12行25字符处写一些自编字。想想Curses打印表达式的.addstr方法。最后,.refresh()可以让我们的修改生效。如果不刷新屏幕的话,我们做的更改是看不到的。然后我们等待用户按键(.getch)之后释放屏幕对象(.endwin)使终端可以回到一般状态。 curses.endwin()命令非常重要,如果没有执行的话,你的终端就会一团糟。所以在程序结束之前一定记得调用这个函数。
将程序保存成CursesExample1.py并在终端中执行。注意事项:每次使用边界时都会消耗掉一个可用的字符位置。还有行和字符位置都是从0开始的。这就意味着屏幕的第一行是0,最后一行是23。所以,相对0,0的最左上方位置是23,79。我们举一个小例子说明一下这个问题: -->
#!/usr/bin/env python
# CursesExample2
import curses
#==========================================================
# MAIN LOOP
#==========================================================
try:
myscreen = curses.initscr()
myscreen.clear()
myscreen.addstr(0,0,"0 1 2 3 4 5 6 7")
myscreen.addstr(1,0,"12345678901234567890123456789012345678901234567890123456789012345678901234567890")
myscreen.addstr(10,0,"10")
myscreen.addstr(20,0,"20")
myscreen.addstr(23,0, "23 - Press Any Key to Continue")
myscreen.refresh()
myscreen.getch()
finally:
curses.endwin()
除try/finally之外的语句都很简单。记住,curses.endwin()非常重要,你需要在每次程序结束的时候执行。这样,即使程序运行非常糟糕,endwin()例程都会被调用。还有很多方法可以实现这个,但是对我来说这个是最简单的。
现在我们开始建立漂亮的菜单。不知道你还记不记得,在第8部分的时候,我们创建过一个有菜单的食谱。我们打印一些东西的时候,终端就会向上滚动。这次我们还用这个想法来做一个菜单,这样你可以让你的食谱更加美观。
看下面是我们当时的编码吧: -->
===================================================
RECIPE DATABASE
===================================================
1 - Show All Recipes
2 - Search for a recipe
3 - Show a Recipe
4 - Delete a recipe
5 - Add a recipe
6 - Print a recipe
0 - Exit
===================================================
Enter a selection ->
这次我们用Curses来实现。先看下面的模板。你可能想将其保存下来并在以后的程序中使用。 -->
#!/usr/bin/env python
#-------------------------------
# Curses Programming Template
#-------------------------------
import curses
def InitScreen(Border):
if Border == 1:
myscreen.border(0)
#==========================================================
# MAIN LOOP
#==========================================================
myscreen = curses.initscr()
InitScreen(1)
try:
myscreen.refresh()
# Your Code Stuff Here...
myscreen.addstr(1,1, "Press Any Key to Continue")
myscreen.getch()
finally:
curses.endwin()
现在将你的模板保存为cursesmenu1.py以便我们可以将原来的模板保留下来。 继续编码之前,我们使用模块方法。
下面是我们要将要进行的部分的伪代码: -->
curses.initscreen
LogicLoop
ShowMainMenu # Show the main menu
MainInKey # This is our main input handling routine
While Key != 0:
If Key == 1:
ShowAllRecipesMenu # Show the All Recipes Menu
Inkey1 # Do the input routines for this
ShowMainMenu # Show the main menu
If Key == 2:
SearchForARecipeMenu # Show the Search for a Recipe Menu
InKey2 # Do the input routines for this option
ShowMainMenu # Show the main menu again
If Key == 3:
ShowARecipeMenu # Show the Show a recipe menu routine
InKey3 # Do the input routine for this routine
ShowMainMenu # Show the main menu again
… # And so on and so on
curses.endwin() # Restore the terminal
当然这些代码只是伪代码。但它却能让你了解整件事情的发展。由于这仅仅是个例子,我们就到此为止,不过你也可以自己进行完善。
现在开始主循环…… -->
# MAIN LOOP
try:
myscreen = curses.initscr()
LogicLoop()
finally:
curses.endwin()
这里还没有开始正式编码。我们现在有和模板中一样的try|finally块。初始化Curses屏幕之后调用叫做LogicLoop的例程。代码如下: -->
def LogicLoop():
DoMainMenu()
MainInKey()
同样,代码量也很少,但是这仅仅只是一个样本。这里我们要调用两个例程。DoMainMenu和MainInKey。DoMainMenu显示主菜单(真的会吗?),MainKey处理所有跟菜单有关的东西。下面是DoMainMenu的代码: -->
def DoMainMenu():
myscreen.erase()
myscreen.addstr(1,1, "========================================")
myscreen.addstr(2,1, " Recipe Database")
myscreen.addstr(3,1, "========================================")
myscreen.addstr(4,1, " 1 - Show All Recipes")
myscreen.addstr(5,1, " 2 - Search for a recipe")
myscreen.addstr(6,1, " 3 - Show a recipe")
myscreen.addstr(7,1, " 4 - Delete a recipe")
myscreen.addstr(8,1, " 5 - Add a recipe")
myscreen.addstr(9,1, " 6 - Print a recipe")
myscreen.addstr(10,1, " 0 - Exit")
myscreen.addstr(11,1, "========================================")
myscreen.addstr(12,1, " Enter a selection: ")
myscreen.refresh()
注意这段例程除了清空屏幕(myscrees.erase)然后在屏幕上显示我们想要的东西之外什么都没做。这里还没有牵扯到键盘动作处理。
那是下面MainInKey的事: -->
def MainInKey():
key = 'X'
while key != ord('0'):
key = myscreen.getch(12,22)
myscreen.addch(12,22,key)
if key == ord('1'):
ShowAllRecipesMenu()
DoMainMenu()
elif key == ord('2'):
SearchForARecipeMenu()
InKey2()
DoMainMenu()
elif key == ord('3'):
ShowARecipeMenu()
DoMainMenu()
elif key == ord('4'):
NotReady("'Delete A Recipe'")
DoMainMenu()
elif key == ord('5'):
NotReady("'Add A Recipe'")
DoMainMenu()
elif key == ord('6'):
NotReady("'Print A Recipe'")
DoMainMenu()
myscreen.refresh()
这段例程确实很简单。进入while循环直到用户的输入等于0。循环中检查输入是否等于各个值,如果等于的话就执行相应的例程,执行完成之后调用主菜单。现在你可以自己填写大部分例程了,但我们还要看看选项2,搜索菜单。菜单很简单,但是InKey2例程就有些复杂了。 -->
def SearchForARecipeMenu():
myscreen.addstr(4,1, "-------------------------------")
myscreen.addstr(5,1, " Search in")
myscreen.addstr(6,1, "-------------------------------")
myscreen.addstr(7,1, " 1 - Recipe Name")
myscreen.addstr(8,1, " 2 - Recipe Source")
myscreen.addstr(9,1, " 3 - Ingredients")
myscreen.addstr(10,1," 0 - Exit")
myscreen.addstr(11,1,"Enter Search Type -> ")
myscreen.refresh()
def InKey2():
key = 'X'
doloop = 1
while doloop == 1:
key = myscreen.getch(11,22)
myscreen.addch(11,22,key)
===== 参考 =====
* http://hougeubuntu.blogspot.com/search/label/Python