A class for menu based terminal applications.

Gribouillis 0 Tallied Votes 3K Views Share

Many threads in daniweb's python forum deal with menu based programs which run in a terminal. This snippet defines a handy class, MenuCrawler, to help writing such programs. Subclassing from this base classes and adding methods corresponding to a given set of menus allows one to build the menu-based application. See the example code included in the snippets for details.

# module menucrawler.py
# (C) 2008 Gribouillis at www.daniweb.com
"""This module implements a base class MenuCrawler for menu based
applications in a terminal.
Note: uses module cbprint from the snippet
http://www.daniweb.com/code/snippet1031.html
"""
import re
from cbprint import Print
menu_pattern = re.compile("^\s*MENU\s+(\w+)")

class MenuCrawler(object):
  """A base class for menu based applications in a terminal.
A MenuCrawler is initialized with a string containing several menus
separated by lines of the form "MENU name".
It can then be started by it's "run" method and it then displays
a menu and prompt the user for an answer.
Answers are handled by methods having the same names as the menus.
These methods can be typically be implemented by subclassing MenuCrawler.
See the example use in this module's source code.
"""

  def __init__(self, menus):
    self.menu = None
    self.menus = {}
    if menus is not None:
      self.parse_menus(menus)

  def parse_menus(self, menus):
    lines = menus.split("\n")
    name = ""
    for line in lines:
      match = menu_pattern.match(line)
      if match is None:
	if name:
	  self.menus[name].append(line)
      else:
	if name:
	  self.menus[name] = "\n".join(self.menus[name]).strip()
        name = match.group(1)
	self.menus[name] = []
    if name:
      self.menus[name] = "\n".join(self.menus[name]).strip()

  def run(self, menu_name, prompt = "[choice] "):
    self.menu = menu_name
    while self.menu is not None:
      Print(self.menus[self.menu])
      choice = raw_input(prompt)
      if hasattr(self, self.menu):
	getattr(self, self.menu)(choice)


#### Example use ###
if __name__ == "__main__":
  my_menus = """
MENU start
Chose one of these
  [1] Fight a monster
  [2] Sleep under a tree
  [3] Eat the apple

MENU fight
Chose a monster
  [1] Unicorn
  [2] Dragon
  [3] Ork

MENU sleep
You slept for an hour, but you were awaken by a strange noise overthere.
What will you do
  [1] Run away
  [2] Go see what caused the noise
  [3] Resume your sleep

MENU apple
The apple is poisoined. You're dying.
  [1] Call the charming prince
  [2] Eat the other apple
  [3] Pronounce the magic words

MENU magic
What is the magic formula ?

MENU quit
Do you want to play again ?
"""
  class Game(MenuCrawler):
    def __init__(self):
      MenuCrawler.__init__(self, my_menus)
      print self.menus
    def start(self, choice):
      if choice == "1":
	self.menu = "fight"
      elif choice == "2":
	self.menu = "sleep"
      elif choice == "3":
	self.menu = "apple"
      elif choice == "quit":
	self.menu = "quit"
    def do_quit(self):
      Print("Thank you for playing")
      raise SystemExit
    def fight(self, choice):
      if choice in ("1", "2", "3"):
	Print("Fights are not yet implemented")
	self.menu = "quit"
      elif choice == "quit":
	self.menu = "quit"
    def quit(self, choice):
      if choice in ("y", "yes", "ok"):
	self.menu = "start"
      else:
	self.do_quit()
    def sleep(self, choice):
      Print("This feature is not yet implemented")
      self.menu = "quit"
    def apple(self, choice):
      if choice == "3":
	self.menu = "magic"
      elif choice == "quit":
	self.menu = "quit"
      else:
	Print("This feature is not yet implemented")
    def magic(self, choice):
      if choice == "abracadabra":
	Print("The magic words save your life!")
	self.menu = "start"
      elif choice == "quit":
	self.menu = "quit"
      else:
	Print("These are not the magic words ! You're dead.")
	self.menu = "quit"
  G = Game()
  G.run("start")