[Home]

Download
The Source (.ZIP)
The Source (.tgz)

The Python Language

1. Python in an Hour
2. Identifiers, Variables, and Numeric Types
3. Expressions and Strings
4. Advanced Data Types
5. Control Flow
6. Program Organization
7. Object-Oriented Python
8. Input and Output

Files, Data Storage, and Operating System Services

9. Processing Strings and Regular Expressions
10. Working with Files and Directories
11. Using Other Operating System Services
12. Storing Data and Objects
13. Accessing Date and Time
14. Using Databases

Networking and the Internet

15. Networking
16. Speaking Internet Protocols
17. Handling Internet Data
18. Parsing XML and Other Markup Languages

User Interfaces and Multimedia

19. Tinkering with Tkinter
20. Using Advanced Tkinter Widgets
21. Building User Interfaces with wxPython
22. Using Curses
23. Building Simple Command Interpreters
24. Playing Sound
25. Processing Images

Advanced Python Programming

26. Multithreading
27. Debugging, Profiling, and Optimization
28. Security and Encryption
29. Writing Extension Modules
30. Embedding the Python Interpreter
31. Number Crunching
32. Using NumPy
33. Parsing and Interpreting Python Code/Introspection

Deploying Python Applications

34. Creating Worldwide Applications
35. Customizing Import Behavior
36. Distributing Python Applications and Modules

Platform-Specific Support

37. Windows
38. UNIX-Compatible Modules

Appendices

A. Online Resources
B. Python Development Environments

Chapter 22. Using Curses


CurseWorld
DeathRay
Mask
Maze
Spiral


CurseWorld

CurseWorld.py
import curses
try:
    MainWindow=curses.initscr() # initialize curses
    MainWindow.addstr("Hello, damn it!")
    MainWindow.refresh()
    MainWindow.getch() # Read a keystroke
finally:
    curses.endwin() # de-initialize curses

DeathRay

DeathRay.py
import curses
import curses.textpad
import whrandom

class CursesButton:
    def __init__(self,Window,Y,X,Label,Hotkey=0):  
        self.Y=Y
        self.X=X
        self.Label=Label
        self.Width=len(Label)+2 # label, plus lines on side
        self.Underline=Underline
        # Draw the button:
        curses.textpad.rectangle(Window,Y,X,Y+2,X+self.Width)
        # Draw the button label:
        Window.addstr(Y+1,X+1,Label,curses.A_BOLD)
        # Make the hotkey stand out:
        Window.addstr(Y+1,X+Underline+1,Label[Underline]
            ,curses.A_REVERSE)
        Window.refresh()
    def KeyPressed(self,Char):
        if (Char>255): return 0 # skip control-characters
        if chr(Char).upper()==self.Label[self.Underline]:
            return 1
        else:
            return 0
    def MouseClicked(self,MouseEvent):
        (id,x,y,z,event)=MouseEvent
        if (self.Y <= y <= self.Y+2) and           (self.X <= x < self.X+self.Width):
            return 1
        else:
            return 0

def ShowDialog(Window):
    curses.mousemask(curses.BUTTON1_PRESSED)
    Window.addstr(5,0,"Really, REALLY fire death ray?")
    YesButton=CursesButton(Window,8,10,"Yes")
    NoButton=CursesButton(Window,8,20,"No")
    MaybeButton=CursesButton(Window,8,30,"Maybe")
    Buttons=[YesButton,NoButton,MaybeButton]
    Window.nodelay(1)
    Action=""
    while 1:
        Key=Window.getch()
        if (Key==-1):
            continue
        for Button in Buttons:
            if Button.KeyPressed(Key):
                Action=Button.Label
        # Handle mouse-events:
        if (Key==curses.KEY_MOUSE):
            MouseEvent=curses.getmouse()
            for Button in Buttons:
                if Button.MouseClicked(MouseEvent):
                    Action=Button.Label
        if Action!="": break
    # Handle the actions
    if (Action=="Yes"):
        FireDeathRay(Window)
    if (Action=="No"):   
        pass
    if (Action=="Maybe" and whrandom.random() > 0.5):
        FireDeathRay(Window)

def FireDeathRay(Window):
        Window.clear()
        # Kra-ppoowwww!  Frrrraapppp!!
        Window.bkgd("X")
        Window.nodelay(0)
        Window.getch()            
    
if __name__=="__main__":
    curses.wrapper(ShowDialog)

Mask

Mask.py
import curses
class Mask:
    def __init__(self,Window,Top,Bottom,Left,Right):
        self.Window=Window
        self.Top=Top
        self.Bottom=Bottom
        self.Left=Left
        self.Right=Right
        self.OldText=None
    def Cover(self,Character="X",Attributes=curses.A_DIM):
        # Cover the current screen contents. Store 
        # them in OldText[RowIndex][ColumnIndex] for later:
        self.OldText=[]
        for Row in range(self.Top,self.Bottom+1):
            self.OldText.append([])
            for Col in range(self.Left, self.Right+1):
                self.OldText[-1].append(                    self.Window.inch(Row,Col))
                self.Window.addstr(Row,Col,  
                    Character,Attributes)
    def Reveal(self):
        if (self.OldText==None): return
        for Row in range(self.Top,self.Bottom+1):
            CurrentLine=self.OldText[Row-self.Top]        
            for Col in range(self.Left, self.Right+1):
                CurrentCol=(Col-self.Left)
                Character=chr(CurrentLine[CurrentCol] & 0xFF)
                Attributes=CurrentLine[CurrentCol] & (~0xFF)
                self.Window.addstr(Row,Col,
                    Character,Attributes)

def Main(MainWindow):
    MainWindow.addstr(10,10,"Yes it is!")
    MainWindow.addstr(11,10,"No it isn't!",curses.A_BOLD)
    MainWindow.addstr(12,10,"Yes it is!",curses.A_UNDERLINE)
    MainWindow.addstr(13,10,"No it isn't!",curses.A_STANDOUT)
    MainWindow.addstr(14,10,"YES IT IS!",curses.A_BOLD)
    MyMask=Mask(MainWindow,10,20,10,40)
    MainWindow.refresh()
    MainWindow.getch()      
    MyMask.Cover()
    MainWindow.refresh()
    MainWindow.getch()      
    MyMask.Reveal()
    MainWindow.refresh()
    MainWindow.getch()      

if (__name__=="__main__"):
    curses.wrapper(Main)

Maze

Maze.py
import curses
import curses.ascii
import whrandom
# Possible contents of maze-squares:
MAZE_WALL="X"
MAZE_ENTRANCE="*"
MAZE_HALLWAY="."
# Attributes for displaying maze squares:
MAZE_ATTRIBUTE={MAZE_WALL:curses.A_NORMAL,
                MAZE_ENTRANCE:curses.A_BOLD,
                MAZE_HALLWAY:curses.A_DIM,}
# Simple class representing a compass direction:
class Direction:
    def __init__(self,Name,XDelta,YDelta):
        self.Name=Name
        self.XDelta=XDelta
        self.YDelta=YDelta
        self.Marker=Name[0]
    def SetOpposite(self,Dir):
        self.Opposite=Dir
        Dir.Opposite=self
NORTH=Direction("North",0,-1)
SOUTH=Direction("South",0,1)
EAST=Direction("East",1,0)
WEST=Direction("West",-1,0)
NORTH.SetOpposite(SOUTH)
EAST.SetOpposite(WEST)
VALID_DIRECTIONS=[NORTH,SOUTH,EAST,WEST]
# Maze creation uses direction "markers" to indicate how we got
# to a square, so that we can (later) backtrack:
MARKED_DIRECTIONS={NORTH.Marker:NORTH,SOUTH.Marker:SOUTH,
            EAST.Marker:EAST,WEST.Marker:WEST}
# Map keystrokes to compass directions:
KEY_DIRECTIONS={curses.KEY_UP:NORTH,curses.KEY_DOWN:SOUTH,
                curses.KEY_LEFT:WEST,curses.KEY_RIGHT:EAST}
class Maze:
    def __init__(self,Size=11):
        # Maze size must be an odd number:
        if (Size%2==0): 
            Size+=1
        self.Size=Size
        self.Pad=curses.newpad(self.Size+1,self.Size+1)
        self.FillWithWalls()
    def FillWithWalls(self):
        for Y in range(0,self.Size):
             self.Pad.addstr(Y,0,MAZE_WALL*self.Size,MAZE_ATTRIBUTE[MAZE_WALL])
    def Set(self,X,Y,Char):
        self.Pad.addstr(Y,X,Char,MAZE_ATTRIBUTE.get(Char,curses.A_NORMAL))
    def Get(self,X,Y):
        return self.Pad.instr(Y,X,1)
    def BuildRandomMaze(self):
        self.FillWithWalls()
        CurrentX=1
        CurrentY=1
        self.Set(CurrentX,CurrentY,MAZE_ENTRANCE)
        while (1):
            Direction=self.GetValidDirection(CurrentX,CurrentY)
            if (Direction!=None):
                # Take one step forward
                self.Set(CurrentX+Direction.XDelta,
                    CurrentY+Direction.YDelta,MAZE_HALLWAY)
                CurrentX+=Direction.XDelta*2
                CurrentY+=Direction.YDelta*2
                self.Set(CurrentX,CurrentY,Direction.Marker)
            else:
                # Backtrack one step
                BackDirectionMarker=self.Get(CurrentX,CurrentY)
                BackDirection=MARKED_DIRECTIONS[BackDirectionMarker].Opposite
                CurrentX+=BackDirection.XDelta*2
                CurrentY+=BackDirection.YDelta*2
                # If we backtracked to the entrance, the maze is done!
                if self.Get(CurrentX,CurrentY)==MAZE_ENTRANCE:
                    break
        # Fix up the maze:
        for X in range(0,self.Size):
            for Y in range(0,self.Size):
                if self.Get(X,Y) not in [MAZE_HALLWAY,MAZE_WALL, MAZE_ENTRANCE]:
                    self.Set(X,Y,MAZE_HALLWAY)
    def GetValidDirection(self,X,Y):
        DirectionIndex=whrandom.randint(0,len(VALID_DIRECTIONS)-1)
        FirstIndex=DirectionIndex
        while (1):
            Direction=VALID_DIRECTIONS[DirectionIndex]
            NextSquare=(X+Direction.XDelta*2,Y+Direction.YDelta*2)
            if ((0 < NextSquare[0] < self.Size) and
               (0 < NextSquare[1] < self.Size) and
               self.Get(NextSquare[0],NextSquare[1])==MAZE_WALL):
                   return Direction
            DirectionIndex+=1
            if (DirectionIndex>=len(VALID_DIRECTIONS)):
                DirectionIndex=0
            if (DirectionIndex==FirstIndex):
                return None
    def ShowSelf(self,ScreenLeft,ScreenTop,PlayerX,PlayerY,Radius):        
        Top=PlayerY-Radius
        Bottom=PlayerY+Radius
        Left=PlayerX-Radius
        Right=PlayerX+Radius
        ScreenRight=ScreenLeft+Radius*2+1
        ScreenBottom=ScreenTop+Radius*2+1
        if (Top<0):
            ScreenTop -= Top
            Top=0
        if (Left<0):
            ScreenLeft -= Left
            Left=0
        if (Right>self.Size-1):
            ScreenRight-=(self.Size-1-Right)
            Right=self.Size-1      
        if (Bottom>self.Size-1):
            ScreenBottom-=(self.Size-1-Bottom)
            Bottom=self.Size-1      
        self.Pad.refresh(Top,Left,ScreenTop,ScreenLeft,ScreenBottom,ScreenRight)

def Main(Window):
    # Set up colors:
    curses.init_pair(1,curses.COLOR_GREEN,curses.COLOR_BLACK)
    curses.init_pair(2,curses.COLOR_BLUE,curses.COLOR_BLACK)
    curses.init_pair(3,curses.COLOR_RED,curses.COLOR_BLACK)
    MAZE_ATTRIBUTE[MAZE_HALLWAY] |= curses.color_pair(1)
    MAZE_ATTRIBUTE[MAZE_ENTRANCE] |= curses.color_pair(2)
    MAZE_ATTRIBUTE[MAZE_WALL] |= curses.color_pair(3)
    curses.curs_set(0) # invisible cursor
    MyMaze=Maze(20)
    MyMaze.BuildRandomMaze()
    PlayerX=19
    PlayerY=19
    LightRadius=3
    MazeWindow=curses.newwin(10,10,10+LightRadius*2+1,10+LightRadius*2+1)
    while 1:
        MazeWindow.erase()
        MyMaze.ShowSelf(10,10,PlayerX,PlayerY,LightRadius)
        Window.addch(10+LightRadius,10+LightRadius,"@",
            curses.color_pair(2) & curses.A_STANDOUT)
        Window.refresh()
        Key=Window.getch()
        if (Key==ord('q') or Key==curses.ascii.ESC):
            break
        Direction=KEY_DIRECTIONS.get(Key,None)
        if (Direction):
            TargetSquare=MyMaze.Get(PlayerX+Direction.XDelta,
                PlayerY+Direction.YDelta)
            if TargetSquare==MAZE_ENTRANCE:
                MazeFinished(Window)
                break
            if TargetSquare==MAZE_HALLWAY:
                PlayerX += Direction.XDelta
                PlayerY += Direction.YDelta

def MazeFinished(Window):
    Window.clear()
    Window.addstr(5,5,"CONGRATULATION!",curses.color_pair(2))
    Window.addstr(6,5,"A WINNER IS YOU!",curses.color_pair(3))
    Window.getch()
    pass

if (__name__=="__main__"):
    curses.wrapper(Main)
    print "Bye!"

Spiral

Spiral.py
import curses
import math

def DrawSpiral(Window,CenterY,CenterX,Height,Width):
    ScalingFactor=1.0
    Angle=0
    HalfHeight = float(Height)/2
    HalfWidth = float(Width)/2
    while (ScalingFactor>0):
        Y = CenterY +  
             (HalfHeight*math.sin(Angle)*ScalingFactor)
        X = CenterX + (HalfWidth*math.cos(Angle)*ScalingFactor)
        Window.move(int(Y),int(X))
        Window.addstr("*")
        Angle+=0.05 
        ScalingFactor=ScalingFactor - 0.001
        Window.refresh()

def Main(Window):
    (Height,Width)=Window.getmaxyx()
    Height-=1 # Don't make the spiral too big
    Width-=1
    CenterY=Height/2
    CenterX=Width/2
    DrawSpiral(Window,CenterY,CenterX,Height,Width)
    Window.getch()

if __name__=="__main__":
    curses.wrapper(Main)

 

 

 

 

Email the webmaster.
Copyright © 2001-2002 Solus Software. All rights reserved.