Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Console Menu Generator in Python

As the title says, I'm writing a Console Menu Generator in Python. I have 2 classes, Menu and Item. But I get into troubles. Here is the code:

class Menu:
    def AddItem(self,item):
        class Item:
            def __init__(self,text,ToDoNext):
                self.text=text
                ??????????????
        self.item.append(Item())
    def Show():
        for i in range(len(self.item)):
            print(str(i+1)+") "+str(self.item[i])+"\n")
        print("0) Back\n")
        option=int(input())
        self.item[option].????????????

This code basically do the next:

Main=Menu()
Menu.AddItem("Open file",ToDo1)
Menu.AddItem("Save file",ToDo2)
Menu.Show()

'''1) Open file
   2) Save file
   0) Back
   _
'''

If I write 1 and press enter should do the portion of code ToDo1, for example. The solution that I thought is the nextone:

def ToDo1():
    print("Hello, world!")
Menu.AddItem("Say Hello","ToDo1()")

and use an eval() function inside the Show().

But I'm not pretty sure this is not the correct way to do that.

I would like you to show me a better way, and if you have ever do something like that (Console Menu Generator) to share the code and see another way of doing the same.

like image 487
Quarktum Avatar asked Sep 02 '25 06:09

Quarktum


1 Answers

I absolutely recommend creating a class Item, even if you only have text and function attributes! Who knows what kind of complex logic you will need later on. With this in mind, creating a menu would probably look something like this:

main = Menu()
main.AddItem(Item("Open", openFile))
main.AddItem(Item("Close", closeFile))

Also, on top of your text and function attributes, you should add parent attribute to the Item class. parent simply points at the parent menu of our item:

main = Menu()

# automatically calls main.AddItem(item1)
open = Item("Open", openFile, main)

# automatically sets parent to main
main.Add(Item("Close", closeFile))

Now that we know how a proper Menu and Item should work, we can start coding the classes.


Menu

This shouldn't be too hard, all we need are add_item(), remove_item() and draw() methods and a list of items. Also it would be good to draw our menu's name, so lets add name attribute.

class Menu:
    def __init__(self, name, items=None):
        self.name = name
        self.items = items or []

    def add_item(self, item):
        self.items.append(item)
        if item.parent != self:
            item.parent = self

    def remove_item(self, item):
        self.items.remove(item)
        if item.parent == self:
            item.parent = None

    def draw(self):
        print(self.label)
        for item in self.items:
            item.draw()

Obviously we could code much more methods and attributes for our menu, but that includes all the essential methods.


Item

Item class should be even easier, it hardly needs any methods at all. Item obviously needs a name and a function (function will be ran when item gets activated), on top of that it has the earlier mentioned parent attribute. We probably should create a setter for parent, which would automatically move the item under an other menu, but I'll leave that for you if you want to do it. Also don't forget the draw()-method for item too, we must be able to draw our items the way they want to be drawn, not the way our Menu wants to draw them.

class Item:
    def __init__(self, name, function, parent=None):
        self.name = name
        self.function = function
        self.parent = parent
        if parent:
            parent.add_item(self) # use add_item instead of append, since who
                                  # knows what kind of complex code you'll have
                                  # in add_item() later on.

    def draw(self):
        # might be more complex later, better use a method.
        print("    " + self.name)

Final thoughts

We've now finished our menu, it works and you should be able to use it as a basic menu. However, the superior console menu would only have one class called MenuItem. Each item's parent would be an other MenuItem (each, but the root MenuItem's of course) and the menu would look something like this when it's drawn:

[-] Root
    [+] Submenu of Root
    [-] An other submenu of Root
          This menu runs functions, others open/close
         <This menu has focus on it>
          Select this menu by pressing mousedown
    [+] A third submenu of Root

Where not giving function parameter would create items list and allow users to close/open the menuitem. If function is given, it will work normally and only execute the function when selected. To go even a step further, we would have separated MenuItem and two subclasses: ActionMenu and ContainerMenu. But please keep in mind, this is somewhat hard to code and not for beginners. You might wanna stick with the first version I went through.


Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!