Tkinter二级联动Combobox调用get()返回空值引发KeyError问题
Tkinter Combobox联动后取值为空导致KeyError的问题解决
问题描述
- 开发Python Tkinter GUI程序,实现第一个Combobox(Subject)选择后联动更新第二个Combobox(Students)的选项
- 联动显示功能正常,但调用
Students.input.get()返回空值 - 点击“Load Student Report”按钮触发
Load_Report方法时,因self.name为空字符串,执行for Assignment in student_repository[self.name].get_Assignments()时抛出KeyError
错误原因
- Report实例初始化时机错误:按钮的
command参数设置为Report().Load_Report,程序启动时就会创建Report实例,此时用户还未选择学生,self.name被初始化为空字符串,后续点击按钮调用Load_Report时使用的仍是这个空值 - Report_Frame初始化错误:
self.Report_Frame = tk.Frame(root).grid()会将grid()的返回值(None)赋值给Report_Frame,后续用它作为Label的父组件会引发异常 - 取值逻辑滞后:在Report的
__init__中获取学生和科目值,无法实时获取用户点击按钮时的最新选中状态
修复方案
- 按钮点击时再实时获取选中值,不在程序启动时提前初始化Report实例
- 调整Report类的
Load_Report方法,在方法内实时获取当前的学生和科目选择 - 修正Report_Frame的初始化方式,先创建Frame对象再调用
grid() - 优先使用绑定的
StringVar获取值,确保与Combobox选中状态同步
修复后的完整代码
import tkinter as tk from tkinter import ttk root = tk.Tk() root.title("Gradebook +") root.columnconfigure(0, weight=1) # 确保窗口缩放时表单居中 variables = dict() class Pupil(): def __init__(self, name): self.name = name self.Assignments = {"History Final":{"Weight":5, "Grade":75}, "Washington Essay":{"Weight":2, "Grade":65}} self.GPA = "0" def loadaverage(self): return self.GPA def get_Assignments(self): return self.Assignments class LabelInput(tk.Frame): def __init__( self, parent, label, inp_cls, inp_args, *args, **kwargs ): super().__init__(parent, *args, **kwargs) self.label = tk.Label(self, text=label, anchor="w") self.input = inp_cls(self, **inp_args) self.columnconfigure(1, weight=1) self.label.grid(sticky=tk.E + tk.W) self.input.grid(row=0, column=1, sticky=tk.E + tk.W) student_directory = {"Honors Biology": ["John", "Bob"], "Nuclear Physics":["Simon", "Fred", "Larry"]} student_repository = {} for i in student_directory.values(): for l in i: student_repository[l] = Pupil(l) variables["Student"] = tk.StringVar() variables["Subject"] = tk.StringVar() def Action(e): selected_subject = Subject.input.get() Students.input.config(value=student_directory[selected_subject]) Students.input.current(0) # 同步更新StringVar的值 variables["Student"].set(student_directory[selected_subject][0]) class Report(): def __init__(self): self.Report_Frame = tk.Frame(root) self.Report_Frame.grid() def Load_Report(self): # 实时获取当前选中的学生和科目 self.name = variables["Student"].get() self.subject = variables["Subject"].get() if not self.name: # 处理未选择学生的情况 tk.Label(self.Report_Frame, text="请先选择学生").grid() return details = "" for Assignment in student_repository[self.name].get_Assignments(): details += "\n" + Assignment + " Score: " + str(student_repository[self.name].get_Assignments()[Assignment]["Grade"]) # 修正属性名:GPA而非Grade report_text = student_repository[self.name].GPA + "\n" + details # 先清空之前的报告内容 for widget in self.Report_Frame.winfo_children(): widget.destroy() Subject_Report = tk.Label(self.Report_Frame, text=report_text) Subject_Report.grid() # 创建组件 Subject = LabelInput(root, "Select Subject: ", ttk.Combobox, {"textvariable":variables["Subject"], "value":list(student_directory.keys())}) Subject.input.bind("<<ComboboxSelected>>", Action) Students = LabelInput(root, "Select Student: ", ttk.Combobox, {"textvariable":variables["Student"], "value":[]}) Subject.grid() Students.grid() # 按钮点击时再创建Report实例并调用方法 Load_Button = tk.Button(root, text="Load Student Report", command=lambda: Report().Load_Report()) Load_Button.grid() root.mainloop()
关键修改点说明
- 按钮
command改为lambda: Report().Load_Report(),确保点击时才创建Report实例并执行方法 - 在
Action函数中添加variables["Student"].set(),同步更新绑定的StringVar值 Report类的Load_Report方法内实时获取variables["Student"].get(),确保拿到最新选中值- 修正了
student_repository[self.name].Grade的错误,改为GPA(对应Pupil类的属性) - 添加了未选择学生时的异常处理,避免KeyError
- 每次加载报告前清空Report_Frame内的旧组件,避免重复显示
内容的提问来源于stack exchange,提问作者Samuel Kostousov
相关产品推荐
相关产品推荐

