如何在Python tkinter计算器中实现左侧可切换侧边栏菜单
Tkinter计算器侧边切换菜单实现方案
前置依赖
你需要先安装Pillow库用来加载PNG图标,执行以下命令安装:
pip install pillow
实现步骤
- 新增图标加载逻辑:读取你准备的菜单开/关状态图标,适配按钮大小
- 新增侧边栏容器:用Frame组件作为侧边菜单的载体,默认隐藏在窗口左侧外
- 新增滑动动画逻辑:通过
place()方法动态修改侧边栏的x坐标,实现平滑滑入滑出效果 - 新增切换逻辑:点击菜单按钮时切换侧边栏状态,同步更换按钮图标
- 你可以自行在侧边栏中添加需要的功能(比如计算历史、主题切换、单位转换等)
修改后完整代码
import tkinter as tk from PIL import Image, ImageTk LABEL_COLOR = "#25265E" DEFAULT_FONT_STYLE = ("Arial", 20,"bold") OFF_WHITE = "#F8FAFF" class Calculator: def __init__(self): self.window = tk.Tk() self.window.geometry("375x660") self.window.resizable(height=True, width= True) self.window.title("Calculator") # 注意替换成你自己的ico文件路径,没有可以注释掉 # self.window.iconbitmap("c:/Users/sheran/Downloads/Calculator_Icon.ico") self.total_expression = "" self.current_expression = "" # 新增侧边栏相关配置 self.menu_open = False self.sidebar_width = 160 # 侧边栏宽度可自行调整 self.animation_speed = 6 # 动画速度可自行调整 self.display_frame = self.create_display_frame() # 新增菜单按钮 self.create_menu_button() # 新增侧边栏 self.create_sidebar() self.total_label, self.label =self.create_display_labels() self.digits = { 7:(1,1), 8:(1,2), 9:(1,3), 4:(2,1), 5:(2,2), 6:(2,3), 1:(3,1), 2:(3,2), 3:(3,3), 0:(4,2), ".":(4,1), } self.operations = {"/": "\u00F7", "*": "\u00D7","-": "-", "+": "+"} self.buttons_frame = self.create_buttons_frame() self.buttons_frame.rowconfigure(0, weight=1) for x in range(1,5): self.buttons_frame.rowconfigure(x, weight=1) self.buttons_frame.columnconfigure(x, weight=1) self.create_digit_buttons() self.create_operator_buttons() self.create_special_buttons() self.bind_keys() # 新增:创建菜单按钮 def create_menu_button(self): # 注意替换为你本地的图标路径 self.menu_close_img = ImageTk.PhotoImage(Image.open("sFxf6.png").resize((24,24))) self.menu_open_img = ImageTk.PhotoImage(Image.open("wDlbQ.png").resize((24,24))) self.menu_btn = tk.Button(self.display_frame, image=self.menu_close_img, bg="#f5f5f5", borderwidth=0, command=self.toggle_menu) self.menu_btn.place(x=12, y=15, anchor="nw") # 新增:创建侧边栏 def create_sidebar(self): self.sidebar = tk.Frame(self.window, bg="#ffffff", width=self.sidebar_width) # 初始隐藏在窗口左侧外 self.sidebar.place(x=-self.sidebar_width, y=0, relheight=1) # 侧边栏示例功能,可自行修改添加 tk.Label(self.sidebar, text="功能菜单", bg="#ffffff", font=("Arial",16,"bold"), pady=20).pack(fill="x") tk.Button(self.sidebar, text="计算历史", bg="#ffffff", borderwidth=0, font=("Arial",14), pady=10).pack(fill="x", padx=10) tk.Button(self.sidebar, text="切换主题", bg="#ffffff", borderwidth=0, font=("Arial",14), pady=10).pack(fill="x", padx=10) tk.Button(self.sidebar, text="单位转换", bg="#ffffff", borderwidth=0, font=("Arial",14), pady=10).pack(fill="x", padx=10) tk.Button(self.sidebar, text="退出程序", bg="#ffffff", borderwidth=0, font=("Arial",14), pady=10, command=self.window.quit).pack(fill="x", padx=10) # 新增:菜单切换动画 def toggle_menu(self): if not self.menu_open: # 滑入效果 def open_animate(): current_x = self.sidebar.winfo_x() if current_x < 0: self.sidebar.place(x=current_x + self.animation_speed) self.window.after(10, open_animate) else: self.menu_btn.config(image=self.menu_open_img) self.menu_open = True open_animate() else: # 滑出效果 def close_animate(): current_x = self.sidebar.winfo_x() if current_x > -self.sidebar_width: self.sidebar.place(x=current_x - self.animation_speed) self.window.after(10, close_animate) else: self.menu_btn.config(image=self.menu_close_img) self.menu_open = False close_animate() def bind_keys(self): self.window.bind("<Return>", lambda event: self.evaluate()) self.window.bind("<BackSpace>", lambda event: self.delete()) self.window.bind("<c>", lambda event: self.clear()) for key in self.digits: self.window.bind(str(key), lambda event, digits = key: self.add_to_expressions(digits)) for key in self.operations: self.window.bind(key, lambda event, operator = key: self.append_operator(operator)) def create_special_buttons(self): self.create_clear_button() self.create_equals_button() self.create_delete_button() self.create_square_button() self.create_sqrt_button() def create_display_labels(self): total_label=tk.Label(self.display_frame, text= self.total_expression, anchor=tk.E, bg="#f5f5f5", fg="#25265E", padx=24, font= ("Arial", 24)) total_label.pack(expand=True, fill="both") label=tk.Label(self.display_frame, text= self.current_expression, anchor=tk.E, bg="#f5f5f5", fg="#25265E", padx=24, font= ("Arial", 40, "bold")) label.pack(expand=True, fill="both") return total_label,label def create_display_frame(self): Frame = tk.Frame(self.window, height=221, bg="#f5f5f5") Frame.pack(expand=True, fill="both") return Frame def add_to_expressions(self, value): self.current_expression += str(value) self.update_label() def create_digit_buttons(self): def changedgt_on_hovering(event): event.widget['bg']='#F0F0F0' def returndgt_to_normalstate(event): event.widget['bg']='#fff' for digit,grid_value in self.digits.items(): Button = tk.Button(self.buttons_frame, text=str(digit), bg="#fff", fg="#25265E", font=("Arial", 24, "bold"), borderwidth=0, activebackground="#f0f0f0",activeforeground="#111237",command=lambda x=digit: self.add_to_expressions(x)) Button.grid(row=grid_value[0], column=grid_value[1], sticky=tk.NSEW) Button.bind("<Enter>", changedgt_on_hovering) Button.bind("<Leave>",returndgt_to_normalstate) def append_operator(self, operator): self.current_expression += operator self.total_expression += self.current_expression self.current_expression ="" self.update_total_label() self.update_label() def create_operator_buttons(self): i = 0 def changeop_on_hovering(event): event.widget['bg']='#E8EEFF' def returnop_to_normalstate(event): event.widget['bg']='#F8FAFF' for operator, symbol in self.operations.items(): button = tk.Button(self.buttons_frame, text=symbol, bg= OFF_WHITE, fg= LABEL_COLOR, font= DEFAULT_FONT_STYLE, borderwidth=0, activebackground="#e8eeff",activeforeground="#111237",command=lambda x=operator:self.append_operator(x)) button.grid(row=i, column=4, sticky=tk.NSEW) button.bind("<Enter>", changeop_on_hovering) button.bind("<Leave>", returnop_to_normalstate) i += 1 def clear(self): self.current_expression = "" self.total_expression = "" self.update_label() self.update_total_label() def create_clear_button(self): button = tk.Button(self.buttons_frame, text="C", bg= OFF_WHITE, fg= LABEL_COLOR, font= DEFAULT_FONT_STYLE, borderwidth=0, activebackground="#e8eeff",activeforeground="#111237",command=self.clear) button.grid(row=0, column=1, sticky=tk.NSEW) def changecl_on_hovering(event): button['bg']='#E8EEFF' def returncl_to_normalstate(event): button['bg']= "#F8FAFF" button.bind("<Enter>", changecl_on_hovering) button.bind('<Leave>',returncl_to_normalstate) def delete(self): self.current_expression = self.current_expression[:-1] self.update_label() def create_delete_button(self): button = tk.Button(self.buttons_frame, text="⌫", bg= "#fff", fg= "#25265E", font=("Arial", 20, "bold"), borderwidth=0, activebackground="#fff",command=self.delete) button.grid(row=4, column=3, sticky=tk.NSEW) def changedel_on_hovering(event): button['bg']='#F0F0F0' def returndel_to_normalstate(event): button['bg']='#fff' button.bind("<Enter>", changedel_on_hovering) button.bind('<Leave>',returndel_to_normalstate) def square(self): self.current_expression = str(eval(f"{self.current_expression}**2")) self.update_label() def create_square_button(self): button = tk.Button(self.buttons_frame, text="x\u00b2", bg= OFF_WHITE, fg= LABEL_COLOR, font= DEFAULT_FONT_STYLE, borderwidth=0, activebackground="#e8eeff",activeforeground="#111237",command=self.square) button.grid(row=0, column=2, sticky=tk.NSEW) def changesq_on_hovering(event): button['bg']='#E8EEFF' def returnsq_to_normalstate(event): button['bg']= "#F8FAFF" button.bind("<Enter>", changesq_on_hovering) button.bind('<Leave>',returnsq_to_normalstate) def sqrt(self): self.current_expression = str(eval(f"{self.current_expression}**0.5")) self.update_label() def create_sqrt_button(self): button = tk.Button(self.buttons_frame, text="\u221ax", bg= OFF_WHITE, fg= LABEL_COLOR, font= DEFAULT_FONT_STYLE, borderwidth=0, activebackground="#e8eeff",activeforeground="#111237",command=self.sqrt) button.grid(row=0, column=3, sticky=tk.NSEW) def changesqrt_on_hovering(event): button['bg']='#E8EEFF' def returnsqrt_to_normalstate(event): button['bg']= "#F8FAFF" button.bind("<Enter>", changesqrt_on_hovering) button.bind('<Leave>',returnsqrt_to_normalstate) def evaluate(self): self.total_expression += self.current_expression self.update_total_label() try: self.current_expression = str(eval(self.total_expression)) self.total_expression = "" except Exception as e: self.current_expression = ("Error") finally: self.update_label() def create_equals_button(self): button = tk.Button(self.buttons_frame, text="=", bg= "#CCEDFF", fg= LABEL_COLOR, font= DEFAULT_FONT_STYLE, borderwidth=0, activebackground="#B8E6FF",activeforeground="#111237",command=self.evaluate) button.grid(row=4, column=4, columnspan=2, sticky=tk.NSEW) def changeeql_on_hovering(event): button['bg']="#B8E6FF" def returneql_to_normalstate(event): button['bg']= "#CCEDFF" button.bind("<Enter>", changeeql_on_hovering) button.bind('<Leave>',returneql_to_normalstate) def create_buttons_frame(self): Frame= tk.Frame(self.window) Frame.pack(expand=True, fill="both") return Frame def update_total_label(self): expression = self.total_expression for operator, symbol in self.operations.items(): expression = expression.replace(operator, f' {symbol}') self.total_label.config(text=expression) def update_label(self): self.label.config(text=self.current_expression[:12]) def run(self): self.window.mainloop() Calculator().run()
注意事项
代码中加载图标的路径需要替换为你下载到本地的菜单图标实际存储路径,否则会报错。你可以自行修改侧边栏的宽度、背景色、功能按钮等配置来匹配你的设计需求。
内容的提问来源于stack exchange,提问作者Sheran Fernando
相关产品推荐
相关产品推荐

