You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Tkinter数字键盘场景中,如何替代Python eval()函数?

替代eval()实现tkinter数字虚拟键盘的可行方案

我在树莓派的10英寸触摸屏上开发了一套包含多个独立小程序的系统,每个小程序的tk.Entry组件仅允许输入数字(0-9)和小数点(.)。系统自带的虚拟键盘(如onBoard)占用屏幕空间过大,因此自行开发了一个数字虚拟键盘。与YouTube上常见的计算器示例不同,该键盘需要识别当前获得焦点的Entry组件。最初我通过字典列表记录Entry组件的名称字符串,但处理键盘输入时不得不使用eval()函数,现寻求替代eval()的方案。


解决方案思路

要彻底避免eval(),核心是直接存储组件对象而非名称字符串,同时利用tkinter原生方法直接操作获焦元素:

  1. 无需手动记录组件的名称(如.!labelframe.!entry),直接通过focus_get()获取获焦的Entry对象
  2. 字典列表中直接保存Entry组件实例和对应的StringVar对象,而非字符串形式的变量名
  3. 通过对象直接调用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()

关键改进点

  1. 移除eval()依赖:直接存储Entry组件和StringVar的实例对象,操作时直接调用对象方法,完全不需要解析字符串变量名
  2. 简化焦点匹配:通过focus_get()获取组件对象后,直接和列表中的对象对比,无需处理组件名称字符串
  3. 增强输入校验:添加了重复小数点的判断,避免输入无效内容
  4. 光标处理优化:用tk.END替代固定数字,自动将光标移到输入内容末尾,更通用

内容的提问来源于stack exchange,提问作者Pragmatic_Lee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 05:55:07