Python控制台菜单生成器

3 投票
4 回答
12695 浏览
提问于 2025-04-17 17:09

正如标题所说,我正在用Python写一个控制台菜单生成器。我有两个类,分别是菜单(Menu)和项目(Item)。但是我遇到了一些麻烦。以下是我的代码:

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].????????????

这段代码基本上是做以下事情:

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

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

如果我输入1然后按回车,就应该执行代码中的ToDo1部分,比如说。

我想到的解决方案是这样的:

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

并在Show()函数里面使用eval()函数。

但我不太确定这样做是否正确。

我希望你能给我展示一个更好的方法,如果你也做过类似的事情(控制台菜单生成器),请分享你的代码,让我看看其他的实现方式。

4 个回答

0

在Python中,函数可以随意传递。如果你写 AddItem("Say Hello", ToDo1),这其实是把 ToDo1 这个函数对象传递过去。你可以把它存储在 self.function 里,之后用 fn = self.item[option].function 把它取出来,然后用 fn() 来调用它。当你明白一个普通的函数调用,比如 do_stuff(),其实做了两件事时,就会更清楚了:第一步是从变量 do_stuff 中获取这个函数对象(这个变量通常是一个不会被修改的全局变量),第二步是调用这个函数对象。

0

这里有一个可以运行的例子

通常,菜单的类部分会放在一个叫做“myMenu”的文件里,然后用命令 from myMenu import myMenu 导入进来

items 是一个字典数组。每个列表项都有一个字典,里面有两个条目,分别是“text”和“func”

输入的编号是 n-1,因为数组的编号是从零开始的

import sys
class myMenu:
    items=[]

    def AddItem(self,text,function):
        self.items.append({'text': text, 'func':function})

    def Show(self):
        c=1
        for l in self.items:
            print c, l['text'],"\n"
            c = c +1

    def Do(self,n):
        self.items[n]['func']()

def clist():
    print "cheeses are wensleydale and cheddar\n"

def bye():
    print "bye"
    sys.exit(0)

if __name__ == "__main__":
    m=myMenu()
    m.AddItem("cheese",clist)
    m.AddItem("quit",bye)

while(True):
    m.Show()
    n=input("choice>")
    m.Do(n-1)

如果你想把菜单项做成一个类,而不是字典,那么在 MyMenu 类后面直接声明这个类,像这样(这个还没测试过)

class myMenu:
    items=[]
    class Item:
        func=None
        text="default"

        def __init__(self,t,f):
            self.text=t
            self.func=f

    def AddItem(self,text,function):
        self.items.append(Item(text,function))
7

我非常推荐你创建一个叫做 Item 的类,即使你现在只需要 textfunction 这两个属性!谁知道将来你会需要什么样复杂的逻辑呢?考虑到这一点,创建一个菜单可能看起来像这样:

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

另外,在你的 textfunction 属性上,你还应该给 Item 类添加一个 parent 属性。parent 只是指向我们这个项目的父菜单:

main = Menu()

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

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

现在我们知道了一个合适的 MenuItem 应该怎么工作,我们可以开始编写这些类的代码了。


菜单

这应该不难,我们只需要 add_item()remove_item()draw() 这几个方法,以及一个 items 的列表。还有,绘制菜单的名字也是个好主意,所以我们加一个 name 属性。

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()

显然,我们可以为菜单编写更多的方法和属性,但这些已经包含了所有基本的方法。


项目

Item 类应该更简单,几乎不需要什么方法。Item 当然需要一个 name 和一个 function(当这个项目被激活时会执行这个函数),此外还有之前提到的 parent 属性。我们可能应该为 parent 创建一个设置方法,这样可以自动将项目移动到另一个菜单下,但如果你想做的话,我就留给你去实现。还有,不要忘了给项目也加一个 draw() 方法,我们必须能够按照项目想要的方式来绘制它们,而不是按照我们的 Menu 想要的方式。

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)

最后的想法

我们现在已经完成了菜单,它可以正常工作,你应该能够把它用作一个基本的菜单。不过,更高级的控制台菜单只会有一个叫做 MenuItem 的类。每个项目的父项会是另一个 MenuItem(当然,除了根 MenuItem)。当菜单被绘制时,它会看起来像这样:

[-] 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

如果不提供 function 参数,就会创建一个 items 列表,并允许用户关闭或打开这个菜单项。如果提供了 function,它会正常工作,并且只有在被选中时才会执行这个函数。为了更进一步,我们可以将 MenuItem 分开成两个子类:ActionMenuContainerMenu。但请记住,这样做有点复杂,不适合初学者。你可能还是先坚持使用我刚才讲的第一个版本吧。

撰写回答