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 擴展方向

  1. 支持極坐標繪圖
  2. 添加導數/積分可視化
  3. 集成3D繪圖功能(使用mpl_toolkits.mplot3d)
  4. 開發數據導入功能(CSV/Excel)

九、總結

本文實現的函數可視化工具具備以下特點:

  1. 安全性:通過表達式過濾和白名單機制防止代碼注入
  2. 交互性:支持實時切換函數類型、調整參數、縮放圖表
  3. 擴展性:預設函數與自定義輸入結合,方便擴展新類型
  4. 專業性:支持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()

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/83351.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/83351.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/83351.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【MySQL基礎】庫的操作:創建、刪除與管理數據庫

MySQL學習&#xff1a; https://blog.csdn.net/2301_80220607/category_12971838.html?spm1001.2014.3001.5482 前言&#xff1a; 在上一篇我們已經講解了數據庫的基本內容&#xff0c;相信大家對數據庫已經有了一些自己的理解&#xff0c;從這篇開始我們就開始正式進入如何…

Linux服務器系統配置初始化腳本

服務器系統配置初始化腳本 #!/bin/bash set -euo pipefail # 安全設置&#xff1a;遇錯退出、未定義變量報錯、管道錯誤處理# 設置時區并同步時間 timedatectl set-timezone Asia/Shanghai >/dev/null || ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime# 安裝c…

Milvus單機模式安裝和試用

1.安裝ollama的package包&#xff1b; # install package pip install -U langchain-ollama2.我們直接使用ChatOllama實例化模型&#xff0c;并通過invoke進行調用&#xff1b; from langchain_ollama import ChatOllamallm ChatOllama(model"deepseek-r1") messa…

秒出PPT正式改名秒出AI,開啟AI賦能新體驗!

在現代辦公環境中&#xff0c;借助智能工具提升工作效率已經成為趨勢。秒出AI作為一款集AI PPT制作、動畫、巨幕、視頻、設計以及智能簡歷功能于一體的綜合辦公平臺&#xff0c;為用戶提供一站式智能內容生成解決方案&#xff0c;極大地簡化了內容創作流程。 1. AI驅動的一鍵P…

Rust 學習筆記:發布一個 crate 到 crates.io

Rust 學習筆記&#xff1a;發布一個 crate 到 crates.io Rust 學習筆記&#xff1a;發布一個 crate 到 crates.io提供有用的文檔注釋常用標題文檔注釋作為測試注釋所包含的項目 使用 pub use 導出一個方便的公共 API設置 crates.io 賬戶添加 metadata 到一個新的 crate發布到 c…

C++輸入與輸出技術詳解

文章目錄 引言一、C標準輸入輸出流1.1 cin與cout1.2 cerr與clog 二、C風格輸入輸出函數2.1 scanf與printf2.2 fgets與puts 三、輸入輸出優化四、總結 引言 在C編程中&#xff0c;輸入與輸出&#xff08;I/O&#xff09;操作是程序與用戶、文件或其他系統組件交互的核心環節。C…

安全編碼與AI接口權限控制

安全編碼與AI接口權限控制 在AI系統中,模型服務的開放接口往往涉及敏感數據、核心算法與算力資源,如果缺乏有效的安全編碼與權限控制機制,極易引發數據泄露、濫用調用或非法操作等問題。本節將從“接口安全策略”“權限驗證流程”“Token管控機制”“多租戶身份隔離”四個方…

redis五種數據結構詳解(java實現對應的案例)

一、簡述 Redis是一款高性能的鍵值對存儲數據庫&#xff0c;它支持五種基本數據類型&#xff0c;分別是字符串(String)、列表(List)、哈希(Hash)、集合(Set)、有序集合(Sorted Set)。 二、五種基本數據類型 2.1 字符串(String) String是Redis最基本的類型&#xff0c;一個key對…

大數據-273 Spark MLib - 基礎介紹 機器學習算法 決策樹 分類原則 分類原理 基尼系數 熵

點一下關注吧&#xff01;&#xff01;&#xff01;非常感謝&#xff01;&#xff01;持續更新&#xff01;&#xff01;&#xff01; 大模型篇章已經開始&#xff01; 目前已經更新到了第 22 篇&#xff1a;大語言模型 22 - MCP 自動操作 FigmaCursor 自動設計原型 Java篇開…

第十一章 Java基礎-繼承

文章目錄 1.繼承來源2.繼承特點3.子類能繼承父類中哪些內容1.繼承來源 是為了解決代碼的重復冗余。

Axure項目實戰:駕駛艙(數據一張圖)制作教程

親愛的小伙伴,在您瀏覽之前,煩請關注一下,在此深表感謝!如有幫助請訂閱專欄! Axure產品經理精品視頻課已登錄CSDN可點擊學習https://edu.csdn.net/course/detail/40420 視頻展示(本文第三部分含所有echarts示例JS代碼,可復制使用): Axure項目實戰:駕駛艙(數據一張圖…

針對Python開發的工具推薦及分析,涵蓋集成開發環境(IDE)、輕量級工具、在線開發平臺、代碼管理工具等)

以下是針對Python開發的工具推薦及全面分析&#xff0c;涵蓋集成開發環境&#xff08;IDE&#xff09;、輕量級工具、在線開發平臺、代碼管理工具等&#xff0c;結合不同場景和需求進行分類說明&#xff1a; 目錄 一、集成開發環境&#xff08;IDE&#xff09; 1. PyCharm 2…

不使用綁定的方法

public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); // 初始設置 A 控件的寬度 ControlA.Width ControlB.Width / 2; // 監聽 B 控件的 SizeChanged 事件 ControlB.SizeChanged (sender, e) > { ControlA.Width ControlB.Actual…

DDR5 ECC詳細原理介紹與基于協議講解

本文篇幅較長,涉及背景原理介紹方便大家理解其運作方式 以及 基于DDR5協議具體展開介紹。 背景原理介紹 上圖參考:DDR 內存中的 ECC 寫入操作時,On-die ECC的工作過程如下: SoC將需要寫入到Memory中的數據發送給控制器控制器將需要寫入的數據直接發送給DRAM芯片在DDR5 DR…

基于springboot的益智游戲系統的設計與實現

博主介紹&#xff1a;java高級開發&#xff0c;從事互聯網行業六年&#xff0c;熟悉各種主流語言&#xff0c;精通java、python、php、爬蟲、web開發&#xff0c;已經做了六年的畢業設計程序開發&#xff0c;開發過上千套畢業設計程序&#xff0c;沒有什么華麗的語言&#xff0…

STM32軟件spi和硬件spi

核心觀點 本文主要介紹了SPI通信的兩種實現方式&#xff1a;軟件SPI和硬件SPI。詳細闡述了SPI通信協議的基本概念、硬件電路連接方式、移位示意圖、時序基本單元以及四種工作模式。同時&#xff0c;對W25Q64模塊進行了詳細介紹&#xff0c;包括其硬件電路、框圖以及操作注意事…

NLP學習路線圖(十五):TF-IDF(詞頻-逆文檔頻率)

在自然語言處理&#xff08;NLP&#xff09;的浩瀚宇宙中&#xff0c;TF-IDF&#xff08;詞頻-逆文檔頻率&#xff09; 猶如一顆恒星&#xff0c;雖古老卻依然璀璨。當ChatGPT、BERT等大模型光芒四射時&#xff0c;TF-IDF作為傳統方法的代表&#xff0c;其簡潔性、高效性與可解…

愛其實很簡單

初春時&#xff0c;元元買來兩只芙蓉鳥。一只白色的&#xff0c;是雄鳥&#xff1b;另一只黃色的&#xff0c;是雌鳥。 每天清晨日出之前&#xff0c;雄鳥便開始“啁啾——啁啾”地啼鳴&#xff0c;鳴聲清脆婉轉&#xff0c;充滿喜悅&#xff0c;仿佛在迎接日出&#xff0c;又…

CentOS 7 環境下部署 LAMP

在 CentOS 7 環境下部署 LAMP&#xff08;Linux Apache MySQL 5.7 PHP 7.4&#xff09; 環境的詳細步驟如下&#xff1a; 1. 系統準備 1.1 更新系統 sudo yum update -y 1.2 安裝依賴 sudo yum install -y gcc pcre pcre-devel zlib zlib-devel openssl openssl-devel e…

如何查看電腦電池性能

檢查電腦電池性能的方法如下&#xff1a; 按下winR鍵&#xff0c;輸入cmd回車&#xff0c;進入命令行窗口 在命令行窗口輸入powercfg /batteryreport 桌面雙擊此電腦&#xff0c;把剛剛復制的路徑粘貼到文件路徑欄&#xff0c;然后回車 回車后會自動用瀏覽器打開該報告 紅…