Python使用Tkinter+Matplotlib实现用户自定义函数绘图的问题咨询
实现方案
核心是通过eval()函数将函数字符串解析为可执行的表达式,同时做输入预处理和异常处理保证程序稳定性,具体修改如下:
核心修改逻辑
- 预处理用户输入:去除
y =这类前缀,仅保留关于x的表达式部分 - 执行表达式时限定命名空间,仅允许调用numpy库和预设的x变量,降低
eval()的安全风险 - 异常捕获:处理用户输入非法表达式的场景,避免程序直接崩溃
- 优化绘图逻辑:把画布和工具栏初始化放到点击事件外,避免重复生成组件
修改后完整代码
import matplotlib matplotlib.use("TkAgg") import numpy as np from tkinter import messagebox from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk from matplotlib.figure import Figure import tkinter as tk from tkinter import ttk LARGE_FONT=("Times New Roman",12) x=np.linspace(-5,5,100) class MyApp(tk.Tk): def __init__(self, *args, **kwargs): tk.Tk.__init__(self,*args,**kwargs) tk.Tk.wm_title(self, "My app") container = tk.Frame(self) container.pack(side="top", fill="both", expand=True) container.grid_rowconfigure(0, weight=1) container.grid_columnconfigure(0, weight=1) self.frames = {} for F in (StartPage, GraphPage): frame = F(container, self) self.frames[F] = frame frame.grid(row=0, column = 0, sticky="nsew") self.show_frame(StartPage) def show_frame(self, cont): frame = self.frames[cont] frame.tkraise() class StartPage(tk.Frame): def __init__(self,parent,controller): tk.Frame.__init__(self,parent) label = tk.Label(self,text="Ed. App - Start Page",font = LARGE_FONT) label.pack(pady=10,padx=10) button3 = ttk.Button(self, text="Go to graph page ->", command = lambda:controller.show_frame(GraphPage)) button3.pack() class GraphPage(tk.Frame): def __init__(self,parent,controller): tk.Frame.__init__(self,parent) # 输入框 self.e = tk.Entry(self, width =50) self.e.pack() label = tk.Label(self,text="Graph your function! 输入示例:np.sin(x)、x**2+2*x",font = LARGE_FONT) label.pack(pady=10,padx=10) button1 = ttk.Button(self, text="Back to Home", command = lambda:controller.show_frame(StartPage)) button1.pack() # 绘图相关初始化放到点击事件外,避免重复生成 self.f = Figure(figsize=(5,5), dpi=100) self.a = self.f.add_subplot(111) self.canvas = FigureCanvasTkAgg(self.f, self) self.canvas.draw() self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True) self.toolbar = NavigationToolbar2Tk(self.canvas, self) self.toolbar.update() self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True) def myClick(): func_str = self.e.get().strip() # 处理y=开头的输入 if func_str.lower().startswith('y='): func_str = func_str[2:].strip() try: # 限定命名空间,仅允许使用np和x,屏蔽内置危险函数 y = eval(func_str, {"np": np, "x": x, "__builtins__": {}}) # 每次绘图前清空旧图像 self.a.clear() self.a.plot(x,y,'r') # 刷新画布 self.canvas.draw() except Exception as e: messagebox.showerror("输入错误", f"函数表达式不合法:{str(e)}") button4 = ttk.Button(self, text="plot", command = myClick) button4.pack() app = MyApp() app.mainloop()
注意事项
- 个人使用场景下
eval()完全够用,若要面向公开用户部署,可改用numexpr库或者自定义语法解析器进一步提升安全性 - 输入时直接写
x**2、np.cos(x)、np.log(x)这类表达式即可,也支持带y=前缀的输入格式
内容的提问来源于stack exchange,提问作者some1else2
相关产品推荐
相关产品推荐

