为何exec()结合Tkinter的END使用时出现报错?
解决Tkinter中exec执行代码时出现的NameError: 'END'未定义问题
问题背景
我编写了两段Python代码:
- 第一段是包含Tkinter组件的代码(保存为文件):
from tkinter import * from tkinter.filedialog import askopenfilename, asksaveasfilename x = "" win = Tk() win.title("TEST") b = "" e = Text(width = 40) e.pack() def but (): global x global e global b global by e.config(state = "disabled") x = e.get("1.0", END) win.destroy() by = Button(width = 30, height = 10, text = "Publish", command = but) by.pack() win.mainloop() print("") print(x)
- 第二段代码通过
exec()读取并执行上述文件:
from tkinter.filedialog import askopenfilename, asksaveasfilename def open_file(): filepath = askopenfilename( filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")] ) if not filepath: return with open(filepath, mode="r", encoding="utf-8") as input_file: text = input_file.read() exec(text) open_file()
点击按钮触发but函数时,出现如下错误:
Exception in Tkinter callback Traceback (most recent call last): File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/tkinter/__init__.py", line 1921, in __call__ return self.func(*args) File "<string>", line 15, in but NameError: name 'END' is not defined
问题原因
当用exec()执行文件内容时,from tkinter import *导入的名称(包括END)仅存在于open_file函数的局部命名空间中。而Tkinter的回调函数but运行时,会在模块级全局命名空间查找END,导致找不到该名称,触发NameError。
解决办法
方案1:让exec使用模块全局命名空间
修改exec()的调用,指定全局命名空间为模块的globals(),这样from tkinter import *导入的名称会被放到模块全局,回调函数就能找到END:
from tkinter.filedialog import askopenfilename, asksaveasfilename def open_file(): filepath = askopenfilename( filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")] ) if not filepath: return with open(filepath, mode="r", encoding="utf-8") as input_file: text = input_file.read() exec(text, globals()) # 指定全局命名空间 open_file()
方案2:在被执行的代码中显式引用tkinter模块
避免使用from tkinter import *,改为导入tkinter模块并通过模块名访问END,这样不受命名空间影响:
import tkinter as tk from tkinter.filedialog import askopenfilename, asksaveasfilename x = "" win = tk.Tk() win.title("TEST") b = "" e = tk.Text(width=40) e.pack() def but(): global x global e global b global by e.config(state="disabled") x = e.get("1.0", tk.END) # 使用tk.END win.destroy() by = tk.Button(width=30, height=10, text="Publish", command=but) by.pack() win.mainloop() print("") print(x)
方案3:在回调函数内部导入END
在but函数内部添加from tkinter import END,确保函数执行时能获取到该名称:
from tkinter import * from tkinter.filedialog import askopenfilename, asksaveasfilename x = "" win = Tk() win.title("TEST") b = "" e = Text(width=40) e.pack() def but(): from tkinter import END # 内部导入END global x global e global b global by e.config(state="disabled") x = e.get("1.0", END) win.destroy() by = Button(width=30, height=10, text="Publish", command=but) by.pack() win.mainloop() print("") print(x)
内容的提问来源于stack exchange,提问作者Xavion Mirchandani
相关产品推荐
相关产品推荐

