MenuSystem-1.0/ 0000755 0001750 0000144 00000000000 10424027776 012647 5 ustar daniel users MenuSystem-1.0/README 0000644 0001750 0000144 00000015050 10424022526 013514 0 ustar daniel users MenuSystem
Author Daniel Mikusa dan at trz dot cc
Date 2006-04-26
License LGPL
WHAT YOU GET
In case you are wondering "What does this package do?", wonder no more. It creates
simple menuing systems. Here's an example of what a menu might look like.
Menu Name
1.) Do Something
2.) Do Something Else
3.) Do Another Something Else
Enter your choice .>
As you can see basic text menus aren't the prettiest things in the world, but they are
easy and effective. Here is what is included with this module.
Menu & Choice classes
MenuGenie Interface
XMLMenuGenie Implementation
The Menu & Choice classes represent menu and choice objects in our systems. Each menu
contains a name, one or more choices, and a prompt. Each choice contains a selector,
name, value, and a function to execute when the menu has been chosen.
The MenuGenie interface is a way that you can create you own menu storage classes. For
instance you may want to save menus a MySQL database (or any other database), XML, or
anything else might like.
Done for you already is XML storage. The XMLMenuGenie allows you to save and load menu
systems from XML. This will alow you to keep you display code seperate from you controller
code.
PURE PYTHON
Menu systems can simply be built by writing a Python script to create the object that
you need. Here's an example.
menu.py
# Selector Functions
def print_ok(val):
print 'OK: %s' % val
def print_bad(val):
print 'BAD: %s' % val
def done(val):
return False
def submenu_handler(val):
print 'Going to submenu'
# Create Sub Menu
lst = []
lst.append(MenuSystem.Choice(selector=1, value=1, handler=print_ok, description='Sub Menu Do Noting Some'))
lst.append(MenuSystem.Choice(selector=2, value=2, handler=print_ok, description='Sub Menu Do Noting More'))
lst.append(MenuSystem.Choice(selector=3, value=3, handler=print_bad, description='Sub Menu Do Noting Alot'))
lst.append(MenuSystem.Choice(selector=4, value=4, handler=done, description='Return to Main Menu'))
sub = MenuSystem.Menu(title='Sub Menu', choice_list=lst, prompt='Select Choice.> ')
# Create Main Menu
lst = []
lst.append(MenuSystem.Choice(selector=1, value=1, handler=print_ok, description='Do Noting Some'))
lst.append(MenuSystem.Choice(selector=2, value=2, handler=None, description='Do Noting More'))
lst.append(MenuSystem.Choice(selector=3, value=3, handler=print_bad, description='Do Noting Alot'))
lst.append(MenuSystem.Choice(selector=4, value=4, handler=None, description='Sub Menu', subMenu=sub))
lst.append(MenuSystem.Choice(selector=5, value=5, handler=done, description='Exit'))
# Creat Menu & Begin Execution
head = MenuSystem.Menu(title='Main Menu', choice_list=lst, prompt='Select Choice.> ')
After you create your menu system all you need to do is call the waitForInput member function
of you top level menu object, and it will handle the rest.
head.waitForInput()
XML
You can create you menu system from an XML file. Each menu and choice are represented by XML
tags. Here's an example.
file.xml
Design your menu system's layout in the XML file, and then write the choice object's handler
functions.
handlers.py
def print_ok(val):
print 'OK: %s' % val
def print_bad(val):
print 'BAD: %s' % val
def done(val):
return False
def submenu_handler(val):
print 'Going to submenu'
The create an XMLMenuGenie object and call it's load function. The load function will
return the top level menu object representing your menu system.
xml = MenuSystem.XMLMenuGenie('file.xml', 'choice_handlers')
head = xml.load()
Then just like the Pure Python method, call the top level menu object's waitForInput
member function to start the menu system.
head.waitForInput()
The second part of the XMLMenuGenie class is that you can save menu objects to XML files. So
if you create a sweet menu in Python and want to save it to XML, just create you XMLMenuGenie
object, and call it's save function.
xml = MenuSystem.XMLMenuGenie('file.xml', 'choice_handlers')
xml.save(head)
MORE INFORMATION
If you need more information I suggest you take a look at the code. There are loads of
comments and, of course, there's the code.
Conceptually the module isn't very tough. The Menu class contains a list of choices.
Each choice is passed a handler. The handler is called when a choice is selected. To
give your menu system depth, you can give a choice object a submenus. The submenu is
just like any other menu and contains it's own choices which can also be submenus. This
can continue on to any depth that you like.
The MenuGenie class is a generic interface that can be sub-classed to create
any number of XXXMenuGenie sub classes. As long as you implement the MenuGenie interface
correctly, all of these sub classes will be interchangable. The XMLMenuGenie sub class
has already been created, if you are trying to make your own sub class I suggest you
use that as an example.
Questions and comments are always welcome, shoot me an email.
LICENSE
Copyright (C) 2006 Daniel Mikusa
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
MenuSystem-1.0/PKG-INFO 0000644 0001750 0000144 00000001102 10424027776 013736 0 ustar daniel users Metadata-Version: 1.0
Name: MenuSystem
Version: 1.0
Summary: Build Text Based Menus Easily
Home-page: http://www.mikusa.com/menu_system/
Author: Daniel Mikusa
Author-email: dan@trz.cc
License: LGPL
Description: UNKNOWN
Platform: OS Independent
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Topic :: Utilities
MenuSystem-1.0/example/ 0000755 0001750 0000144 00000000000 10424027776 014302 5 ustar daniel users MenuSystem-1.0/example/handlers.py 0000644 0001750 0000144 00000001354 10415251166 016447 0 ustar daniel users """Menu Handlers
Author: Daniel Mikusa
Copyright: April 5, 2006
Define all of you choice handler functions in a seperate module
You must define all of you choice handler functions in a seperate file. This
is a good idea because it seperates your business logic code from your
presentation code and because it is technically required for the Menu System
to be able to find your functions.
For Function 'X' in module 'Y'
Choice(selector=1, value=1, handler=X.Y, description='Do Something!')
"""
# Define Some Handler Functions
def print_ok(val):
print 'OK: %s' % val
def print_bad(val):
print 'BAD: %s' % val
def done(val):
return False
def submenu_handler(val):
print 'Going to submenu'
MenuSystem-1.0/example/save.xml 0000644 0001750 0000144 00000001614 10424023603 015745 0 ustar daniel users
MenuSystem-1.0/example/example1.py 0000644 0001750 0000144 00000003613 10424023577 016366 0 ustar daniel users from menusystem import MenuSystem
from handlers import *
"""Project 1
Author: Daniel Mikusa
Copyright: April 4, 2006
Sample project to demonstrate the use of the MenuSystem set of classes.
In effect this project will create a menu with a few choices, some sub-menus,
etc...
When run the user can then navigate through the system.
"""
"""Create A Menu and Sub Menu by hand"""
# Create Sub Menu
lst = []
lst.append(MenuSystem.Choice(selector=1, value=1, handler=print_ok, description='Sub Menu Do Noting Some'))
lst.append(MenuSystem.Choice(selector=2, value=2, handler=print_ok, description='Sub Menu Do Noting More'))
lst.append(MenuSystem.Choice(selector=3, value=3, handler=print_bad, description='Sub Menu Do Noting Alot'))
lst.append(MenuSystem.Choice(selector=4, value=4, handler=done, description='Return to Main Menu'))
sub = MenuSystem.Menu(title='Sub Menu', choice_list=lst, prompt='Select Choice.> ')
# Create Some Choices
lst = []
lst.append(MenuSystem.Choice(selector=1, value=1, handler=print_ok, description='Do Noting Some'))
lst.append(MenuSystem.Choice(selector=2, value=2, handler=None, description='Do Noting More'))
lst.append(MenuSystem.Choice(selector=3, value=3, handler=print_bad, description='Do Noting Alot'))
lst.append(MenuSystem.Choice(selector=4, value=4, handler=None, description='Sub Menu', subMenu=sub))
lst.append(MenuSystem.Choice(selector=5, value=5, handler=done, description='Exit'))
# Creat Menu & Begin Execution
head = MenuSystem.Menu(title='Main Menu', choice_list=lst, prompt='Select Choice.> ')
head.waitForInput()
"""Save Menu To XML"""
# Save Menu
xml = MenuSystem.XMLMenuGenie('save.xml', 'handlers')
xml.save(head)
head2 = xml.load()
head2.waitForInput()
"""Load Menu from XML"""
# Load Menu
#loader = MenuSystem.XMLMenuGenie('save.xml', 'choice_handlers')
#head2 = loader.load()
#head2.waitForInput()
MenuSystem-1.0/menusystem/ 0000755 0001750 0000144 00000000000 10424027776 015060 5 ustar daniel users MenuSystem-1.0/menusystem/MenuSystem.py 0000644 0001750 0000144 00000035417 10424022553 017541 0 ustar daniel users """
Copyright (C) 2006 Daniel Mikusa
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
"""
import sys, xml.dom.minidom
"""Menu System
Author: Daniel Mikusa
Copyright: April 4, 2006
A Menu System is a group of Menu and Choice Object arranged to provide a text
based interface for an end user. These classes are aimed at making
development of Menu based systems easy and fast.
A Menu System is orgainzed into a heirarchy. There is a top level Menu object,
which has a set of choices. Any / All / None of this Menu's Choice objects can
contain sub-menus. The Menu System heirarchy is extended downward by creating
Menu objects with Choice objects that have sub-menus. As is natural, each
Choice object can only have one sub-menu, but each Menu can have many Choice
objects.
"""
class Menu:
"""Represents one level of a menu system
A Menu System consists of one or more Menus. Each Menu consists of a
title, choices, and a prompt. A Menu is required to have a title, a prompt
and at least one choice. There is no technical limit to the number of
choices per menu, but asteticly and organizationally more than 10 will
likely cause problems and should be divided into sub-menus.
A Menu object is capable of displaying itself and of retreiving a choice
from the user. Both function only needs to be called on the top
level menu object. The calls will trickle down to all of the menu object
beneath it in the heirarchy as needed.
"""
def __init__(self, title='', choice_list=[], prompt=''):
"""Setup a new menu object
Allows a new Menu object to be easily created by passing the required
values into a constructor. All parameters are optional.
title -- Menu object's title
prompt -- Menu object's prompt
choice_list -- List of Choice object for Menu
"""
self.title = title
self.choices = choice_list
self.prompt = prompt
def __getitem__(self, key):
"""Overloads the [] operator for Menu object to get Choices
Allows us to select a choice from a Menu object using the [] operator.
print s['3']. This is mostly a convenience, mostly.
Given a string 'x', returns the Choice object that has the selector 'x'.
"""
if isinstance(key, str) and key in self.choices:
return self.choices[self.choices.index(key)]
def __repr__(self):
"""Prints the Menu to Standard Out
Prints the current Menu and triggers a recursive call to all sub-menus
that are part of this Menu heirarchy.
The format for display is as follows:
TITLE
choice 1
choice ...
choice n
PROMPT
A Choice object will handle its own formatting, all that is required is
to call the Choice object's display function.
"""
if not self.choices:
return "Please create some choices for this menu"
tmp = "\n%s\n\n" % self.title
for choice in self.choices:
tmp += "\t%s\n" % str(choice)
tmp += "\n%s" % self.prompt
return tmp
def waitForInput(self):
"""Main event loop, progresses indefinitly
Starts the main event loop for the menu system, which consists of
getting input from standard input, validating it, and calling the proper
handler function. Once the handler function returns we continue
this process indefinitly.
This process will loop forever, unless you create a return level / exit
option for the user. To return a level, or exit if it is the top level
just return False from the handler function.
"""
loop = True
while loop != False:
print self,
c = sys.stdin.readline().strip()
if c in self.choices:
if self[c].handler:
loop = self[c].handler(self[c].value)
if self[c].subMenu:
self[c].subMenu.waitForInput()
class Choice:
"""
Represents one option of a Menu
Each Menu requires one or more Choices. A Choice consists of a selector,
a description, a value, and a sub-menu. The selector, description, and
value are required, while the sub-menu is optional.
The presence of a sub-menu will alter the way the Choice is displayed to
indicate it is a sub-menu to the end user.
A Choice object is capable of displaying itself and provides instructions
on what to do if the Choice object is selected.
"""
def __init__(self, selector=1, description='',
value=None, subMenu=None, handler=None):
"""Setup a new Choice object
Allows a new Choice object to be easily created by specifying the
required values to the constructor. All parameters are optional.
Here are the parameters:
selector -- value that a user will enter to select a Choice
description -- value that explains job this Choice will accomplish.
value -- value of the selected Choice.
subMenu -- the Menu object that will be triggered if this Choice
is selected.
handler -- the function that will be executed if this Choice
is selected.
The handler function will be executed even if the Choice object contains
a sub-menu. The handler function will be executed, and then control
will be switched to the handler function.
The handler function will be passed a single parameter containing the
value of the Choice object that was selected. This can be used to find
out what choice was selected by the end user. This value will always
be a string.
The handler function should never return 'False' unless the desired
effect is to either go up one level in the menu heirarchy or exit if it
is at the top level of the menu heirarchy. If the handler returns
anything else it is ignored. Returning True or None are typically best
and will keep you at the same menu level, Returning False will return
one level.
"""
self.selector = selector
self.description = description
self.value = str(value)
self.subMenu = subMenu
self.handler = handler
def __repr__(self):
"""Prints the Choice to Standard Out
The format for display is as follows:
SELECTOR.) DESCRIPTION
If the Choice has a sub-menu it will be displayed as follows:
SELECTOR.) DESCRIPTION **
DO NOT INCLUDE END OF LINE CHARACTERS!
"""
if not self.selector or not self.description:
return "Menu needs to be defined before it can be printed."
if not self.subMenu:
return "%s.) %s" % (self.selector, self.description)
else:
return "%s.) %s **" % (self.selector, self.description)
def __eq__(self, ch):
"""Overloads the == operator for Choice object
This allows us to determine if two Choice objects are equal by saying
choiceA == choiceB. This is mostly a convenience, mostly.
"""
if isinstance(ch, str):
return ch == str(self.selector)
class MenuGenie:
"""Generic Interface for loading and saving Menu Systems.
Defines an Interface for loading and saving Menu Systems. This is just
an Interface, it doesn't define how the Menu System is stored.
To implement this interface you need to define two functions: load and
save. As you would expect, load will create a menu from permanent storage
and return the top level Menu object. Save takes a Menu object as
a parameter and will write that Menu object and all sub-menus to permanent
storage.
"""
def load(self):
"""Load a Menu object from permanent storage
Load and create a Menu object from permanent storage. This is simply a
place holder, and needs to be overloaded in a derived class. Returns
a menu object that is loaded.
"""
pass
def save(self, menu):
"""Writes a Menu object to permanent storage
Write an existing Menu object and all sub-mens to permenant storage.
This is simply a place holder, and needs to be overloaded in a derived
class. Parameter menu is the menu object to write.
"""
pass
class XMLMenuGenie(MenuGenie):
"""Loads and Saves Menu Systems to XML
Implements the MenuGenie Interface allowing Menu Systems to be saved as
XML files. The format of the XML file is as follows:
- represents a menu object (valid anywhere). Must contain at
least one choice object. Title and prompt attributes are
required.
attribute list
title - menu title
prompt - menu prompt
- represents a choice object (only valid in menu tag). A
choice object may contain one and only one menu object.
If the choice object contains a menu object, then the choice
object becomes a sub-menu. All attributes are valid for
regular choices and sub-menus. Selector, description, and
handler attributes are required. If value is not specified
then value will be set to equal selector.
attribute list
selector - used to determine if an object is selected
description - text to describe object to end user
value - value representation passed to handler function
handler - name of python function to call when selected
The use of other tags will trigger an error. The use of unspecified
attributes will not result in an error, they will be ignored.
"""
def __init__(self, loc, module_name):
"""Initalize the XMLGenie Object
Parameter loc sets the location of the XML file that will be read or
written to. Parameter module_name should be the module that defines
all of the functions that are used to handle menu choices.
"""
self.loc = loc
self.module = __import__(module_name)
def load(self):
"""Load a Menu System from XML File
Loads a Menu System from an XML file in the above specified format. The
parameter location is used to specify where to find the XML. It can be
a file object, the path to a local file, a URL, or the actual XML as
a Python String.
After the XML is processed, a Menu object is returned which contains
the top level menu of the Menu System.
"""
fp = self._open('r')
doc = xml.dom.minidom.parse(fp)
fp.close()
m = self._load(doc.documentElement)
doc.unlink()
return m
def _load(self, head):
"""Creates all menu object recursivly
Starting at the document root, processes all menu tags thus building
the menu system.
Each meun tag is processed for it's attributes and contained choices.
If one of the menu's choices is itself a [sub]menu, then recursivly
process all of that menu's attributes and choices.
The parameter to this function is the xml an menu tag object. Start
with the document root, and recursivly progress down through submenus.
The return value of this function is the head Menu object representing
the entire menu system.
"""
choice_list = []
for child in [x for x in head.childNodes if isinstance(x, xml.dom.minidom.Element) and x.tagName.lower() == 'choice']:
c = Choice()
c.selector = int(child.getAttribute('selector'))
c.description = child.getAttribute('description')
c.value = child.getAttribute('value')
handler_name = child.getAttribute('handler')
if handler_name != 'None':
c.handler = getattr(self.module, handler_name)
else:
c.handler = None
if child.hasChildNodes():
tmp_m = [x for x in child.childNodes if isinstance(x, xml.dom.minidom.Element) and x.tagName.lower() == 'menu']
try:
c.subMenu = self._load(tmp_m[0])
except IndexError:
pass
choice_list.append(c)
return Menu(title=head.getAttribute('title'), prompt=head.getAttribute('prompt'), choice_list=choice_list)
def save(self, menu):
"""Saves a Menu System to XML File
Saves a complete Menu System and all sub-menus to an XML file in the
above specified format. The parameter location is used to specify
where the file is written to. It can be a file object, the path to a
local file or a string which will be set with the actual XML.
Note unlike the load function, you cannot save to a URL.
"""
self.doc = xml.dom.minidom.Document()
self.doc.appendChild(self._save(menu))
fp = self._open('w')
if fp:
self.doc.writexml(fp,addindent='\t', newl='\n')
fp.close()
else:
print 'Unable to output xml'
self.doc.unlink()
def _save(self, menu):
"""Helper function for Saving
Helper function to implement recursion for traversing the Menu System.
Parameter menu is the current menu object.
Returns the menu xml element
"""
m = self.doc.createElement('menu')
m.setAttribute('title', str(menu.title))
m.setAttribute('prompt', str(menu.prompt))
for choice in menu.choices:
c = self.doc.createElement('choice')
c.setAttribute('selector', str(choice.selector))
c.setAttribute('description', str(choice.description))
c.setAttribute('value', str(choice.value))
if choice.handler:
c.setAttribute('handler', choice.handler.func_name)
else:
c.setAttribute('handler', 'None')
if choice.subMenu:
c.appendChild(self._save(choice.subMenu))
m.appendChild(c)
return m
def _open(self, mode):
"""Open any location for reading or writing
Helper function to open a file, url, or string for reading or writing.
Used in conjunction with the save and load functions.
Valid options for mode are 'w' for write and 'r' for read. There is no
need for append.
The src parameter can be any valid url, file location, string, or file
object.
This method was borrowed from
http://www.diveintopython.org/xml_processing/index.html, and
modified slightly.
The return value will be a file object unless there is an error. In
that case the return value will equal None.
"""
mode = mode.lower()
if mode not in ('w', 'r'):
return None
if self.loc == '-' and mode == 'w':
return sys.stdout
if self.loc == '-' and mode == 'r':
return sys.stdin
if hasattr(self.loc, 'read') and mode == 'r':
return self.loc
if hasattr(self.loc, 'write') and mode == 'w':
return self.loc
if mode == 'r':
import urllib
try:
return urllib.urlopen(self.loc)
except (IOError, OSError):
pass
try:
return open(self.loc, mode)
except (IOError, OSError):
pass
if type(self.loc) == str:
import StringIO
return StringIO.StringIO(self.loc)
return None
MenuSystem-1.0/menusystem/__init__.py 0000644 0001750 0000144 00000000000 10424013346 017143 0 ustar daniel users MenuSystem-1.0/setup.py 0000644 0001750 0000144 00000001151 10424027771 014352 0 ustar daniel users from distutils.core import setup
setup(name='MenuSystem',
version='1.0',
author='Daniel Mikusa',
author_email='dan@trz.cc',
url='http://www.mikusa.com/menu_system/',
description='Build Text Based Menus Easily',
long_description=None,
license='LGPL',
platforms='OS Independent',
classifiers=[
'Development Status :: 4 - Beta',
'Environment :: Console',
'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
'Intended Audience :: Developers',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Utilities'
],
packages=['menusystem']
)