Tkinter递归函数compare_stages()调用触发NoneType报错问题
问题根因
报错由三个代码逻辑错误直接导致:
- 递归调用无返回值:
compare_stages()、compare_doc_lists()中触发递归重选逻辑时,仅调用了递归函数本身,未加return关键字透传返回值,导致进入递归分支时最外层调用拿到的返回值为默认的None,后续访问stage[0]时就会触发NoneType下标访问错误。 - Tk多实例冲突:所有选择、提示逻辑都新建独立的
Tk()根窗口,旧窗口未销毁,会导致Tkinter事件循环混乱、变量取值异常。 - 选择函数取值逻辑错误:
select_doc/select_stage/select_reviewer中绑定在OptionMenu上的save_selected回调仅在点击下拉选项时触发,mainloop()结束后手动传入StringVar对象调用该函数,本身无法拿到正确的用户选择值。
修复方案
- 所有递归调用前加
return,把内层递归的合法返回值逐层透传到最外层调用 - 全局只保留一个
Tk()根窗口,所有弹窗用Toplevel实现,用完及时销毁,避免多实例冲突 - 修正选择类函数的取值逻辑,增加确认按钮,用
wait_window()等用户操作完成、窗口销毁后再读取选择值 - 审核人已完成阶段用字典统一存储,替换多组独立列表+重复if判断的冗余写法
- 抽离通用提示弹窗函数,减少重复代码
修正后可运行代码
from tkinter import * # 基础配置数据 doc_list = ["doc_A","doc_B","doc_C","doc_D"] review_stages = ["internal_review","client_review","reg_review"] reviewer_list = ["Sue","Joe","Don","Billy"] doc_reviews_completed = ["doc_B"] review_stages_completed = ["internal_review"] # 字典存储审核人已完成阶段,替代多个独立列表 reviewer_completed_map = { "Sue": ["client_review"], "Joe": [], "Don": [], "Billy": [] } def show_tip(msg): """通用提示弹窗""" top = Toplevel() top.geometry("250x170") Label(top, text="Oops!", font=("Courier",14)).pack() txt = Text(top, height=5, width=40) txt.pack() txt.insert(END, msg) Button(top, text="确定", command=top.destroy).pack() top.wait_window() # 等待弹窗关闭后再执行后续逻辑 def select_doc(root): """选择文档,基于主窗口生成子弹窗""" select_win = Toplevel(root) select_win.geometry("715x250") select_win.grab_set() # 模态窗口,必须完成当前选择才能操作其他窗口 selected = StringVar(value="Select Document") def on_confirm(): select_win.destroy() OptionMenu(select_win, selected, *doc_list).pack() Button(select_win, text="确认选择", command=on_confirm).pack() select_win.wait_window() return selected.get() def select_reviewer(root): """选择审核人""" select_win = Toplevel(root) select_win.geometry("715x250") select_win.grab_set() selected = StringVar(value="Select reviewer name.") def on_confirm(): select_win.destroy() OptionMenu(select_win, selected, *reviewer_list).pack() Button(select_win, text="确认选择", command=on_confirm).pack() select_win.wait_window() return selected.get() def select_stage(root): """选择审核阶段""" select_win = Toplevel(root) select_win.geometry("715x250") select_win.grab_set() selected = StringVar(value="Select review stage.") def on_confirm(): select_win.destroy() OptionMenu(select_win, selected, *review_stages).pack() Button(select_win, text="确认选择", command=on_confirm).pack() select_win.wait_window() return selected.get() def compare_doc_lists(root): a = select_doc(root) if a in doc_reviews_completed: show_tip("Document review complete. Please choose again.") # 递归前加return透传返回值 return compare_doc_lists(root) else: return a def compare_stages(root): stage_choice = select_stage(root) reviewer = select_reviewer(root) stage_list = reviewer_completed_map[reviewer] if stage_choice in review_stages_completed: show_tip("The selected stage is complete for this document. Please choose again.") # 递归前加return透传返回值 return compare_stages(root) elif stage_choice in stage_list: show_tip("You have already completed this review stage. Please choose again.") # 递归前加return透传返回值 return compare_stages(root) else: return stage_choice, reviewer def enter_stage_completed(): # 全局唯一主根窗口,所有弹窗基于该窗口生成 root = Tk() root.withdraw() # 隐藏默认主窗口,仅展示功能弹窗 stage = compare_stages(root) confirm_win = Toplevel(root) confirm_win.geometry("300x100") confirm_win.grab_set() selected_stage = stage[0] def save_selected(): confirm_win.destroy() return selected_stage Label(confirm_win, text="Click to confirm that you completed reviewing the previously selected review stage.").pack() btn = Button( master = confirm_win, text=selected_stage, command=save_selected ) btn.pack() confirm_win.wait_window() root.destroy() # 所有流程结束销毁主窗口 return selected_stage # 功能测试 if __name__ == "__main__": res = enter_stage_completed() print("用户确认完成的阶段:", res)
内容的提问来源于stack exchange,提问作者mountainscaler
相关产品推荐
相关产品推荐

