Python数学可视化——显函数、隐函数及复杂曲线的交互式绘图技术
Python数学可视化——显函数、隐函数及复杂曲线的交互式绘图技术
一、引言
在科学计算和数据分析中,函数与方程的可视化是理解数学关系和物理现象的重要工具。本文基于Python的Tkinter和Matplotlib库,实现一个功能完善的函数与方程可视化工具,支持显函数、隐函数、特殊曲线(如心形线)及物理场分布(如电势)的交互式绘图,并提供安全的表达式解析、图像保存等功能。
二、核心技术架构
2.1 系统架构与技术选型
- 界面层:使用Tkinter构建GUI,包含类型选择、表达式输入、预设函数下拉菜单等控件
- 计算层:
- 显函数:通过
np.linspace
生成采样点,安全计算函数值 - 隐函数:基于等高线算法
contour
绘制等值线 - 安全机制:通过正则表达式过滤非法字符,限制白名单函数防止代码注入
- 显函数:通过
- 可视化层:Matplotlib实现图表渲染,支持动态更新和交互式工具条
2.2 安全表达式解析
def is_valid_expression(expr):"""验证表达式安全性"""allowed_chars = set("0123456789.+-*/()xy^np_sin_cos_tan_exp_sqrt_log_pi_ ")invalid_chars = set(expr.replace('.', '').replace('_', '')) - allowed_charsif invalid_chars:raise ValueError(f"非法字符: {''.join(invalid_chars)}")# 括号匹配检查stack = []for char in expr:if char == '(': stack.append(char)elif char == ')':if not stack: raise ValueError("括号不匹配")stack.pop()if stack: raise ValueError("括号不匹配")return Truedef safe_eval(expr, namespace):"""安全执行表达式"""expr = expr.replace('^', '**') # 替换幂运算符allowed_funcs = {'np': np, 'sin': np.sin, 'cos': np.cos, 'tan': np.tan,'exp': np.exp, 'sqrt': np.sqrt, 'log': np.log, 'pi': np.pi}safe_globals = {"__builtins__": None}safe_locals = {**allowed_funcs, **namespace}compiled_code = compile(expr, '<string>', 'eval')return eval(compiled_code, safe_globals, safe_locals)
三、显函数可视化
3.1 核心实现
def plot_explicit_function(self, f, x_range, title):"""绘制显函数"""self.fig.clear()ax = self.fig.add_subplot(111)ax.set_facecolor('white')x = np.linspace(x_range[0], x_range[1], 1000)y = np.array([f(xi) for xi in x]) # 逐点计算防止数组错误ax.plot(x, y, 'b-', linewidth=2.5)ax.set_title(title)ax.grid(True, linestyle='--', alpha=0.6)self.optimize_ticks(ax, x_range, (y.min(), y.max()))
3.2 案例演示
案例1:三次函数
# 预设函数定义
self.explicit_presets = {"三次函数": {"func": lambda x: x**3 - 3*x,"expr": "x**3 - 3*x","x_range": (-2.5, 2.5),"title": "三次函数: $y = x^3 - 3x$",}
}
案例2:双曲线
plot_explicit("1/x", x_range=(-5,5)) # 输入表达式直接绘制
四、隐函数可视化
4.1 核心实现
def plot_implicit_equation(self, eq, x_range, y_range):"""绘制隐函数F(x,y)=0"""x = np.linspace(x_range[0], x_range[1], 500)y = np.linspace(y_range[0], y_range[1], 500)X, Y = np.meshgrid(x, y)Z = eq(X, Y)self.fig.contour(X, Y, Z, levels=[0], colors='red', linewidths=2.5)self.fig.contourf(X, Y, Z, alpha=0.6) # 填充色显示数值分布self.fig.colorbar(label='F(x,y)')
4.2 案例演示
案例1:圆方程
# 预设隐函数
self.implicit_presets["圆"] = {"eq": lambda x, y: x**2 + y**2 - 4,"title": "圆: $x^2 + y^2 = 4$",
}
案例2:笛卡尔叶形线
plot_implicit("x**3 + y**3 - 3*x*y", x_range=(-3,3), y_range=(-3,3))
五、特色曲线与物理应用
5.1 心形线(数学艺术)
def plot_heart_curve(self):"""笛卡尔心形线"""eq = lambda x,y: (x**2 + y**2 -1)**3 - x**2*y**3self.plot_implicit_equation(eq, x_range=(-1.5,1.5), y_range=(-1.5,1.5))self.fig.contourf(..., colors='pink', alpha=0.4) # 填充爱心区域
5.2 电势分布(物理应用)
def plot_electric_potential(self):"""点电荷电势分布"""charges = [{"x":-1,"y":0,"q":1}, {"x":1,"y":0,"q":-1}]x = np.linspace(-2.5,2.5,500)y = np.linspace(-2,2,500)X,Y = np.meshgrid(x,y)V = sum(charge['q']/np.sqrt((X-c['x'])**2 + (Y-c['y'])**2) for c in charges)self.fig.contourf(X,Y,V, cmap='coolwarm') # 温度映射显示电势self.fig.scatter([c['x']], [c['y']], s=300, c=['red','blue'], marker='+-')
六、交互式GUI设计
6.1 界面布局
def __init__(self, root):self.root = rootself.root.geometry("1200x800")# 左侧控制面板left_frame = ttk.LabelFrame(root, text="可视化选项")ttk.Radiobutton(left_frame, text="显函数", variable=self.viz_type, value="explicit")ttk.Radiobutton(left_frame, text="隐函数", variable=self.viz_type, value="implicit")ttk.Radiobutton(left_frame, text="心形线", variable=self.viz_type, value="heart")# 右侧绘图区域self.canvas = FigureCanvasTkAgg(self.fig, master=right_frame)self.toolbar = NavigationToolbar2Tk(self.canvas, toolbar_frame) # 集成缩放工具
6.2 动态控件更新
def update_controls(self):"""根据选择类型显示对应控件"""if self.viz_type.get() == "explicit":self.explicit_frame.pack()self.update_preset_options(self.explicit_presets.keys())elif self.viz_type.get() == "implicit":self.implicit_frame.pack()self.update_preset_options(self.implicit_presets.keys())# 隐藏其他面板
七、高级功能
7.1 图像保存
def save_image(self):filename = simpledialog.askstring("保存", "文件名")if filename:self.fig.savefig(f"{filename}.png", dpi=150, bbox_inches="tight")messagebox.showinfo("成功", f"保存至: {os.path.abspath(filename)}")
7.2 公式渲染
def get_function_label(self, expr):"""生成LaTeX公式"""expr = expr.replace('np.sin', '\\sin').replace('**', '^')expr = re.sub(r'(\d)/(\d)', r'\\frac{\1}{\2}', expr) # 自动转换分数return f"${expr}$"
八、教学与应用场景
8.1 教学场景
- 基础数学:演示函数图像变换(平移、缩放、翻转)
- 解析几何:对比显式方程与隐式方程的几何意义
- 高等数学:展示参数方程(如心形线)与极坐标方程
- 大学物理:可视化电场、磁场等物理场分布
8.2 扩展方向
- 支持极坐标绘图
- 添加导数/积分可视化
- 集成3D绘图功能(使用mpl_toolkits.mplot3d)
- 开发数据导入功能(CSV/Excel)
九、总结
本文实现的函数可视化工具具备以下特点:
- 安全性:通过表达式过滤和白名单机制防止代码注入
- 交互性:支持实时切换函数类型、调整参数、缩放图表
- 扩展性:预设函数与自定义输入结合,方便扩展新类型
- 专业性:支持LaTeX公式渲染、物理场可视化等专业需求
该工具可广泛应用于数学教学、工程仿真、科学研究等领域,帮助用户快速建立数学表达式与图形之间的直观联系。
十、完整代码
import tkinter as tk
from tkinter import ttk, messagebox, simpledialog
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import matplotlib.patches as patches
from matplotlib import ticker
from matplotlib.colors import ListedColormap
import re
import os# 设置matplotlib支持中文显示
plt.rcParams["font.family"] = ["SimHei","WenQuanYi Micro Hei","Heiti TC","Arial Unicode MS",
]
plt.rcParams["axes.unicode_minus"] = False # 解决负号显示问题class FunctionVisualizer:def __init__(self, root):self.root = rootself.root.title("函数与方程可视化工具")self.root.geometry("1200x800")self.root.minsize(1000, 700)# 设置主题颜色self.bg_color = "#f5f5f5"self.frame_color = "#ffffff"self.button_color = "#3b82f6"self.button_text_color = "#ffffff"# 预设函数分组(显函数/隐函数)self.explicit_presets = {"三次函数": {"func": lambda x: x**3 - 3 * x,"expr": "x**3 - 3*x","x_range": (-2.5, 2.5),"title": "三次函数: $y = x^3 - 3x$",},"双曲线": {"func": lambda x: 1 / x,"expr": "1/x","x_range": (-5, 5),"title": "双曲线: $y = \\frac{1}{x}$",},"指数函数": {"func": lambda x: np.exp(x),"expr": "np.exp(x)","x_range": (-3, 3),"title": "指数函数: $y = e^x$",},}self.implicit_presets = {"圆": {"eq": lambda x, y: x**2 + y**2 - 4,"expr": "x**2 + y**2 - 4","x_range": (-3, 3),"y_range": (-3, 3),"title": "圆: $x^2 + y^2 = 4$",},"椭圆": {"eq": lambda x, y: x**2 / 4 + y**2 / 9 - 1,"expr": "x**2/4 + y**2/9 - 1","x_range": (-3, 3),"y_range": (-4, 4),"title": "椭圆: $\\frac{x^2}{4} + \\frac{y^2}{9} = 1$",},"双曲线(隐式)": {"eq": lambda x, y: x**2 - y**2 - 1,"expr": "x**2 - y**2 - 1","x_range": (-3, 3),"y_range": (-3, 3),"title": "双曲线: $x^2 - y^2 = 1$",},"笛卡尔叶形线": {"eq": lambda x, y: x**3 + y**3 - 3 * x * y,"expr": "x**3 + y**3 - 3*x*y","x_range": (-3, 3),"y_range": (-3, 3),"title": "笛卡尔叶形线: $x^3 + y^3 = 3xy$",},}# 创建主框架main_frame = ttk.Frame(self.root, padding=10)main_frame.pack(fill=tk.BOTH, expand=True)# 创建左侧控制面板left_frame = ttk.LabelFrame(main_frame, text="函数与方程可视化选项", padding=10, width=375)left_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 10))left_frame.pack_propagate(False) # 固定宽度# 创建右侧绘图区域right_frame = ttk.Frame(main_frame)right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)# 创建绘图区域和工具栏容器self.plot_frame = ttk.Frame(right_frame)self.plot_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)# 初始化绘图区域self.fig = Figure(figsize=(8, 6), dpi=100)self.canvas = FigureCanvasTkAgg(self.fig, master=self.plot_frame)self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)# 添加工具栏self.toolbar_frame = ttk.Frame(right_frame, height=40)self.toolbar_frame.pack(fill=tk.X, padx=5, pady=(0, 5))self.toolbar = NavigationToolbar2Tk(self.canvas, self.toolbar_frame)self.toolbar.update()# 添加控制选项self.create_controls(left_frame)# 初始显示self.plot_predefined_function()def create_controls(self, parent):"""创建控制选项"""ttk.Label(parent, text="选择可视化类型:", font=("SimHei", 10, "bold")).pack(anchor=tk.W, pady=(0, 10))# 可视化类型选择self.viz_type = tk.StringVar(value="explicit")types = [("显函数", "explicit"),("隐函数", "implicit"),("心形线", "heart"),("电势分布", "potential"),]for text, value in types:ttk.Radiobutton(parent,text=text,variable=self.viz_type,value=value,command=self.update_controls,).pack(anchor=tk.W, padx=5, pady=2)# 预设函数下拉菜单(动态更新选项)self.preset_frame = ttk.LabelFrame(parent, text="预设函数", padding=10)self.preset_frame.pack(fill=tk.X, pady=10)# 动态选项变量self.preset_functions = tk.StringVar()self.preset_combobox = ttk.Combobox(self.preset_frame, textvariable=self.preset_functions, width=30)self.preset_combobox.pack(fill=tk.X, pady=5)ttk.Button(self.preset_frame,text="绘制预设函数",command=self.plot_predefined_function,).pack(fill=tk.X, pady=5)# 显函数输入self.explicit_frame = ttk.LabelFrame(parent, text="显函数输入", padding=10)self.explicit_frame.pack(fill=tk.X, pady=10)ttk.Label(self.explicit_frame, text="函数表达式 (例如 x**2):").pack(anchor=tk.W)self.explicit_entry = ttk.Entry(self.explicit_frame, width=30)self.explicit_entry.insert(0, "x**3 - 3*x")self.explicit_entry.pack(fill=tk.X, pady=5)ttk.Label(self.explicit_frame, text="X范围 (min,max):").pack(anchor=tk.W)self.x_range_entry = ttk.Entry(self.explicit_frame, width=30)self.x_range_entry.insert(0, "-2.5,2.5")self.x_range_entry.pack(fill=tk.X, pady=5)ttk.Button(self.explicit_frame, text="绘制显函数", command=self.plot_explicit).pack(fill=tk.X, pady=5)# 隐函数输入self.implicit_frame = ttk.LabelFrame(parent, text="隐函数输入", padding=10)self.implicit_frame.pack(fill=tk.X, pady=10)ttk.Label(self.implicit_frame, text="方程表达式 (例如 x**2 + y**2 - 4):").pack(anchor=tk.W)self.implicit_entry = ttk.Entry(self.implicit_frame, width=30)self.implicit_entry.insert(0, "x**3 + y**3 - 3*x*y")self.implicit_entry.pack(fill=tk.X, pady=5)ttk.Label(self.implicit_frame, text="X范围 (min,max):").pack(anchor=tk.W)self.implicit_x_range_entry = ttk.Entry(self.implicit_frame, width=30)self.implicit_x_range_entry.insert(0, "-3,3")self.implicit_x_range_entry.pack(fill=tk.X, pady=5)ttk.Label(self.implicit_frame, text="Y范围 (min,max):").pack(anchor=tk.W)self.implicit_y_range_entry = ttk.Entry(self.implicit_frame, width=30)self.implicit_y_range_entry.insert(0, "-3,3")self.implicit_y_range_entry.pack(fill=tk.X, pady=5)ttk.Button(self.implicit_frame, text="绘制隐函数", command=self.plot_implicit).pack(fill=tk.X, pady=5)# 保存图像按钮ttk.Button(parent, text="保存图像", command=self.save_image).pack(side=tk.BOTTOM, pady=10)# 初始更新控件状态self.update_controls()def update_controls(self):"""更新控件状态"""viz_type = self.viz_type.get()# 隐藏所有输入面板self.preset_frame.pack_forget()self.explicit_frame.pack_forget()self.implicit_frame.pack_forget()# 显示对应面板if viz_type == "explicit":self.explicit_frame.pack(fill=tk.X, pady=10)self.update_preset_options(self.explicit_presets.keys()) # 显函数预设elif viz_type == "implicit":self.implicit_frame.pack(fill=tk.X, pady=10)self.update_preset_options(self.implicit_presets.keys()) # 隐函数预设elif viz_type == "heart":self.plot_heart_curve()elif viz_type == "potential":self.plot_electric_potential()# 显示预设框架self.preset_frame.pack(fill=tk.X, pady=10)def update_preset_options(self, options=None):"""动态更新预设函数选项"""if options is None:options = []self.preset_combobox["values"] = list(options)if options:self.preset_functions.set(list(options)[0]) # 默认选择第一个def plot_predefined_function(self):"""绘制预设函数"""viz_type = self.viz_type.get()selected = self.preset_functions.get()self.fig.clear()ax = self.fig.add_subplot(111)ax.set_facecolor("white")self.fig.set_facecolor("white")if viz_type == "explicit" and selected in self.explicit_presets:data = self.explicit_presets[selected]self.plot_explicit_function(f=data["func"], x_range=data["x_range"], title=data["title"])# 更新显函数输入框self.explicit_entry.delete(0, tk.END)self.explicit_entry.insert(0, data["expr"])self.x_range_entry.delete(0, tk.END)self.x_range_entry.insert(0, f"{data['x_range'][0]},{data['x_range'][1]}")elif viz_type == "implicit" and selected in self.implicit_presets:data = self.implicit_presets[selected]self.plot_implicit_equation(eq=data["eq"],x_range=data["x_range"],y_range=data["y_range"],title=data["title"],)# 更新隐函数输入框self.implicit_entry.delete(0, tk.END)self.implicit_entry.insert(0, data["expr"])self.implicit_x_range_entry.delete(0, tk.END)self.implicit_x_range_entry.insert(0, f"{data['x_range'][0]},{data['x_range'][1]}")self.implicit_y_range_entry.delete(0, tk.END)self.implicit_y_range_entry.insert(0, f"{data['y_range'][0]},{data['y_range'][1]}")self.canvas.draw()def is_valid_expression(self, expr):"""验证表达式是否为有效的数学表达式"""# 允许的字符:数字、运算符、函数名、xy变量、小数点、括号、空格allowed_chars = set("0123456789.+-*/()xy^np_sin_cos_tan_exp_sqrt_log_pi_ ")# 移除所有允许的字符,检查是否还有剩余cleaned = expr.replace('.', '').replace('_', '')invalid_chars = set(cleaned) - allowed_charsif invalid_chars:raise ValueError(f"非法字符: {''.join(invalid_chars)}")# 检查括号匹配stack = []for char in expr:if char == '(':stack.append(char)elif char == ')':if not stack:raise ValueError("括号不匹配:缺少左括号")stack.pop()if stack:raise ValueError("括号不匹配:缺少右括号")return Truedef safe_eval(self, expr, namespace):"""安全地执行表达式计算"""try:self.is_valid_expression(expr)# 替换常见函数别名expr = expr.replace('^', '**') # 替换^为**# 白名单函数和变量allowed_funcs = {'np': np,'sin': np.sin,'cos': np.cos,'tan': np.tan,'exp': np.exp,'sqrt': np.sqrt,'log': np.log,'pi': np.pi,'arctan2': np.arctan2,}# 创建安全命名空间safe_globals = {"__builtins__": None}safe_locals = {**allowed_funcs, **namespace}# 使用编译后的代码提高安全性compiled_code = compile(expr, '<string>', 'eval')return eval(compiled_code, safe_globals, safe_locals)except Exception as e:raise ValueError(f"表达式错误: {str(e)}")def plot_explicit(self):"""绘制用户输入的显函数"""try:func_str = self.explicit_entry.get().strip()x_range_str = self.x_range_entry.get().strip()if not func_str or not x_range_str:raise ValueError("请输入函数表达式和X范围")# 解析x范围x_min, x_max = map(float, x_range_str.split(","))if x_min >= x_max:raise ValueError("X范围的最小值必须小于最大值")# 生成x值x_vals = np.linspace(x_min, x_max, 1000)# 安全计算y值(逐个点计算,避免数组错误)y_vals = np.zeros_like(x_vals)for i, x in enumerate(x_vals):y_vals[i] = self.safe_eval(func_str, {'x': x})# 绘制函数self.plot_explicit_function(f=lambda x: y_vals,x_range=(x_min, x_max),title=f"显函数: $y = {self.get_function_label(func_str)}$",)self.canvas.draw()except Exception as e:messagebox.showerror("错误", f"绘制显函数时出错: {str(e)}")def plot_implicit(self):"""绘制用户输入的隐函数(修复网格点数不匹配问题)"""try:eq_str = self.implicit_entry.get().strip()x_range_str = self.implicit_x_range_entry.get().strip()y_range_str = self.implicit_y_range_entry.get().strip()if not eq_str or not x_range_str or not y_range_str:raise ValueError("请输入完整的方程表达式和范围")# 解析范围x_min, x_max = map(float, x_range_str.split(","))y_min, y_max = map(float, y_range_str.split(","))if x_min >= x_max or y_min >= y_max:raise ValueError("范围的最小值必须小于最大值")# 创建向量化的方程函数(直接处理数组输入)eq = lambda X, Y: self.safe_eval(eq_str, {'x': X, 'y': Y})# 调用隐函数绘图函数,使用默认分辨率500(与函数内部一致)self.plot_implicit_equation(eq=eq,x_range=(x_min, x_max),y_range=(y_min, y_max),title=f"隐函数: ${self.get_function_label(eq_str)} = 0$",)self.canvas.draw()except Exception as e:messagebox.showerror("错误", f"绘制隐函数时出错: {str(e)}")except Exception as e:messagebox.showerror("错误", f"绘制隐函数时出错: {str(e)}")def plot_explicit_function(self, f, x_range=(-5, 5), title="显函数图像"):"""绘制显函数 y = f(x) 的图像参数:f: 函数对象x_range: x轴范围title: 图像标题"""self.fig.clear()ax = self.fig.add_subplot(111)# 设置背景为白色ax.set_facecolor("white")self.fig.set_facecolor("white")# 创建网格和样式ax.grid(True, linestyle="--", alpha=0.6)ax.spines["left"].set_position("zero")ax.spines["bottom"].set_position("zero")ax.spines["right"].set_visible(False)ax.spines["top"].set_visible(False)# 生成数据x = np.linspace(x_range[0], x_range[1], 1000)try:y = f(x)except Exception as e:messagebox.showerror("函数错误", f"计算函数值时出错: {str(e)}")return# 绘制函数曲线ax.plot(x, y, "b-", linewidth=2.5)# 设置标题和标签ax.set_title(title, fontsize=16, pad=20)ax.set_xlabel("x", fontsize=12, labelpad=-10, x=1.02)ax.set_ylabel("y", fontsize=12, labelpad=-20, y=1.02, rotation=0)# 优化坐标轴刻度self.optimize_ticks(ax, x_range, (np.min(y), np.max(y)))self.fig.tight_layout()def plot_implicit_equation(self,eq,x_range=(-3, 3),y_range=(-3, 3),resolution=500,levels=[0],cmap="viridis",title="隐函数图像",):"""绘制隐函数 F(x, y) = 0 的图像参数:eq: 函数对象x_range, y_range: 绘图范围resolution: 网格分辨率levels: 绘制等高线的值cmap: 颜色映射title: 图像标题"""self.fig.clear()ax = self.fig.add_subplot(111)# 设置背景为白色ax.set_facecolor("white")self.fig.set_facecolor("white")# 创建网格x = np.linspace(x_range[0], x_range[1], resolution)y = np.linspace(y_range[0], y_range[1], resolution)X, Y = np.meshgrid(x, y)# 计算方程值try:Z = eq(X, Y)except Exception as e:messagebox.showerror("方程错误", f"计算方程值时出错: {str(e)}")return# 绘制等高线 (隐函数曲线)contour = ax.contour(X, Y, Z, levels=levels, colors="red", linewidths=2.5)# 添加填充色显示方程值的变化 (只在需要时)if len(levels) > 1:ax.contourf(X, Y, Z, levels=np.linspace(Z.min(), Z.max(), 100), cmap=cmap, alpha=0.6)# 添加颜色条cbar = self.fig.colorbar(contour)cbar.set_label("F(x, y)", rotation=270, labelpad=20)# 设置网格和样式ax.grid(True, linestyle="--", alpha=0.4)ax.set_aspect("equal")# 设置标题和标签ax.set_title(title, fontsize=16, pad=20)ax.set_xlabel("x", fontsize=12)ax.set_ylabel("y", fontsize=12)# 添加零线ax.axhline(0, color="black", linewidth=0.8, alpha=0.7)ax.axvline(0, color="black", linewidth=0.8, alpha=0.7)# 优化坐标轴刻度self.optimize_ticks(ax, x_range, y_range)self.fig.tight_layout()def optimize_ticks(self, ax, x_range, y_range):"""优化坐标轴刻度,避免刻度过于密集"""x_min, x_max = x_rangey_min, y_max = y_range# 根据数据范围自动设置刻度x_span = x_max - x_miny_span = y_max - y_min# 设置合理的刻度间隔x_major_locator = ticker.MaxNLocator(nbins=7)y_major_locator = ticker.MaxNLocator(nbins=7)ax.xaxis.set_major_locator(x_major_locator)ax.yaxis.set_major_locator(y_major_locator)def plot_heart_curve(self):"""绘制心形线"""self.fig.clear()# 创建图像和子图ax1 = self.fig.add_subplot(111)ax1.set_aspect("equal")ax1.set_title("心形线: $(x^2+y^2-1)^3 - x^2y^3 = 0$", fontsize=14)# 设置背景为白色ax1.set_facecolor("white")self.fig.set_facecolor("white")# 定义心形线方程def heart_eq(x, y):return (x**2 + y**2 - 1) ** 3 - x**2 * y**3# 生成网格x = np.linspace(-1.5, 1.5, 500)y = np.linspace(-1.5, 1.5, 500)X, Y = np.meshgrid(x, y)Z = heart_eq(X, Y)# 绘制心形线contour = ax1.contour(X, Y, Z, levels=[0], colors="red", linewidths=3)# 填充颜色ax1.contourf(X, Y, Z, levels=[-1000, 0], colors=["pink"], alpha=0.4)# 添加网格和样式ax1.grid(True, linestyle="--", alpha=0.3)ax1.set_xlim(-1.5, 1.5)ax1.set_ylim(-1.5, 1.5)# 优化坐标轴刻度self.optimize_ticks(ax1, (-1.5, 1.5), (-1.5, 1.5))self.fig.tight_layout()self.canvas.draw()def plot_electric_potential(self):"""可视化点电荷系统的电势分布"""self.fig.clear()ax = self.fig.add_subplot(111)# 设置背景为白色ax.set_facecolor("white")self.fig.set_facecolor("white")# 定义两个点电荷的位置和电荷量charges = [{"x": -1, "y": 0, "q": 1}, # 正电荷{"x": 1, "y": 0, "q": -1}, # 负电荷]# 创建网格x = np.linspace(-2.5, 2.5, 500)y = np.linspace(-2, 2, 500)X, Y = np.meshgrid(x, y)# 计算电势 (k=1)V = np.zeros_like(X)for charge in charges:r = np.sqrt((X - charge["x"]) ** 2 + (Y - charge["y"]) ** 2)V += charge["q"] / r# 避免除以零V = np.nan_to_num(V, posinf=10, neginf=-10)# 绘制电势等高线 (使用contourf创建填充等高线)levels = np.linspace(-10, 10, 21)contourf = ax.contourf(X, Y, V, levels=levels, cmap="coolwarm", alpha=0.8)contour = ax.contour(X, Y, V, levels=levels, colors="k", linewidths=0.5)ax.clabel(contour, inline=True, fontsize=8)# 绘制电荷位置for charge in charges:color = "red" if charge["q"] > 0 else "blue"marker = "+" if charge["q"] > 0 else "_"ax.scatter(charge["x"], charge["y"], s=300, c=color, marker=marker, linewidths=2)ax.text(charge["x"],charge["y"] + 0.2,f"{charge['q']}q",ha="center",fontsize=12,weight="bold",)# 设置标题和标签ax.set_title("两个点电荷的电势分布", fontsize=16, pad=20)ax.set_xlabel("x (m)", fontsize=12)ax.set_ylabel("y (m)", fontsize=12)# 添加网格和样式ax.set_aspect("equal")ax.grid(True, linestyle="--", alpha=0.4)# 添加坐标轴ax.axhline(0, color="k", linewidth=0.8, alpha=0.7)ax.axvline(0, color="k", linewidth=0.8, alpha=0.7)# 添加物理公式ax.text(1.5,1.8,r"$V = \sum \frac{kq_i}{r_i}$",fontsize=14,bbox=dict(facecolor="white", alpha=0.8),)# 添加颜色条cbar = self.fig.colorbar(contourf, label="电势 (V)")# 优化坐标轴刻度self.optimize_ticks(ax, (-2.5, 2.5), (-2, 2))self.fig.tight_layout()self.canvas.draw()def get_function_label(self, func_str):"""生成函数的LaTeX标签"""# 安全检查,防止恶意代码if any(word in func_str.lower() for word in ["import", "os", "sys", "subprocess"]):raise ValueError("检测到不安全的代码")# 直接使用原始字符串,不再进行转义safe_str = func_str# 替换常见的数学函数replacements = {r'np\.sin\(([^)]+)\)': r'\sin(\1)',r'np\.cos\(([^)]+)\)': r'\cos(\1)',r'np\.tan\(([^)]+)\)': r'\tan(\1)',r'np\.exp\(([^)]+)\)': r'\exp(\1)',r'np\.sqrt\(([^)]+)\)': r'\sqrt{\1}',r'np\.log\(([^)]+)\)': r'\ln(\1)',r'np\.pi': r'\pi',r'\*\*': r'^',r'\*': r'\cdot',}# 应用所有替换,捕获可能的正则表达式错误for pattern, replacement in replacements.items():try:safe_str = re.sub(pattern, replacement, safe_str)except re.error as e:continue # 跳过有问题的替换# 处理分数 - 更稳健的方法if '/' in safe_str:# 只替换不包含字母的分数表达式if re.search(r'\d+\.?\d*/\d+\.?\d*', safe_str):parts = safe_str.split('/')if len(parts) == 2:numerator = parts[0].strip()denominator = parts[1].strip()safe_str = r'\frac{' + numerator + '}{' + denominator + '}'return safe_strdef save_image(self):"""保存当前图像"""try:filename = simpledialog.askstring("保存图像", "请输入文件名:", initialvalue="function_plot.png")if filename:if not filename.endswith(".png"):filename += ".png"self.fig.savefig(filename, dpi=150, bbox_inches="tight")messagebox.showinfo("成功", f"图像已保存至: {os.path.abspath(filename)}")except Exception as e:messagebox.showerror("保存错误", f"保存图像时出错: {e}")def main():root = tk.Tk()# 设置样式style = ttk.Style()style.configure("TFrame", background="#f5f5f5")style.configure("TLabelframe", background="#ffffff", relief="sunken")style.configure("TLabelframe.Label", background="#ffffff", font=("SimHei", 10, "bold"))style.configure("TButton", padding=5)# 尝试设置中文字体try:plt.rcParams["font.family"] = ["SimHei"]except:try:plt.rcParams["font.family"] = ["WenQuanYi Micro Hei"]except:try:plt.rcParams["font.family"] = ["Heiti TC"]except:try:plt.rcParams["font.family"] = ["Arial Unicode MS"]except:plt.rcParams["font.family"] = ["DejaVu Sans", "sans-serif"]print("警告: 未找到中文字体,图表文字可能无法正确显示")app = FunctionVisualizer(root)root.mainloop()if __name__ == "__main__":main()
相关文章:

Python数学可视化——显函数、隐函数及复杂曲线的交互式绘图技术
Python数学可视化——显函数、隐函数及复杂曲线的交互式绘图技术 一、引言 在科学计算和数据分析中,函数与方程的可视化是理解数学关系和物理现象的重要工具。本文基于Python的Tkinter和Matplotlib库,实现一个功能完善的函数与方程可视化工具ÿ…...

代码随想录打卡|Day51 图论(dijkstra(堆优化版)精讲、Bellman_ford 算法精讲)
图论part09 dijkstra(堆优化版)精讲(不熟悉) 代码随想录链接 题目链接 import java.util.*;class Edge {int to; // 邻接顶点int val; // 边的权重Edge(int to, int val) {this.to to;this.val val;} }class MyComparison implements Comparator<…...
【深度剖析】流处理系统性能优化:解决维表JOIN、数据倾斜与数据膨胀问题
目录 前言:为什么你的流处理作业总是慢? 一、维表JOIN优化:从普通连接到高性能查询 1.1 时态表的双面性 1.2 Lookup Join 优化 1.3 多表JOIN优化策略 二、数据倾斜:单分区也会遇到的隐形杀手 2.1 单分区数据倾斜 2.2 热点键打散技术 2.3 时间窗口预聚合 三、数据…...
PostgreSQL优化实践:从查询到架构的性能提升指南
## 引言 PostgreSQL作为先进的开源关系型数据库,在复杂查询处理与高并发场景中表现卓越,但不当的使用仍会导致性能瓶颈。本文系统性梳理优化路径,覆盖SQL编写、索引策略、参数调优等关键环节,配合代码示例与量化建议,…...

AI入门——AI大模型、深度学习、机器学习总结
以下是对AI深度学习、机器学习相关核心技术的总结与拓展,结合技术演进逻辑与前沿趋势,以全新视角呈现关键知识点 一、深度学习:从感知到认知的技术革命 核心突破:自动化特征工程的范式变革 深度学习通过多层神经网络架构&#x…...

【AI论文】论文转海报:迈向从科学论文到多模态海报的自动化生成
摘要:学术海报生成是科学交流中一项关键但具有挑战性的任务,需要将长上下文交织的文档压缩成单一的、视觉上连贯的页面。 为了应对这一挑战,我们引入了第一个用于海报生成的基准和度量套件,该套件将最近的会议论文与作者设计的海报…...

智慧零工平台前端开发实战:从uni-app到跨平台应用
智慧零工平台前端开发实战:从uni-app到跨平台应用 本文将详细介绍我如何使用uni-app框架开发一个支持微信小程序和H5的零工平台前端应用,包含技术选型、架构设计、核心功能实现及部署经验。 前言 在当今移动互联网时代,跨平台开发已成为提高开发效率的重要手段。本次我选择…...

【Linux】基础文件IO
🌟🌟作者主页:ephemerals__ 🌟🌟所属专栏:Linux 前言 无论是日常使用还是系统管理,文件是Linux系统中最核心的概念之一。对于初学者来说,理解文件是如何被创建、读取、写入以及存储…...
opencv调用模型
在 C++ 中,OpenCV 的 cv::dnn::readNetFromONNX() 函数用于加载 ONNX 格式的深度学习模型,将其转换为 OpenCV DNN 模块可用的网络对象 (cv::dnn::Net)。以下是详细说明: 函数原型 #include <opencv2/dnn.hpp>cv::dnn::Net cv::dnn::readNetFromONNX(const String&am…...
由浅入深一文详解同余原理
由浅入深一文详解同余原理 一、同余原理的基本概念1.1 同余的定义1.2 剩余类与完全剩余系 二、同余原理的基本性质2.1 自反性2.2 对称性2.3 传递性2.4 加减性2.5 乘性2.6 幂性 三、同余原理的运算与应用3.1 同余运算在计算中的应用3.2 密码学中的应用3.3 日期与周期问题 四、案…...

ESP-IDF 离线安装——同时存在多个版本以及进行版本切换的方法
一、离线安装包的下载方法 ESP-IDF离线安装包下载链接 我下载了下面三个版本进行测试 二、离线安装包的安装方法 1.创建文件夹 创建ESP-IDF文件夹,并为不同版本的IDF分别创建一个文件夹,如下图所示 2.双击离线安装包(以5.0版本为例&am…...

android 上位机调试软件-安卓串口 com ttl 调试——仙盟创梦IDE
在 Android 开发中,基于com.ttl库的串口调试 Web 编写意义非凡。它打破了硬件与软件之间的壁垒,让 Android 设备能够与外部串口设备通信。对于智能家居、工业控制等领域,这一功能使得手机或平板能成为控制终端,实现远程监控与操作…...

python打卡day42
Grad-CAM与Hook函数 知识点回顾 回调函数lambda函数hook函数的模块钩子和张量钩子Grad-CAM的示例 在深度学习中,我们经常需要查看或修改模型中间层的输出或梯度,但标准的前向传播和反向传播过程通常是一个黑盒,很难直接访问中间层的信息。PyT…...

XMOS以全新智能音频及边缘AI技术亮相广州国际专业灯光音响展
全球领先的边缘AI和智能音频解决方案提供商XMOS于5月27-30日亮相第23届广州国际专业灯光、音响展览会(prolight sound Guangzhou,以下简称“广州展”,XMOS展位号:5.2A66)。在本届展会上,XMOS将展出先进的音…...

Playwright 测试框架 - Node.js
🚀超全实战:基于 Playwright + Node.js 的自动化测试项目教程【附源码】 📌 本文适合自动化测试入门者 & 前端测试实战者。从零开始手把手教你搭建一个 Playwright + Node.js 项目,涵盖配置、测试用例编写、运行与调试、报告生成以及实用进阶技巧。建议收藏!👍 �…...

机器学习有监督学习sklearn实战二:六种算法对鸢尾花(Iris)数据集进行分类和特征可视化
本项目代码在个人github链接:https://github.com/KLWU07/Machine-learning-Project-practice 六种分类算法分别为逻辑回归LR、线性判别分析LDA、K近邻KNN、决策树CART、朴素贝叶斯NB、支持向量机SVM。 一、项目代码描述 1.数据准备和分析可视化 加载鸢尾花数据集&…...

vr中风--数据处理模型搭建与训练2
位置http://localhost:8888/notebooks/Untitled1-Copy1.ipynb # -*- coding: utf-8 -*- """ MUSED-I康复评估系统(增强版) 包含:多通道sEMG数据增强、混合模型架构、标准化处理 """ import numpy as np impor…...

鸿蒙next系统以后会取代安卓吗?
点击上方关注 “终端研发部” 设为“星标”,和你一起掌握更多数据库知识 官方可没说过取代谁谁,三足鼎立不好吗?三分天下,并立共存。 鸿蒙基于Linux,有人说套壳;ios/macos基于Unix,说它ios开源了…...

PolyGen:一个用于 3D 网格的自回归生成模型 论文阅读
[2002.10880] PolyGen:一个用于 3D 网格的自回归生成模型 --- [2002.10880] PolyGen: An Autoregressive Generative Model of 3D Meshes 图 2:PolyGen 首先生成网格顶点(左侧),然后基于这些顶点生成网格面࿰…...
约瑟夫问题 洛谷 - P1996
Description n个人围成一圈,从第一个人开始报数,数到 m 的人出列,再由下一个人重新从 1 开始报数,数到 m 的人再出圈,依次类推,直到所有的人都出圈,请输出依次出圈人的编号。 注意:本题和《深…...

系统思考:成长与投资不足
最近认识了一位95后年轻创业者,短短2年时间,他的公司从十几个人发展到几百人,规模迅速扩大。随着团队壮大,用户池也在持续扩大,但令人困惑的是,业绩增长却没有明显提升,甚至人效持续下滑。尽管公…...

快手可灵视频V1.6模型API如何接入免费AI开源项目工具
全球领先的视频生成大模型:可灵是首个效果对标 Sora 、面向用户开放的视频生成大模型,目前在国内及国际上均处于领先地位。快手视频生成大模型“可灵”(Kling),是全球首个真正用户可用的视频生成大模型,自面…...

数学建模期末速成 最短路径
关键词:Dijkstra算法 Floyd算法 例题 已知有6个村庄,各村的小学生人数如表所列,各村庄间的距离如图所示。现在计划建造一所医院和一所小学,问医院应建在哪个村庄才能使最远村庄的人到医院看病所走的路最短?又问小学建…...
【Netty系列】实现HTTP文件服务器
目录 一、完整代码实现 1. Maven依赖 (pom.xml) 2. 主启动类 (FileServer.java) 3. 通道初始化类 (FileServerInitializer.java) 4. 核心业务处理器 (FileServerHandler.java) 二、代码关键解释 1. 架构分层 2. 安全防护机制 3. 文件传输优化 4. 目录列表生成 三、运…...

Java开发经验——阿里巴巴编码规范实践解析7
摘要 本文主要解析了阿里巴巴 Java 开发中的 SQL 编码规范,涉及 SQL 查询优化、索引建立、字符集选择、分页查询处理、外键与存储过程的使用等多个方面,旨在帮助开发者提高代码质量和数据库操作性能,避免常见错误和性能陷阱。 1. 【强制】业…...

权威认证与质量保障:第三方检测在科技成果鉴定测试中的核心作用
科技成果鉴定测试是衡量科研成果技术价值与应用潜力的关键环节,其核心目标在于通过科学验证确保成果的可靠性、创新性和市场适配性。第三方检测机构凭借其独立性、专业性和权威性,成为科技成果鉴定测试的核心支撑主体。本文从测试流程、第三方检测的价值…...
混和效应模型在医学分析中的应用
混合效应模型(Mixed Effects Model),又称多层模型或随机效应模型,因其能同时分析固定效应(群体平均趋势)和随机效应(个体或组间差异),在医学研究中广泛应用于处理具有层次…...
架构分享|三层存储架构加速云端大模型推理
作者简介 Nilesh Agarwal,Inferless 联合创始人&CTO 关于Inferless Inferless :无服务器 GPU 推理无需管理服务器即可扩展机器学习推理,轻松部署复杂的自定义模型。获得Sequoia、Antler 和 Blume Ventures 的支持。 大语言模型(LLM&a…...

Perforce P4产品简介:无限扩展+全球协作+安全管控+工具集成(附下载)
本产品简介由Perforce中国授权合作伙伴——龙智编辑整理,旨在带您快速了解Perforce P4版本控制系统的强大之处。 世界级无限可扩展的版本控制系统 Perforce P4(原Helix Core)是业界领先的版本控制平台,备受19家全球Top20 AAA级游…...

网络协议入门:TCP/IP五层模型如何实现全球数据传输?
🔍 开发者资源导航 🔍🏷️ 博客主页: 个人主页📚 专栏订阅: JavaEE全栈专栏 内容: 网络初识什么是网络?关键概念认识协议五元组 协议分层OSI七层模型TCP/IP五层(四层&…...