![]()
Chapter 22. Using Curses
CurseWorldCurseWorld.pyimport 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
DeathRayDeathRay.pyimport 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)
MaskMask.pyimport 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)
MazeMaze.pyimport 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!"
SpiralSpiral.pyimport 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.
|