在Tkinter数字键盘场景中,如何替代Python eval()函数?
替代eval()实现tkinter数字虚拟键盘的可行方案
我在树莓派的10英寸触摸屏上开发了一套包含多个独立小程序的系统,每个小程序的tk.Entry组件仅允许输入数字(0-9)和小数点(.)。系统自带的虚拟键盘(如onBoard)占用屏幕空间过大,因此自行开发了一个数字虚拟键盘。与YouTube上常见的计算器示例不同,该键盘需要识别当前获得焦点的Entry组件。最初我通过字典列表记录Entry组件的名称字符串,但处理键盘输入时不得不使用eval()函数,现寻求替代eval()的方案。
解决方案思路
要彻底避免eval(),核心是直接存储组件对象而非名称字符串,同时利用tkinter原生方法直接操作获焦元素:
- 无需手动记录组件的名称(如
.!labelframe.!entry),直接通过focus_get()获取获焦的Entry对象 - 字典列表中直接保存Entry组件实例和对应的StringVar对象,而非字符串形式的变量名
- 通过对象直接调用
get()、set()、icursor()等方法,完全不需要eval()
改进后的完整代码
import tkinter as tk from tkinter import messagebox class NumericKeypadGUI: def __init__(self): self.root = tk.Tk() self.root.geometry('650x225') self.root.title('Numeric Keypad Demo') self.allEntryFields = [] # 存储Entry组件和对应StringVar的字典列表 # Frame 1 - 标签和输入框 f1 = tk.LabelFrame(self.root,padx=5,pady=5) f1.grid(column=0,row=0) tk.Label(f1,text='Frame 1 Entry 1',font=('Arial',12)).grid(row=0,column=0) tk.Label(f1,text='Frame 1 Entry 2',font=('Arial',12)).grid(row=1,column=0) self.f1e1 = tk.StringVar() self.f1e2 = tk.StringVar() f1e1_entry = tk.Entry(f1,textvariable=self.f1e1,width='7',font=('Arial',12)) f1e2_entry = tk.Entry(f1,textvariable=self.f1e2,width='7',font=('Arial',12)) f1e1_entry.grid(row=0,column=1) f1e2_entry.grid(row=1,column=1) # 直接存储对象,而非名称字符串 self.allEntryFields.append({ 'entry_widget': f1e1_entry, 'text_var': self.f1e1 }) self.allEntryFields.append({ 'entry_widget': f1e2_entry, 'text_var': self.f1e2 }) # Frame 2 - 更多标签和输入框 f2 = tk.LabelFrame(self.root,padx=5,pady=5) f2.grid(column=1,row=0) tk.Label(f2,text='Frame 2 Entry 1',font=('Arial',12)).grid(row=0,column=0) tk.Label(f2,text='Frame 2 Entry 2',font=('Arial',12)).grid(row=1,column=0) self.f2e1 = tk.StringVar() self.f2e2 = tk.StringVar() f2e1_entry = tk.Entry(f2,textvariable=self.f2e1,width='7',font=('Arial',12)) f2e2_entry = tk.Entry(f2,textvariable=self.f2e2,width='7',font=('Arial',12)) f2e1_entry.grid(row=0,column=1) f2e2_entry.grid(row=1,column=1) self.allEntryFields.append({ 'entry_widget': f2e1_entry, 'text_var': self.f2e1 }) self.allEntryFields.append({ 'entry_widget': f2e2_entry, 'text_var': self.f2e2 }) # Frame 3 - 数字键盘 f3 = tk.LabelFrame(self.root,padx=5,pady=5) f3.grid(column=2,row=0) # 键盘按钮布局 tk.Button(f3,text='7',bg='white',command=lambda: self.kb_entry(7),width=4,font=('Arial',16)).grid(row=0,column=1,padx=3,pady=3) tk.Button(f3,text='8',bg='white',command=lambda: self.kb_entry(8),width=4,font=('Arial',16)).grid(row=0,column=2,padx=3,pady=3) tk.Button(f3,text='9',bg='white',command=lambda: self.kb_entry(9),width=4,font=('Arial',16)).grid(row=0,column=3,padx=3,pady=3) tk.Button(f3,text='4',bg='white',command=lambda: self.kb_entry(4),width=4,font=('Arial',16)).grid(row=1,column=1,padx=3,pady=3) tk.Button(f3,text='5',bg='white',command=lambda: self.kb_entry(5),width=4,font=('Arial',16)).grid(row=1,column=2,padx=3,pady=3) tk.Button(f3,text='6',bg='white',command=lambda: self.kb_entry(6),width=4,font=('Arial',16)).grid(row=1,column=3,padx=3,pady=3) tk.Button(f3,text='1',bg='white',command=lambda: self.kb_entry(1),width=4,font=('Arial',16)).grid(row=2,column=1,padx=3,pady=3) tk.Button(f3,text='2',bg='white',command=lambda: self.kb_entry(2),width=4,font=('Arial',16)).grid(row=2,column=2,padx=3,pady=3) tk.Button(f3,text='3',bg='white',command=lambda: self.kb_entry(3),width=4,font=('Arial',16)).grid(row=2,column=3,padx=3,pady=3) tk.Button(f3,text='0',bg='white',command=lambda: self.kb_entry(0),width=4,font=('Arial',16)).grid(row=3,column=1,padx=3,pady=3) tk.Button(f3,text='.',bg='white',command=lambda: self.kb_entry('.'),width=4,font=('Arial',16)).grid(row=3,column=2,padx=3,pady=3) self.root.mainloop() def kb_entry(self,the_key): focused_widget = self.root.focus_get() if not isinstance(focused_widget, tk.Entry): tk.messagebox.showerror('Error','请先选择输入框') return # 匹配获焦的Entry组件 target = None for entry_info in self.allEntryFields: if entry_info['entry_widget'] == focused_widget: target = entry_info break if target: current_val = target['text_var'].get() # 额外校验:避免重复输入小数点 if the_key == '.' and '.' in current_val: return new_val = current_val + str(the_key) target['text_var'].set(new_val) target['entry_widget'].icursor(tk.END) # 直接将光标移到末尾,不用固定数字 else: tk.messagebox.showerror('Error','无法识别当前输入框') if __name__ == '__main__': NumericKeypadGUI()
关键改进点
- 移除eval()依赖:直接存储Entry组件和StringVar的实例对象,操作时直接调用对象方法,完全不需要解析字符串变量名
- 简化焦点匹配:通过
focus_get()获取组件对象后,直接和列表中的对象对比,无需处理组件名称字符串 - 增强输入校验:添加了重复小数点的判断,避免输入无效内容
- 光标处理优化:用
tk.END替代固定数字,自动将光标移到输入内容末尾,更通用
内容的提问来源于stack exchange,提问作者Pragmatic_Lee
相关产品推荐
相关产品推荐

