faqts : Computers : Programming : Languages : Python : Projects

+ Search
Add Entry AlertManage Folder Edit Entry Add page to http://del.icio.us/
Did You Find This Entry Useful?

12 of 13 people (92%) answered Yes
Recently 9 of 10 people (90%) answered Yes

Entry

How can I create a platform independent 'program launch menu' with python?

Mar 26th, 2002 12:08
Jason Stracner,


Here is a good way to organize some of your most commonly run programs 
with a simple text screen interface that you can design however you 
like.  Although, there are plenty of cool dos menus out there (RVMS, 
Automenu, Power Menu, …), if you want to create one of your own with 
Python (see http://python.org) that can be run on a multitude of 
platforms (windows, Mac, Linux, …) try this.  I've written an easy to 
use framework for creating a menu (or menus) of programs and system 
commands.  You just run this python script and press the letter of the 
menu item you want to run.  Each menu item can contain as many commands 
and programs as you like.  You may also use the left and right arrow 
keys to jump to other menus.  This program reads from a file called 
menu.txt to get the menu screens and command list.  You will need to 
create two text files to make this all work.  The first is the menu.txt 
file and the other is a file with extension .py (e.g.: menu.py).  The 
form of the menu.txt should be something like this:
<Menu1>
<Screen1>
@ECHO off
COLOR 0C
CLS
@ECHO A. Notepad
@ECHO B. Wordpad.
</Screen1>
<A>
CLS
@ECHO Starting notepad.
call start c:\windows\notepad.exe
</A>
<B>
call start c:\windows\wordpad.exe
</B>
</Menu1>
You can also add a <Menu2> tag and the launcher will allow you to use 
the left and right arrow keys to switch menus.  Just add more by adding
more <menu?> tags (e.g.: <menu3>, <menu4>, <menu5>, <menu6>, ...).
Note: Sometimes on windows ‘call’ and/or ‘start’ is used before the 
command to keep the python script from staying open after the program 
is launched.
Tip:  You might want to consider putting a shortcut to this script on 
your desktop and in the shortcut's properties assign it a keyboard 
shortcut.  By doing this you can just press something like Ctrl+Alt+C 
to bring up your menu no matter where you are.  
Okay.  Here is the code for the python script.  You can just copy this 
and save it to a file with the extension .py.
______________________________________________
# Menu.py
"""A class for making a program launcher menu
 for multiple platforms."""
#
# Author:  Jason Stracner
#
import os, string, re, sys, traceback, time
try:
  # This will give an error on non-windows systems.
  # We will use raw_input() for those platforms.
  import msvcrt
except:
  pass
class my_menu:
  # This holds the number of the current menu.
  # This is shown in the menu.txt file as <Menu1>
  iMenu = 1
  # This holds the letters of the commands.
  sMenuKeys=[]
  # Holds the actual commands.  They are separated
  # by newline characters.
  sMenuCommands=[]
  # Holds the full text of the menu.txt file.
  sTextFromMenuFile=""
  # Display the screen interface that is defined
  # by the <Screen?> tag in the menu.txt file.
  # Send each line in the tag to the system as
  # commands.
  def defPrintMenu(self):
    # Use a regular expression to get the value
    # of the <Screen?> tag.
    # re.S <--- Makes the . character represent
    # any character including the \n (newline)
    # character.
    # re.I <--- Makes the expression ignore case.
    oMatches = re.compile("<Screen" +
                          str(self.iMenu) +
                          ">.*</Screen" +
                          str(self.iMenu) +
                          ">",re.S|re.I)
    # Run the regular expression against the text
    # in the menu.txt file.
    oMatch = oMatches.search(self.sTextFromMenuFile)
    # If there was a match.
    if oMatch:
      # Get the matching text.
      sScreen = oMatch.group()
      # Split the text in the <screen> tag into
      # a list.  Split at the newline character.
      sScreenLines = string.split(sScreen,"\n")
      # Loop through each line of the screen tag.
      for sScreenLine in sScreenLines:
        # Do not send the lines that have the
        # screen tag in them.
        if (string.lower(sScreenLine)[0:7] <> "<screen"
              and string.lower(sScreenLine)[0:8] <> "</screen"):
          # Send the command to the system.
          os.system(sScreenLine)
    # No match.  This happens when you go to
    # a menu number that has not been defined.
    else:
      if sys.platform[:3] == 'win':
        # If this is a windows system then we
        # can clear the screen with the CLS
        # command.
        os.system('CLS')
      print "No menu.  Use the arrow keys to return to the last menu."
  # Fills the sMenuKeys and sMenuCommands class attributes
  # using the data in the menu.txt file.
  def load_menu_items(self):
    # Get the contents of the file into the sTextFromMenuFile
    # class attribute.
    self.sTextFromMenuFile=open("menu.txt").read()
    # Get a match object from a regular expression.
    oMatches = re.compile("<Menu" +
                          str(self.iMenu) + ">.*</Menu" +
                          str(self.iMenu) + ">",re.S|re.I)
    # Run the regular expression agains the text in from
    # the file.
    oMatch = oMatches.search(self.sTextFromMenuFile)
    # Reset the sMenuKeys class attribute.
    self.sMenuKeys=[]
    # Reset the sMenuCommands class attribute.
    self.sMenuCommands=[]
    # If there was a match found.
    if oMatch:
      # Place the matching text (the stuff between
      # the <menu?> tags) into sTextFromMenuFile.
      self.sTextFromMenuFile = oMatch.group()
      # Split the current menu text into a list.  Split
      # at the newline character.
      sTextLines=string.split(self.sTextFromMenuFile,"\n")
      # Loop through the lines.
      for sTextLine in sTextLines:
        # The line has to be longer than two characters.
        if sTextLine.__len__() > 2:
          # The line must have a < at the start and a >
          # at the third character.
          if sTextLine[0]=="<" and sTextLine[2]==">":
            # Add the menu letter to the sMenuKeys
            # class attribute.
            self.sMenuKeys.append(sTextLine[1])
            # Add the commands for that letter to the
            # sMenuCommands class attribute.
            self.sMenuCommands.append(string.split(
              self.sTextFromMenuFile,sTextLine[1] + ">")[1])
  # This is the first def that will run.
  def main(self):
    # Load the menu data into the class attributes
    self.load_menu_items()
    # Show the menu screen for the current menu.
    self.defPrintMenu()
    # Loop forever.
    while 1:
      if sys.platform[:3] == 'win':
        # It this is windows then check if there has
        # been a keypress event.
        if msvcrt.kbhit()!=0:
          # If so get the character of the key that
          # was pressed.
          sKey = msvcrt.getch()
        else:
          # If there was no keypressed then set
          # sKey to ''.
          sKey = ''
      else:
        # This is not windows so we will just use the
        # raw_input() method to get user input.
        sKey = raw_input("Select a menu item and hit enter:")
      # If there was a key pressed.
      if sKey:
        if ord(sKey) == 75: # Left arrow.
          # If the left arrow was pressed then
          # subtract one from the iMenu attribute.
          self.iMenu = self.iMenu - 1
          # Load the menu data.
          self.load_menu_items()
          # Print the screen for the current menu.
          self.defPrintMenu()
          print "Menu: " + str(self.iMenu)
        if ord(sKey) == 77: # Right arrow.
          # Subtract one if this was the right arrow.
          self.iMenu = self.iMenu + 1
          self.load_menu_items()
          self.defPrintMenu()
          print "Menu: " + str(self.iMenu)
        if ord(sKey) == 27: # Esc
          # If this was the escape key.
          if sys.platform[:3] == 'win':
            # If this is windows use CLS to
            # clear the screen.
            os.system('CLS')
          # Exit the while loop.
          break
        # Loop through each item in the list of menu keys.
        for i in range(0, self.sMenuKeys.__len__()):
          # Check the key against each in the sMenuKeys list.
          if string.lower(self.sMenuKeys[i]) == string.lower(sKey):
            # Make a list from the commands for the menu key.
            sEachCommand = string.split(self.sMenuCommands[i],"\n")
            # Loop through each command.
            for sCommand in sEachCommand:
              # If this command is not '' and not '</'
              # Happens because of the way we split it up.
              if sCommand.__len__() > 0 and sCommand <> "</":
                # Send each command for the menu key.
                os.system(sCommand)
            # End program.
            sys.exit()
# If this file was executed and not imported.
if __name__ == "__main__":
  try:
    # Make sure that the current directory is the
    # directory that contains this script.
    os.chdir(os.path.dirname(sys.argv[0]))
    # Make an instance object from the my_menu class.
    oMyMenu = my_menu()
    # Run the main() method in the class.
    oMyMenu.main()
  except SystemExit:
    # This error is caused by the sys.exit()
    # method.  This error was triggered so that
    # we can end the program so we will just
    # ignore it.
    # The pass method doesn't do anything.
    pass
  except:
    # Print the traceback information for errors
    # that were unexpected.
    print "\nFatal error.  Cannot continue."
    print "\n\n######## Error Info ###########"
    traceback.print_exc()
    print     "###############################"
    # Log the error info to error_log.txt.
    sErr_File_Content_List=[]
    if os.path.exists('error_log.txt'):
        sErr_File_Content_List = string.split(
              open("./error_log.txt").read(),"\n")
        # Don't let the error log get longer than
        # 100,000 lines.
        if os.path.getsize("./error_log.txt")>100000:
          # Shorten by 1000 lines if needed.
          sErr_File_Content_List=sErr_File_Content_List[1000:]
    # Log a bunch of information about the error.
    sErr_File_Content = string.join(sErr_File_Content_List,'\n')
    sErr_File_Content = sErr_File_Content + '\n--- '
    sErr_File_Content = sErr_File_Content +  time.ctime(time.time())
    sErr_File_Content = sErr_File_Content + ' ---\n'
    # Open the error log file for writing ('w').
    oFile_Err_Log = open('error_log.txt','w')
    # Write the error info.
    oFile_Err_Log.write(sErr_File_Content)
    # Write the traceback into to the file.
    traceback.print_exc(None, oFile_Err_Log)
    # Close the log file.
    oFile_Err_Log.close()
    # Make sure the oFile_Err_Log object is removed.
    oFile_Err_Log=None
    print "Press enter to exit"
    raw_input("")
##################################
#  Here is an example menu file. #
##################################
#
#<menu1>
#<Screen1>
#@ECHO off
#COLOR 0C
#CLS
#@ECHO ============================================================
#@ECHO =           Jason's Super Cool DOS Menu                    =
#@ECHO ============================================================
#@ECHO =                                                          =
#@ECHO =  A. Python         B. Connect to the internet.           =
#@ECHO =  C. Disconnect from internet.  D. IE                     =
#@ECHO =  E. Dos prompt      F. Winamp                            =
#@ECHO =  G. Windows Media Player  H. FrontPage                   =
#@ECHO =                                                          =
#@ECHO =  Z. Edit this Menu.                                      =
#@ECHO =   Right arrow key = Programming                          =
#@ECHO ============================================================
#@ECHO =      (ESC) = Exit                                        =
#@ECHO ============================================================
#</Screen1>
#<A>
#CLS
#python
#</A>
#<B>
#call c:\menus\rasdial -D:"Connection to HandtechISP Properties"
#</B>
#<C>
#call c:\menus\rasdial -H
#</C>
#<D>
#C:\Program Files\Internet Explorer\IEXPLORE.EXE
#</D>
#<E>
#cls
#cmd
#</E>
#<F>
#call "C:\Program Files\Winamp\winamp.exe"
#</F>
#<G>
#call "C:\Program Files\Windows Media Player\wmplayer.exe" /prefetch:1
#</G>
#<H>
#call "C:\Program Files\Microsoft Office\Office\FRONTPG.EXE"
#</H>
#<Z>
#call start notepad menu.txt
#</Z>
#</Menu1>
#
#
#
#
#<Menu2>
#<Screen2>
#
#@ECHO off
#COLOR 0C
#CLS
#@ECHO ===============================================================
#@ECHO =         Jason's Super Cool DOS Programming Menu             =
#@ECHO ===============================================================
#@ECHO =                                                             =
#@ECHO =   A. Open menu.py in IDLE                                   =
#@ECHO =                                                             =
#@ECHO =   Left arrow key = Main Menu                                =
#@ECHO ===============================================================
#@ECHO =       (ESC) = Exit                                          =
#@ECHO ===============================================================
#</Screen2>
#<A>
#call start pythonw.exe "C:\Python22\Tools\idle\idle.pyw" -e menu.py
#</A>
#</Menu2>