用户工具

站点工具


fullcircle:13-cureses

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)

参考

fullcircle/13-cureses.txt · 最后更改: 2011/02/13 12:20 (外部编辑)