写本教程的原因以及前言
这几天希望能找到一个优秀的tkinter教程以在短时间内能够学会tkinter,但是找了很久没找到,网上的博客说的不够详细,b站上的又找不到足够快且详细的教程,所以狠下心来自己看书并且结合网上的一些博客来写这个教程。
个人也是一个初学者,刚刚大一,仍在学习过程,写这篇教程是为了加深自己的印象并方便以后复习。在本教程中我会以一个python入门的初学者的角度来进行讲解,所以若有哪些不够严谨或者出错的地方,可以联系我进行更正,我也很乐意和他人互相分享经验以共同促进,非常感谢!
若有其他要了解的,可以参考python官方文档:Graphical User Interfaces with Tk — Python 3.12.2 documentation
tkinter是一个python中自带的GUI设计库,不需要自己再进行安装。
请注意:笔者所用的方法和属性皆为最常用的方法和属性,基本可以满足大部分需求,若有其他功能想要实现,请询问GPT、文心一言等AI,都能够得到详细的回答。
若认为我的文章有哪些地方可以改进的,可以联系我,我也希望借此机会练练手,非常感谢!
创建窗口并设置窗口参数
from tkinter import * # 从tkinter库中直接导入所有的方法
window = Tk() # 实例化一个窗口对象,从现在开始,window就是一个窗口了,以后我们可以对其进行设置
window.title("This is Tk") # 设置window的标题
window.geometry('600x400+100+100') # 设置window的大小,geometry:几何形状
window.maxsize(width=800,height=600) # 设置窗口拖动可达到的最大尺寸
window.config(bg="red") # 设置窗口背景颜色为红色
window.mainloop() # loop:循环 将窗口不断地循环刷新以保持窗口存在直到手动关闭
代码运行结果如下:
注意事项:
1、所有关于尺寸的设置都是 宽x高
2、geometry() 中的为字母‘x’ ,第一个'+100' 表示距离屏幕左边界100像素点,第二个为y
要想获知窗口的大小,可以使用微信截图获取,可以依此设计窗口大小。
3、window.mainloop() 放在代码的最后一行
Label标签组件及参数
Label() 方法用于在窗口内建立文字或者图形标签
from tkinter import * # 从tkinter库中直接导入所有的方法
window = Tk() # 实例化一个窗口对象,从现在开始,window就是一个窗口了,以后我们可以对其进行设置
window.title("This is Tk") # 设置window的标题
window.geometry('600x400+100+100') # 设置window的大小,geometry:几何形状
window.maxsize(width=800, height=600) # 将窗口最大化
label = Label(window, # 所有组件的第一个参数都是父窗口,父窗口指的是该组件建立在父窗口之上
width=20, # 宽度设置为20个字符
height=2, # 高度设置为2个字符
text="This is a label", # 设置标签显示的文本内容
bg='lightyellow', # 设置背景 backgroud 为亮黄色
fg='red', # 设置字体 foreground 颜色为红色
font="Arial 12 bold italic", # 设置字体的参数,有字体、字号、样式
relief=RAISED # 设置标签的外框样式为凸起
)
label.pack() # 已经设置好了label的参数,就需要将其pack打包放到window窗口上
window.mainloop() # loop:循环 将窗口不断地循环刷新以保持窗口存在直到手动关闭
代码运行结果如下:
注意事项:
1、除了以上的参数之外,还有参数如:textvariable 设置标签内容以变量形式显示 image 使标签以图片的方式呈现
2、relief=RAISED 表示标签的边框为凸起,也可设置为FLAT,SOLID,SUNKEN,RIDGE,GROOVE 若不设置默认为FLAT
3、font中的参数可以随便写在哪个位置,程序会自动识别(只要没写错),Arial代表字体,bold代表粗体,italic代表斜体
pack()方法
pack()方法只能把组件放在窗口的上下左右四个位置,默认是上面(TOP)
label.pack(side=BUTTOM, padx=10, pady=5) 表示打包在窗口下面,设置组件与其他边界之间的x方向间距为10个像素点,y方向间距为5个像素点。
label2 = Label(window, width=20, height=2, text="This is second label", relief=RAISED)
label3 = Label(window, width=20, height=2, text="Third label", relief=RAISED)
label2.pack(side=BOTTOM, padx=10, pady=5)
label3.pack(side=BOTTOM)
加入以上代码后运行结果如下
注意:在打包label3的时候会自动堆叠在label2的上面
grid()方法
这是一种以网格状(类似于Excel电子表格)包装和定位的窗口组件的方法,概念是使用row(行)和column(列)参数。
from tkinter import * # 从tkinter库中直接导入所有的方法
window = Tk() # 实例化一个窗口对象,从现在开始,window就是一个窗口了,以后我们可以对其进行设置
window.title("This is Tk") # 设置window的标题
window.geometry('600x400+100+100') # 设置window的大小,geometry:几何形状
window.maxsize(width=800, height=600) # 将窗口最大化
label = Label(window, # 所有组件的第一个参数都是父窗口,父窗口指的是该组件建立在父窗口之上
width=20, # 宽度设置为20个字符
height=2, # 高度设置为2个字符
text="This is a label", # 设置标签显示的文本内容
bg='lightyellow', # 设置背景 backgroud 为亮黄色
fg='red', # 设置字体 foreground 颜色为红色
font="Arial 12 bold italic", # 设置字体的参数,有字体、字号、样式
relief=RAISED, # 设置标签的外框样式为凸起
)
label.grid(row=1, column=1) # 已经设置好了label的参数,就需要将其pack打包放到window窗口上
label4 = Label(window, width=20, height=2, text="This is second label", relief=RAISED)
label5 = Label(window, width=20, height=2, text="Third label", relief=RAISED)
label4.grid(row=2, column=2)
label5.grid(row=3, column=1)
label6 = Label(window, width=25, height=2, text="I'll show you columnspan", relief=RAISED)
label7 = Label(window, width=20, height=2, text="This is rowspan", relief=RAISED)
label6.grid(row=4, column=1, columnspan=2) # 设定在column方向的合并数量
label7.grid(row=1, column=3, rowspan=2) # 设定在row方向的合并数量
window.mainloop() # loop:循环 将窗口不断地循环刷新以保持窗口存在直到手动关闭
运行结果如下:
注意:在代码中使用了pack()来包装组件之后,不可以再用grid(),因为会导致冲突
row和column都可以从0开始为第一行或者第一列,就正常人习惯1就是第一行,在此我就使用起始索引为1
Button按钮组件及参数
按钮用于设计单机功能按钮的时候执行某一个特定的动作。
from tkinter import * # 从tkinter库中直接导入所有的方法
window = Tk() # 实例化一个窗口对象,从现在开始,window就是一个窗口了,以后我们可以对其进行设置
window.title("This is Tk") # 设置window的标题
window.maxsize(width=800, height=600) # 设置窗口拖动能达到的最大尺寸
window.config(bg="white") # 设置窗口背景颜色为红色
def msgshow():
label['text'] = "I love python" # 可以通过类似字典的方法来对组件的属性进行更改
label['bg'] = 'lightyellow'
label['fg'] = 'blue'
botton = Button(window, text="Message", width=15, command=msgshow) # 注意最后一个参数command
button2 = Button(window,text="Exit", width=15, command=window.destroy) # window.destroy代表关闭窗口
label = Label(window)
label.pack()
botton.pack(side=LEFT)
button2.pack(side=RIGHT)
window.mainloop() # loop:循环 将窗口不断地循环刷新以保持窗口存在直到手动关闭
按钮和标签的差距不大,按钮仅仅提供了一个点击以触发命令的功能
在按钮command参数中,可以使用lambda表达式,如
def setcolor(color):
window.config(bg=color)
button1 = Button(window, text="Change Blue", width=15, command=lambda:setcolor("blue"))
button2 = Button(window, text="Change Red", width=15, command=lambda:setcolor("red"))
button1.pack()
button2.pack()
运行代码如下:
注意事项:
command中的函数只能是函数名,不可带括号
变量类型
from tkinter import *
def kick():
global kick_on # 声明kick_on为全局变量
if kick_on == False:
kick_on = True
var.set("This is tkinter") # 为字符串变量赋值的方法
else:
kick_on = False
var.set("")
window = Tk()
window.title("The type of variable")
var = StringVar() # 定义一个字符串变量
label = Label(window, textvariable=var,width=25, height=2, bg='lightyellow', fg='black', font="Verdana 16 bold")
label.pack()
kick_on = False
button = Button(window, text="Kick me", command=kick)
button.pack()
window.mainloop()
运行代码结果如下:
代码说明:当反复点击Kick_me的时候,会反复显示/隐藏标签内容
变量类型有:1、整数变量: x = IntVar() 默认是0
2、浮点数变量: x = DoubleVar() 默认是0.0
3、字符串变量:x = StringVar() 默认是""
4、布尔值变量:x = BooleanVar() Ture是1,False是0
使用get()方法获得变量内容,set()方法设定变量内容
Entry文本输入框及参数
Entry文本输入框用于让用户输入内容
from tkinter import *
window = Tk()
window.title("Entry")
# 因为在后续代码不需要使用到label1的其他功能,所以直接在此打包
label1 = Label(window, text="Account").grid(row=1, column=1)
label2 = Label(window, text="Password").grid(row=2, column=1)
e1 = Entry(window)
e2 = Entry(window,show="*")
# 给文本输入框插入内容,通常用于设置默认值如记住密码时
e1.insert(0,"This is ")
e2.insert(0,"tkinter")
e1.grid(row=1, column=2)
e2.grid(row=2, column=2)
def printInfo():
print(f"Account:{e1.get()}\nPassword:{e2.get()}")
button1 = Button(window, text='Print Info', command=printInfo)
button2 = Button(window, text='Exit', command=window.destroy)
button1.grid(row=3, column=1, padx=10, pady=10) # 当设置一个组件的xy间隙之后,该行该列的组件都会变动
button2.grid(row=3, column=2)
window.mainloop()
代码运行结果如下:
代码说明:1、已经默认记住账号密码,程序已经自动填入
2、按下Print Info按钮会打印输出账号和密码,并且清空两个文本框的内容
Text文字区域及参数(滚动条)
Text文字区域攻来让用户输入内容,相当于记事本 ,当然也可以用get()方法获取内容
from tkinter import *
window = Tk()
window.title("Text and Scrollbar")
text = Text(window, width=30, height=6)
scrollbar = Scrollbar(window)
# fill表示组件的填充方式,可选X,Y,BOTH,表示在某一个方向上填充父窗口
scrollbar.pack(side=RIGHT, fill=Y)
# expand=True 表示该组件有资格填充窗口
text.pack(expand=True, side=LEFT, fill=BOTH)
# 设置滚动条滚动的时候触发的命令,在此是将文本框(Text)的纵向滚动视图函数(yview)与滚动条(Scrollbar)的操作(command)关联起来。
scrollbar.config(command=text.yview)
# 将文本区域与滚动条链接,当在文本区域显示的文本变化时,滚动条也会同时变化以确保滚动条划到底的时候文本区域也到底
text.config(yscrollcommand=scrollbar.set)
def getcontent():
print(text.get(1.0,END)) # 1.0代表的是第一行第0列
button1 = Button(window, text='Get content', command=getcontent)
button1.pack(pady=2)
entry = Entry(window)
entry.pack(pady=5)
def insert():
content = entry.get() # 获取文本输入框的内容
text.insert(END, content) # 在text的末尾位置插入内容
entry.delete(0,END) # 已经获取了内容,清空输入框
button2 = Button(window, text='Insert',command=insert)
button2.pack()
window.mainloop()
代码运行如下:
这个内容新的内容比较多,是一个难点,下面是详细的解读:
1、fill 指的是组件的填充方式,可选X,Y,BOTH,如BOTH表示组件在X和Y方向填充可用的空间
2、expand 指的是组件是否有资格扩张自身大小
3、expand = True 配合 fill=BOTH 才可以实现窗口缩放时文本区域自动填充整个父窗口,要先有资格才可以填充.
4、scrollbar.config(command=text.yview) 设置滚动条滚动的时候触发的命令:当滚动滚动条的时候调用文本的纵向滚动视图函数。
5、text.config(yscrollcommand=scrollbar.set) 设置当滚动鼠标滚轮的时候触发的命令:当滚动鼠标滚轮时,调整滚动条的位置,以保证滚动条的位置显示的是当前文本内容
Radiobutton单选按钮
单选按钮的由来是无线电的按钮,在收音机时代可以用无线电的按钮选择特定频道。
from tkinter import *
window = Tk()
window.title("Radiobutton")
var = StringVar() # 创建一个字符串变量以在Radiobutton中使用
var.set(" ") # 先设置var的值,否则打开窗口后会显示所有单选框都选上了
List = ["东京", '北京', '巴黎', '纽约', '香港']
label = Label(window, text="未选择", width=30, height=1, bg='lightyellow', fg='black')
label.pack()
def printInfo():
label['text'] = f"你选择了{var.get()}" # 获取变量中的内容
for city in List:
Radiobutton(window,
text=city,
variable=var, value=city,
command=printInfo
).pack()
window.mainloop()
运行结果如下:
注意事项:
1、当单选框较多时,使用列表来创建单选框更为迅速
2、切记var是一个变量,要获得内容必须要使用get()方法,设定内容用set()
3、Radiobutton中的variable,value两个参数是连着用的,指的是指定了变量是哪一个后,当用户选择了当前选项后会把value的值赋给该变量
Checkbutton多选框
from tkinter import *
window = Tk()
window.title("Checkbutton")
label = Label(window, width=30, text="请选择你喜欢的运动", bg='lightyellow', fg='black')
label.pack(pady=5)
List = ['足球','篮球','羽毛球','乒乓球','台球','橄榄球','游泳','滑板']
sports = []
def sport(param): # 当元素在列表中时,删除该元素,若不在则添加进列表
if param not in sports:
sports.append(param)
else:
sports.remove(param)
for every in List:
Checkbutton(window,
text=every,
command=lambda every = every: sport(every) # 将循环变量作为默认参数传递给lambda表达式
).pack()
def printInfo():
label['text'] = "你选择了" + ' '.join(str(i) for i in sports)
button = Button(window, text="确定", width=20, command=printInfo)
button.pack()
window.mainloop()
注意事项:
当在循环内部创建lambda
函数并希望它能够记住循环变量的当前值时,常常会遇到所谓的“延迟绑定”(late binding)问题。因为lambda
函数在定义时并不会立即执行,而是在被调用时才执行,所以它会捕获到循环变量的最后一个值,而不是循环迭代时的当前值。
为了避免这个问题,可以在lambda
函数中引入默认参数来创建一个新的局部变量,该局部变量会在每次循环迭代时被立即绑定到当前的循环变量值。这样,当lambda
函数在后续被调用时,它会使用这个已经绑定的局部变量值,而不是循环变量的最后一个值。
Listbox选项框
from tkinter import *
window = Tk()
window.title("Listbox")
window.maxsize(width=600, height=400)
scrollbar = Scrollbar(window) # 实例化一个滚动条对象
scrollbar.pack(side=RIGHT, fill=Y) # 将滚动条组件打包放在窗口右边,并且在垂直方向填充
# 实例化Listbox对象, 选择模式为多选 对齐方式为居中
listbox = Listbox(window, width=30, selectmode=MULTIPLE, justify=CENTER)
for i,item in enumerate(range(1,51)):
# listbox中要设定内容需要使用索引和元素,索引从0开始,每个代表一行,所以要使用enumerate(枚举)
listbox.insert(i, item)
listbox.pack()
scrollbar.config(command=listbox.yview) # 设置当滚动条被拉动时触发文本的纵向滚动视图函数
listbox.config(yscrollcommand=scrollbar.set) # 设置当鼠标滚轮滚动文本内容时同步设置滚动条的状态
def insert():
selected_index = listbox.curselection() # 获得当前已选选项的索引,返回的是元组
selected_items = [listbox.get(i) for i in selected_index] # 使用get(index)方法来获取listbox中特定索引的内容
for i,item in enumerate(selected_items):
listbox.insert(END, item) # 在末尾插入当前选中的选项内容
def delete():
selected_index = listbox.curselection() # 获得当前已选选项的索引,返回的是元组
print(selected_index)
for i in reversed(selected_index): # 重点内容
listbox.delete(i)
button1 = Button(window, width=14, text="Insert", command=insert)
button2 = Button(window, width=14, text="Delete", command=delete)
button1.pack()
button2.pack()
window.mainloop()
代码运行效果:
注意事项:
1、在每一个组件中都有expand(扩张)、justify(对齐)参数.
justify的值可以是CENTER,LEFT,RIGHT,默认是LEFT。
2、Listbox中没有提供直接获取已选选项的内容的功能,所以要先使用curselection()来获取一个包含已选选项的索引的元组,然后再用循环遍历和get(index)方法获得已选选项的文本内容。
3、for i in reversed(selected_index) 为什么要用reversed?
解答:如果不用reversed,比如先删除索引为10的选项,那么删除了之后列表框的索引会减少一个,此时你删除的索引为11的选项是你想要删除的下一个选项的再下一个。所以用reversed先删除索引靠后的选项,这样子才能够正确删除所有已选的选项。
Combobox下拉选项框
from tkinter import *
# Combobox在tkinter包中的ttk库中
from tkinter.ttk import Combobox
window = Tk()
window.title("Combobox")
window.maxsize(width=600, height=400)
def on_combobox_select(event):
selected_item = combobox.get() # 获得当前选项文本
print(f"你选择了选项: {selected_item}")
# 用列表中的每一个元素来表示选项
combobox = Combobox(window, width=30, height=2, values=['选项1','选项2','选项3'])
combobox.current(0) # 设置默认值为第一个选项
combobox.pack()
# 当用户选择 Combobox 中的某个选项时,会触发 on_combobox_select 函数,并打印所选的选项。
combobox.bind("<<ComboboxSelected>>", on_combobox_select)
window.mainloop()
代码运行结果如下:
注意事项:
1、Combobox位于tkinter中的ttk库中,需要导入
2、Combobox使用value=[ ] 参数来进行选项的设置,每个元素代表一个选项
3、combobox.current(index) 设置默认选项
4、combobox.bind("<<ComboboxSelected>>", on_combobox_select) 此代码效果见代码注释,其他的事件可参考python官方文档tkinter.ttk --- Tk 风格的控件 — Python 3.12.2 文档,我也不会。
Listbox、Text、Entry区别
get()方法的区别:
-
Listbox:
get(first, last=None)
: 获取列表框中指定范围内的元素。 若没有last,则取得该索引的内容first
: 起始索引,通常是一个整数或字符串,表示要获取的元素的起始位置。last
: 结束索引,可选参数,通常是一个整数或字符串,表示要获取的元素的结束位置。如果不指定,则默认为起始索引的下一个元素。
-
Text:
get(start, end=None)
: 获取文本框中指定范围内的文本。start
: 起始索引,必须要写为类似1.0的形式表示第一行第0列即开头end
: 结束索引,可选参数,通常是一个字符串,表示要获取的文本的结束位置。如果不指定,则默认为文本框的末尾。
-
Entry:
get()
: 获取单行文本输入框中的文本内容。
insert()方法的区别:
-
Listbox:
insert(index, *elements)
: 向Listbox
中的指定索引位置插入一个或多个元素。
比如使用insert(END,elements) 表示在末尾插入内容index
: 要插入元素的索引位置,通常是一个整数。elements
: 要插入的元素,可以是单个元素或一个元素的列表。
-
Text:
insert(index, chars, *tags)
: 在指定的索引位置插入文本。
比如 text.insert(END, content) 表示在文本的末尾插入内容index
: 要插入文本的索引位置,通常是一个字符串,如"1.0"
表示第一行第一个字符。chars
: 要插入的文本内容。tags
: 可选参数,用于给插入的文本应用标签,以便后续对其进行格式化或管理。
-
Entry:
insert(index, string)
: 向Entry
中的指定索引位置插入字符串。
比如entry.insert(END,content) 表示在文本输入框的末尾插入内容index
: 要插入字符串的索引位置,通常是一个整数。string
: 要插入的字符串。
delete()方法的区别:
-
Listbox:
delete(first, last=None)
: 删除列表框中指定范围内的元素。
通常使用于删除特定索引的标签如 listbox.delete(5) 表示删除第六个标签first
: 起始索引,通常是一个整数,表示要删除的元素的起始位置。last
: 结束索引,可选参数,通常是一个整数,表示要删除的元素的结束位置。如果不指定,则默认为起始索引的下一个元素。
-
Text:
delete(start, end=None)
: 删除文本框中指定范围内的文本。
比如text.delete(1.0,END) 表示清空整个文本框start
: 起始索引,通常是一个字符串,如"1.0"
表示第一行第一个字符。end
: 结束索引,可选参数,通常是一个字符串,表示要删除的文本的结束位置。如果不指定,则默认为文本框的末尾。
-
Entry:
delete(first, last=None)
: 删除单行文本输入框中指定范围内的文本。
比如entry.delete(0,END) 表示清空整个文本输入框first
: 起始索引,通常是一个整数,表示要删除的文本的起始位置。last
: 结束索引,可选参数,通常是一个整数,表示要删除的文本的结束位置。如果不指定,则默认为起始索引的下一个字符。
总结:
可以发现只有Text中的所有方法第一个start参数都必须是类似于1.0表示第一行第一个字符的形似,Listbox和Entry都只需要输入整数索引即可。
Scale尺度及参数
from tkinter import *
window = Tk()
window.title("Text and Scrollbar")
var = StringVar()
label = Label(window, textvariable=var, width=30, bg='lightyellow', fg='black')
label.pack()
def printInfo(value):
var.set(f"你当前的位置是{value}")
# 设置起点为0,终点为100,方向为水平方向,滑块长度为100,滑动时触发的命令为printInfo(会自动传递当前滑块值作为参数)
scale = Scale(window, from_=0, to=100, orient=HORIZONTAL, length=100, command=printInfo) # 左边是0,右边是100
scale.set(5) # 设置滑块的值
scale.pack()
var2 = StringVar()
def secondinfo(value):
var2.set(scale2.get()) # 使用get方法获得scale2当前的值
scale2 = Scale(window,from_=10,to=0,length=100,orient=VERTICAL,command=secondinfo) # 上面是10,下面是0,设置方向为垂直方向
scale2.pack()
label2 = Label(window,textvariable=var2).pack()
window.mainloop()
代码运行结果如下:
注意事项:
Scale有几个参数:from_ 起点 to 终点 orient 方向 length 长度
两个方法:get() 获取当前滑块的值 set() 设定当前滑块的值
Menu菜单及其参数(弹窗)
菜单的功能有2点:
1、点击一个菜单展开另一群菜单以使用其他功能
2、点击菜单上的某按钮以触发命令
from tkinter import *
from tkinter import messagebox # 从tkinter中导入messagebox库,以便使用弹窗功能
window = Tk()
window.title("Combobox")
window.maxsize(width=600, height=400)
def newfile():
# 展示消息,第一个参数为标题,第二个为弹窗的内容
messagebox.showinfo("打开新文件",'可在此编写打开新文件程序代码')
def savefile():
messagebox.showinfo("保存文件",'可在此编写保存文件的程序代码')
def about():
messagebox.showinfo("程序说明",'Today is 2024.2.10')
def callback():
print("已被调用")
menu = Menu(window, tearoff=0) # 实例化一个Menu对象,并且设置tearoff参数为0的值,即为关闭,详细见解读
# menu.pack() 无法打包menu,因为Menu菜单只能处在窗口的上方位置不可更改,所以不可pack
filemenu = Menu(menu, tearoff=0) # 实例化一个Menu对象,父窗口是menu,即filemenu处在menu之上
# 若一个菜单中有子菜单,则需要使用add_cascade()方法
menu.add_cascade(label='File', menu=filemenu) # 子菜单名称为File,此子菜单的对象是filemenu
menu.add_command(label='About', command=about) # 添加一个命令,命令的名称是About,点击该命令会触发About函数
menu.add_command(label='Exit', command=window.destroy)
window.config(menu=menu) # 设置window的配置,使menu参数的值为menu
filemenu.add_command(label='New', command=newfile) # 点开File后,会显示子菜单filemenu的内容
filemenu.add_separator() # 增加分隔线
filemenu.add_command(label='Save', command=savefile)
filemenu.add_separator()
filemenu.add_command(label='About', command=about)
window.mainloop()
注意事项
1、tearoff 参数:Tkinter 中,tearoff
是菜单小部件(Menu Widget)的一个选项,用于指定是否允许用户拖动菜单,将其从主窗口中分离出来成为一个独立的窗口。默认情况下,大多数操作系统的菜单都支持这一功能,因此在菜单中会显示一个小的虚线标记,用户可以拖动以打开一个独立的窗口。就开发者而言,其实不希望用户拖动打开新的菜单,因为这样子不便于美观和使用,所以我个人建议设置为0即False。
2、相关方法有:
(1)add_cascade() 建立分层菜单,同时让子功能项目与父菜单建立链接
(2)add_command() 增加菜单项
(3)add_separator() 增加分隔线