Python控制台菜单生成器
正如标题所说,我正在用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 个回答
在Python中,函数可以随意传递。如果你写 AddItem("Say Hello", ToDo1)
,这其实是把 ToDo1
这个函数对象传递过去。你可以把它存储在 self.function
里,之后用 fn = self.item[option].function
把它取出来,然后用 fn()
来调用它。当你明白一个普通的函数调用,比如 do_stuff()
,其实做了两件事时,就会更清楚了:第一步是从变量 do_stuff
中获取这个函数对象(这个变量通常是一个不会被修改的全局变量),第二步是调用这个函数对象。
这里有一个可以运行的例子
通常,菜单的类部分会放在一个叫做“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))
我非常推荐你创建一个叫做 Item
的类,即使你现在只需要 text
和 function
这两个属性!谁知道将来你会需要什么样复杂的逻辑呢?考虑到这一点,创建一个菜单可能看起来像这样:
main = Menu()
main.AddItem(Item("Open", openFile))
main.AddItem(Item("Close", closeFile))
另外,在你的 text
和 function
属性上,你还应该给 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))
现在我们知道了一个合适的 Menu
和 Item
应该怎么工作,我们可以开始编写这些类的代码了。
菜单
这应该不难,我们只需要 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
分开成两个子类:ActionMenu
和 ContainerMenu
。但请记住,这样做有点复杂,不适合初学者。你可能还是先坚持使用我刚才讲的第一个版本吧。