Welcome to HBH! If you had an account on hellboundhacker.org you will need to reset your password using the Lost Password system before you will be able to login.

Console Application Menu Module - Python Code Bank


Console Application Menu Module
Well, it bothers me to build menus for every shell application that needs one. So, I've written a module to handle that, like in the good old turbo pascal times. Here it is, the module comments are pretty explanatory, I think, and the sample usage if run as script should enlighten the concept. Use it, give comments, improvements, have fun... Cheers and Happy coding
                import string
from textwrap import wrap

try:
    from cStringIO import StringIO
except:
    from StringIO import StringIO

class Console_App_Menu():
    
    """Console Application Menu module.
        It implements basic menu and dialogs on a console window."""

    def __init__(self, title='Console Application Menu', size=(80, 25), border='#', log_title='Log Output:'):

        """The class initialization takes as optional arguments:
            - title: title text of the menu
            - size: the size in characteres of the console window menu
            - border: character to be printed as border for the menu and dialogs
            - log_title: title text of the log window"""
        
        self.__width = size[0]
        self.__height = size[1] - 1
        self.__line = lambda(x): x * self.__width
        self.__ruler = lambda((x, l)): (x / 2) - (l / 2)
        self.__content = StringIO()
        self.__content.write((' ' * self.__width) * self.__height)
        self.__border = border[0]
        self.__log_title = log_title
        self.__log = ['', '', '', '']
        self.__draw_border()
        self.__writeline(3, title)
        self.__log_box()

    def __draw_border(self):
        self.__content.seek(0)
        self.__content.write(self.__border * self.__width)
        for i in range (1, self.__height):
            self.__content.seek(self.__line(i))
            self.__content.write(self.__border)
            self.__content.seek(self.__line(i + 1) - 1)
            self.__content.write(self.__border)
        self.__content.seek(self.__line(i))
        self.__content.write(self.__border * self.__width)

    def __writeline(self, line_n, text):
        self.__content.seek(self.__line(line_n) + self.__ruler((self.__width, len(text))))
        self.__content.write(text)

    def __log_box(self):
        self.__content.seek(self.__line(self.__height - 8) + 10)
        self.__content.write(self.__border + self.__border + self.__log_title)
        self.__content.write(self.__border * (self.__width - 22 - len(self.__log_title)))
        for i in reversed(range(4, 8)):
            self.__content.seek(self.__line(self.__height - i) + 10)
            self.__content.write(self.__border)
            self.__content.seek(self.__line(self.__height - (i - 1)) - 11)
            self.__content.write(self.__border)
        self.__content.seek(self.__line(self.__height - 3) + 10)
        self.__content.write(self.__border * (self.__width - 20))

    def __update_display(self):
        print self.__content.getvalue(),
        print self.__question

    def __log_write(self, output):
        for i in range(len(self.__log)):
            if self.__log[i] == '':
                self.__log[i] = output
                break
        else:
            self.__log.pop(0)
            self.__log.append(output)
        ylines = 7
        self.__content.seek(self.__line(self.__height - ylines) + 12)
        for i in range(len(self.__log)):
            self.__content.write(self.__log[i] + (' ' * (self.__width - 24 - len(self.__log[i]))))
            ylines -= 1
            self.__content.seek(self.__line(self.__height - ylines) + 12)
        
    def __dialog(self, dlg_type, dlg_text):
        scr_buffer = self.__content.getvalue()
        dlg_types = 'error', 'info', 'query_str', 'query_int', 'query_bool'
        dlg_titles = 'Error:', 'Information:', 'Question:', 'Question:', 'Question:'
        line = (self.__height / 2) - 3
        self.__content.seek(self.__line(line) + (self.__width / 4) - 1)
        self.__content.write(' ' * ((self.__width / 2) + 2))
        line += 1
        self.__content.seek(self.__line(line) + (self.__width / 4) - 1)
        self.__content.write(' ' + self.__border + self.__border + dlg_titles[dlg_types.index(dlg_type)])
        self.__content.write(self.__border * ((self.__width / 2) - len(dlg_titles[dlg_types.index(dlg_type)])) + ' ')
        for i in range(1, 4):
            self.__content.seek(self.__line(line + i) + (self.__width / 4) - 1)
            self.__content.write(' ' + self.__border + (' ' * (self.__width / 2) + self.__border + ' '))
        self.__content.seek(self.__line(line + 4) + (self.__width / 4) - 1)
        self.__content.write(' ' + (self.__border * ((self.__width / 2) + 2)) + ' ')
        self.__content.seek(self.__line(line + 2) + (self.__width / 4) + 1)
        self.__content.write((' ' * ((self.__width / 4) - (len(dlg_text) / 2) - 1)) + dlg_text  + (' ' * ((self.__width / 4) - (len(dlg_text) / 2))))
        print self.__content.getvalue(),
        if dlg_type in ('error', 'info'):
            raw_input('Press Enter to continue... ')
            self.__content.seek(0)
            self.__content.write(scr_buffer)
            self.__update_display()
        elif dlg_type == 'query_str':
            value = raw_input('Enter string: ')
            self.__content.seek(0)
            self.__content.write(scr_buffer)
            return value
        elif dlg_type == 'query_int':
            while True:
                value = raw_input('Enter number: ')
                try:
                    value = int(value)
                    self.__content.seek(0)
                    self.__content.write(scr_buffer)
                    return value
                except:
                    self.__content.seek(0)
                    self.__content.write(scr_buffer)
                    return self.__dialog(dlg_type, dlg_text)
        elif dlg_type == 'query_bool':
            while True:
                value = raw_input('Enter \'Y(y)...\' or \'N(n)...\': ')
                if value:
                    if value[0].lower() == 'y':
                        self.__content.seek(0)
                        self.__content.write(scr_buffer)
                        return True
                    elif value[0].lower() == 'n':
                        self.__content.seek(0)
                        self.__content.write(scr_buffer)
                        return None
                self.__content.seek(0)
                self.__content.write(scr_buffer)
                return self.__dialog(dlg_type, dlg_text)

    def set_options(self, index_type, options_list, separator=') ', question='Insert option: '):

        """Sets menu options. It takes as arguments:
            - index type: 'numbers', 'low' and  'caps'
            - options: list of menu options
            and as optional arguments:
            - separator: characteres between the index and option
            - question: text following the menu
            Usage: Console_App_Menu.set_options(index, options)"""

        self.__separator = separator
        self.__question = question
        lenght = len(options_list)
        start = self.__ruler((self.__height - (self.__height / 4), lenght)) 
        string_size = max([len(item) for item in options_list])
        self.__options_list = [string.ljust(item, string_size) for item in options_list]
        if index_type == 'numbers':
            self.__indexs = string.digits
        elif index_type == 'low':
            self.__indexs = string.lowercase
        elif index_type == 'caps':
            self.__indexs = string.uppercase
        else:
            print 'Invalid Index Type'
        self.__valids = []
        index = 0
        for i in range(start, start + lenght):
            self.__writeline(i, self.__indexs[index] + self.__separator + self.__options_list[index])
            self.__valids.append(self.__indexs[index])
            index += 1

    def option(self):

        """Returns user selected menu option as a tuple containing:
            - the option index order
            - the option index character
            - the option text
            Usage: option = Console_App_Menu.option()"""

        print self.__content.getvalue(),
        selected = raw_input(self.__question)
        if selected not in self.__valids:
            self.__dialog('error', 'Invalid Option Selected!!!')
            return self.option()
        else:
            return (self.__valids.index(selected), selected, self.__options_list[self.__valids.index(selected)].strip())

    def log(self, output):

        """Prints given text to the log window.
            Usage: Console_App_Menu.log(text)"""
        
        if len(output) > 56:
            list_output = wrap(output, 56)
            for output in list_output:
                self.__log_write(output)
            self.__update_display()
        else:
            self.__log_write(output)
            self.__update_display()
        
    def info(self, text):

        """Displays a information dialog with the given text.
            Usage: Console_App_Menu.info(text)"""
        
        self.__dialog('info', text[0:37])

    def error(self, text):

        """Displays a error dialog with the given text.
            Usage: Console_App_Menu.error(text)"""

        self.__dialog('error', text[0:37])

    def query(self, input_type, text):

        """Displays a query dialog with the given text and can return
            three different types of data:
                - an integer: Console_App_Menu.query('int', text)
                - a string: Console_App_Menu.query('str', text)
                - a boolean: Console_App_Menu.query('bool', text)
            Usage: input = Console_App_Menu.query(type, text)"""
        
        if input_type in ('int', 'str', 'bool'):
            return self.__dialog('query_' + input_type, text[0:37])
        
if __name__ == '__main__':

    menu = Console_App_Menu(title='Console Application Menu Calculator Demo')

    menu.set_options('low', ('Add x to y', 'Subtract x to y', 'Multiply x by y', 'Divide x by y', 'Help', 'Exit'))

    menu.log('Welcome, please select an option to continue...')

    exit_flag = None

    while not exit_flag: 

        selected = menu.option()

        if selected == (0, 'a', 'Add x to y'):
            x = menu.query('int', 'Insert number x')
            y = menu.query('int', 'Insert number y')
            menu.log('The result of adding %s to %s is %s.' % (x, y, (x + y)))
        elif selected[0] == 1:
            x = menu.query('int', 'Insert number x')
            y = menu.query('int', 'Insert number y')
            menu.log('The result of subtracting %s to %s is %s.' % (x, y, (y - x)))    
        elif selected[1] == 'c':
            x = menu.query('int', 'Insert number x')
            y = menu.query('int', 'Insert number y')
            menu.log('The result of multiplying %s by %s is %s.' % (x, y, (x * y)))    
        elif selected[0] == 3:
            x = menu.query('int', 'Insert number x')
            y = menu.query('int', 'Insert number y')
            menu.log('The result of dividing %s by %s is %s.' % (x, y, (x / y)))    
        elif selected[2] == 'Help':
            menu.log('Select a calculation option. The numbers will be asked, the calculation is done and the result will be displayed on this log window.')    
        elif selected[2] == 'Exit':
            if menu.query('bool', 'Are you sure you want to exit?'):
                exit_flag = True
    else:
        name = menu.query('str', 'Insert your name')
        menu.info('Bye, %s!' % name)
            
Comments
Sorry but there are no comments to display