🐸 青蛙编程网
首页 / Python / 桌面客户端 / 6. 菜单、对话框与多窗口

6. 菜单、对话框与多窗口

这是什么:菜单条(窗口顶部的 文件/编辑/帮助)、对话框(弹出要你确认或填东西的小窗)、多窗口(Toplevel 附属窗口)——让自制程序"像个正经软件"的三件套。

为什么用:第 5 章结束,你的程序已经有交互了,但操作全靠把按钮摆在脸上——功能一多界面就挤爆。菜单把功能收纳进顶栏,对话框负责"要输入、要确认、要选文件",Toplevel 负责"关于本程序"——这三样用熟,程序立刻从"玩具"升级成"工具"。

如何用:本章做一个迷你记事本,三件套全用上。

菜单条:顶栏收纳功能

import tkinter as tk

def say_hello():
    print("hello")            # 控制台里看效果(窗口程序没控制台也行)

root = tk.Tk()
root.title("菜单演示")
root.geometry("300x200")

menubar = tk.Menu(root)
filemenu = tk.Menu(menubar, tearoff=0)
filemenu.add_command(label="新建", command=say_hello)
filemenu.add_command(label="退出", command=root.quit)
filemenu.add_separator()
menubar.add_cascade(label="文件", menu=filemenu)

helpmenu = tk.Menu(menubar, tearoff=0)
helpmenu.add_command(label="关于",
                     command=lambda: print("迷你记事本 v1.0"))
menubar.add_cascade(label="帮助", menu=helpmenu)

root.config(menu=menubar)
root.mainloop()

你会看到:窗口顶部出现"文件 / 帮助"两个菜单,点开文件有"新建 —— 退出"(中间带分隔线)。

代码 作用
tk.Menu(...) 建菜单;挂在 root 上的是菜单条本身,加进菜单条的是子菜单
add_command(label=..., command=...) 加一项,点击执行 command
add_cascade(label=..., menu=...) 把子菜单挂到菜单条上
tearoff=0 关掉子菜单顶部那条可"撕下"的小虚线(现代界面都不要它)
root.quit 结束事件循环;关窗口用 root.destroy,日常差别极小

三种对话框:开箱即用

messagebox 问话、filedialog 选文件、simpledialog 要输入:

import tkinter as tk
from tkinter import messagebox, filedialog, simpledialog

root = tk.Tk()
root.withdraw()                      # 先藏起主窗口,让对话框直接弹出

messagebox.showinfo("提示", "你好,我是消息框")
answer = messagebox.askyesno("确认", "要删除这条记录吗?")   # 返回 True/False
name = simpledialog.askstring("输入", "你叫什么名字?")      # 取消返回 None
path = filedialog.askopenfilename(                          # 取消返回 ""
    title="选一个文件",
    filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")])

root.deiconify()                     # 把主窗口重新显示出来
root.mainloop()

你会看到:连着弹四个系统风格的小窗——提示、是否确认(点"否"试试)、输入名字、文件选择器。

函数 用途 返回值
showinfo / showwarning / showerror 通知一句话 None
askyesno / askokcancel 要用户拍板 True / False
askstring / askinteger / askfloat 要一个输入 内容,取消为 None
askopenfilename / asksaveasfilename 要一个路径 路径字符串,取消为空串

取消不是失败,是常态:对话框的返回值先判空/判 False 再继续,否则 open("") 直接崩——本章练习埋了这个雷。

多窗口:Toplevel

def open_about():
    about = tk.Toplevel(root)            # 附属窗口,独立可关
    about.title("关于")
    about.geometry("220x120")
    tk.Label(about, text="迷你记事本 v1.0\n© 2026 青蛙编程网").pack(expand=True)

tk.Toplevel(root) 再建一个窗口,有自己的标题栏和关闭按钮;主窗关了就全体退出。"关于"面板、独立设置窗用它;别拿它当"新页面"把主窗关掉——新手最爱把 root 关光再 tk.Tk() 重开,那是重启程序,不是切页面。

迷你记事本 v1.0:三件套合体

import tkinter as tk
from tkinter import filedialog, messagebox

root = tk.Tk()
root.title("迷你记事本")
root.geometry("480x320")

editor = tk.Text(root)
editor.pack(fill="both", expand=True)

def new_file():
    if messagebox.askyesno("新建", "当前内容未保存,确定清空?"):
        editor.delete("1.0", "end")

def open_file():
    path = filedialog.askopenfilename(filetypes=[("文本", "*.txt")])
    if not path:
        return                       # 用户点了取消
    try:
        with open(path, encoding="utf-8") as f:
            content = f.read()
    except OSError as e:
        messagebox.showerror("出错", f"读不了:{e}")
        return
    editor.delete("1.0", "end")
    editor.insert("1.0", content)

def save_file():
    path = filedialog.asksaveasfilename(defaultextension=".txt")
    if not path:
        return
    with open(path, "w", encoding="utf-8") as f:
        f.write(editor.get("1.0", "end-1c"))   # end-1c:别把末尾换行读进去
    messagebox.showinfo("保存", "已存好!")

menu = tk.Menu(root)
fm = tk.Menu(menu, tearoff=0)
fm.add_command(label="新建", command=new_file)
fm.add_command(label="打开", command=open_file)
fm.add_command(label="保存", command=save_file, accelerator="Ctrl+S")
fm.add_separator()
fm.add_command(label="退出", command=root.quit)
menu.add_cascade(label="文件", menu=fm)
root.config(menu=menu)
root.bind("<Control-s>", lambda e: save_file())

root.mainloop()

你会看到:一个能用的记事本——能打字、菜单新建/打开/保存(弹标准文件对话框)、Ctrl+S 快捷键保存、退出前会问一句(自行加的 askyesno)。

editor.get("1.0", "end-1c") 里的 "1.0" / "end" / "end-1c"(末尾回退一个字符)是 Text 控件的"位置语法":行号.列号,第 1 章 Text 那行已预告。

记忆口诀:menu 管顶栏收纳,dialog 管要话要文件,Toplevel 管分身;见空就 return,取消不崩溃。

动手改

  1. 给记事本加"另存为"(提示:和保存共用函数,只是菜单多一项)和编辑菜单——"全选"(editor.tag_add("sel", "1.0", "end") 是歪招,正规做法是 editor.event_generate("<<SelectAll>>"),试后者)
  2. 把窗口标题同步成文件名:打开/保存后 root.title(f"{path} - 迷你记事本")
  3. 挑战:窗口关闭时若有未保存内容就弹 askyesno 问"确定退出?"(提示:root.protocol("WM_DELETE_WINDOW", 你的函数))

下一章:打包成 exe:PyInstaller 🐸

📥 下载本章代码

ZIP 内含本章全部代码文件与说明,解压即用,与教程内容同步生成。