设计一个GUI界面的函数画图器
输入函数,如
y=2x^2+1.3x+1
(x/3)^2+(y/2)^2=1
1/4x^2+1/4xy+1/3y^2-3/8x+1/9y-2=0
y-2x^(1/3)=0
解决了一般分数幂x^(a/b),如y= x^(1/3)图像不全问题,根据我原先另一个版本设计情况这是一个难点,解决下了很大功夫,成功解决。
①对于一般函数(方程),自变量和因变量分别用 x 和 y 表示。
②对于参数方程,自变量用 t 表示,因变量用 x 和 y 表示,两个参数方程用英文分号分隔。如 x=3cos(t) ; y=4sin(t)
③对于极坐标方程,自变量用 t 表示,因变量用 r 表示。如 r=4/(1+0.5cos(t)) ,注:为了输入方便考虑,不使用θ和ρ。
运行效果
这个程序涉及到多个Python模块/包/库。下面简要说明功能及哪些需要安装:
numpy:这是一个用于科学计算的基础库,提供了多维数组对象以及对数组进行操作的大量函数。通常需要通过pip安装(除非你的环境中已经预装了它)。
matplotlib及其组件(matplotlib.pyplot, matplotlib.backends.backend_tkagg, NavigationToolbar2Tk):matplotlib是一个绘图库,用于生成出版质量的图表。pyplot是其提供的一种类似MATLAB的绘图接口,而后两个模块是与Tkinter结合使用时需要用到的部分。需要安装(除非你的环境中已经预装了它)。
tkinter:这是Python的标准GUI库,通常随Python解释器一起分发,因此不需要单独安装。但在某些Linux发行版上可能需要单独安装Python-tk包。
sympy:这是一个用于符号数学计算的Python库。如果你需要进行代数、微积分等符号运算,就需要安装(除非你的环境中已经预装了它)。
re:这是Python的内置模块,用于正则表达式操作,因此不需要安装。
math:这是Python的内置模块,提供了数学函数定义(如三角函数、对数等),也不需要安装。
fractions:这也是一个Python标准库,用于分数计算,不需要安装。
总之,你需要通过pip或其他方式安装的是numpy, matplotlib, 和 sympy。其他提到的模块要么是Python的标准库(如re, math, fractions),要么通常是随Python一起提供的。
打包好的exe文件,地址:https://download.csdn.net/download/cnds123/90796911
源码如下:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import sympy as sp
import re
import math
import fractions
class HelpWindow:
def __init__(self, parent):
self.window = tk.Toplevel(parent)
self.window.title("使用帮助")
self.window.geometry("620x590")
self.window.resizable(True, True)
# self.window.transient(parent) # 设置它将限制了窗口的某些功能,如没有最小化等
# self.window.grab_set() # 模态窗口
# 保持窗口始终在最前面,但不阻止主窗口操作
self.window.attributes('-topmost', True)
# 创建帮助内容框架
main_frame = ttk.Frame(self.window, padding="20")
main_frame.pack(fill=tk.BOTH, expand=True)
# 标题
title_label = ttk.Label(main_frame, text="函数画图器使用说明", font=("Arial", 14, "bold"))
title_label.pack(pady=(0, 20))
# 创建文本区域
help_text = """
函数画图器支持三种类型的函数绘制:
1. 普通函数(方程)
• 自变量和因变量分别用 x 和 y 表示
• 显式函数格式:y = f(x)
例如:y = 2x^2 + 1.3x + 1
y=abs(x^2+x-2)+x
• 隐函数格式:F(x,y) = G(x,y)
例如:(x/3)^2 + (y/2)^2 = 1
y^2-(x^3+1)=0 或 y^2=(x^3+1)
2. 参数方程
• 自变量用 t 表示,因变量用 x 和 y 表示
• 两个参数方程用英文分号分隔
• 格式:x = f(t); y = g(t)
例如:x = 3cos(t); y = 4sin(t)
x=4Cos(2t)Sin(t) ; y=4Sin(2t)Sin(t)
3. 极坐标方程
• 自变量用 t 表示,因变量用 r 表示
• 格式:r = f(t)
例如:r = 4/(1+0.5cos(t))
r=3(Sin(2t)+2Sin(3t))
这个程序支持的数学函数和数学常量是通过NumPy库提供的。有所调整,解决了一般分数幂x^(a/b),如y=x^(1/3)图像不全问题(这是一个难点)。
支持的数学函数:
sin(x), cos(x), tan(x), exp(x), log(x), sqrt(x), abs(x)
支持的常量:
pi, e
例如:y=e^x+pi/2
使用步骤:
1. 选择函数类型
2. 输入函数表达式
3. 设置坐标范围
4. 点击"绘制图像"按钮
“显示模式”按钮是一个切换式按钮:“显示模式:等比例”绘图区显示为正方形,“显示模式:自适应”绘图区显示处于自适应状态。你可以画一个圆x^2 + y^2 = 9,在两种模式看看差别。
"""
# 使用文本框显示帮助内容,允许滚动
text_frame = ttk.Frame(main_frame)
text_frame.pack(fill=tk.BOTH, expand=True)
scrollbar = ttk.Scrollbar(text_frame)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
help_text_widget = tk.Text(text_frame, wrap=tk.WORD, yscrollcommand=scrollbar.set)
help_text_widget.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
help_text_widget.insert(tk.END, help_text)
help_text_widget.config(state=tk.DISABLED) # 设置为只读
scrollbar.config(command=help_text_widget.yview)
# 关闭按钮
close_button = ttk.Button(main_frame, text="关闭", command=self.window.destroy)
close_button.pack(pady=10)
# 居中窗口
self.center_window()
def center_window(self):
self.window.update_idletasks()
width = self.window.winfo_width()
height = self.window.winfo_height()
x = (self.window.winfo_screenwidth() // 2) - (width // 2)
y = (self.window.winfo_screenheight() // 2) - (height // 2)
self.window.geometry('{}x{}+{}+{}'.format(width, height, x, y))
class FunctionPlotter:
def __init__(self, root):
self.root = root
self.root.title("函数画图器 - V1.02 设计:WKJ")
self.root.geometry("1000x800")
# 设置matplotlib中文支持
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei','SimHei','DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
# 创建主框架
self.main_frame = ttk.Frame(root, padding="10")
self.main_frame.pack(fill=tk.BOTH, expand=True)
# 创建一个水平框架来包含参数区和函数列表
top_frame = ttk.Frame(self.main_frame)
top_frame.pack(fill=tk.X, pady=5)
# 创建参数区
self.param_frame = ttk.LabelFrame(top_frame, text="参数设置", padding="10")
self.param_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 5))
# 函数输入框
ttk.Label(self.param_frame, text="函数表达式:").grid(row=0, column=0, sticky=tk.W, pady=5)
self.func_entry = ttk.Entry(self.param_frame, width=40)
self.func_entry.grid(row=0, column=1, sticky=tk.W, pady=5)
self.func_entry.insert(0, "y=2x^2+1.3x+1")
# 函数类型选择
ttk.Label(self.param_frame, text="函数类型:").grid(row=1, column=0, sticky=tk.W, pady=5)
self.func_type = tk.StringVar(value="普通函数")
self.type_combo = ttk.Combobox(self.param_frame, textvariable=self.func_type,
values=["普通函数", "参数方程", "极坐标方程"])
self.type_combo.grid(row=1, column=1, sticky=tk.W, pady=5)
# 在这里添加画笔线条宽度选择
ttk.Label(self.param_frame, text="画笔线条宽度:").grid(row=2, column=0, sticky=tk.W, pady=5)
self.linewidth_var = tk.DoubleVar(value=1.5)
linewidth_spin = ttk.Spinbox(self.param_frame, from_=0.5, to=4.0, increment=0.5,
textvariable=self.linewidth_var, width=5)
linewidth_spin.grid(row=2, column=1, sticky=tk.W, pady=5)
# 坐标范围设置
range_frame = ttk.Frame(self.param_frame)
range_frame.grid(row=3, column=0, columnspan=2, sticky=tk.W, pady=5) # 行号改为3
ttk.Label(range_frame, text="x范围:").grid(row=0, column=0, sticky=tk.W)
self.x_min = ttk.Entry(range_frame, width=8)
self.x_min.insert(0, "-10")
self.x_min.grid(row=0, column=1, sticky=tk.W, padx=2)
ttk.Label(range_frame, text="到").grid(row=0, column=2, sticky=tk.W)
self.x_max = ttk.Entry(range_frame, width=8)
self.x_max.insert(0, "10")
self.x_max.grid(row=0, column=3, sticky=tk.W, padx=2)
ttk.Label(range_frame, text="y范围:").grid(row=0, column=4, sticky=tk.W, padx=(20, 0))
self.y_min = ttk.Entry(range_frame, width=8)
self.y_min.insert(0, "-10")
self.y_min.grid(row=0, column=5, sticky=tk.W, padx=2)
ttk.Label(range_frame, text="到").grid(row=0, column=6, sticky=tk.W)
self.y_max = ttk.Entry(range_frame, width=8)
self.y_max.insert(0, "10")
self.y_max.grid(row=0, column=7, sticky=tk.W, padx=2)
# 参数方程和极坐标方程的t范围
ttk.Label(range_frame, text="t范围:").grid(row=1, column=0, sticky=tk.W, pady=(5, 0))
self.t_min = ttk.Entry(range_frame, width=8)
self.t_min.insert(0, "0")
self.t_min.grid(row=1, column=1, sticky=tk.W, padx=2, pady=(5, 0))
ttk.Label(range_frame, text="到").grid(row=1, column=2, sticky=tk.W, pady=(5, 0))
self.t_max = ttk.Entry(range_frame, width=8)
self.t_max.insert(0, "2*pi")
self.t_max.grid(row=1, column=3, sticky=tk.W, padx=2, pady=(5, 0))
# 按钮区域
button_frame = ttk.Frame(self.param_frame)
button_frame.grid(row=4, column=0, columnspan=2, pady=10)
# 绘图按钮
self.plot_button = ttk.Button(button_frame, text="绘制图像", command=self.plot_function)
self.plot_button.pack(side=tk.LEFT, padx=5)
# 帮助按钮
self.help_button = ttk.Button(button_frame, text="使用帮助", command=self.show_help)
self.help_button.pack(side=tk.LEFT, padx=5)
# 清除按钮
self.clear_button = ttk.Button(button_frame, text="清除图像", command=self.clear_plot)
self.clear_button.pack(side=tk.LEFT, padx=5)
# 保存图像按钮
self.save_button = ttk.Button(button_frame, text="保存图像", command=self.save_figure)
self.save_button.pack(side=tk.LEFT, padx=5)
# 显示模式切换按钮(默认为等比例显示)
self.is_square_aspect = True
self.aspect_button = ttk.Button(button_frame, text="显示模式:等比例", command=self.toggle_aspect)
self.aspect_button.pack(side=tk.LEFT, padx=5)
# 添加函数列表显示区域 - 放在参数区域的右侧
func_list_frame = ttk.LabelFrame(top_frame, text="已绘制函数列表", padding="10")
func_list_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=(5, 0))
# 创建文本框和滚动条的容器框架
list_container = ttk.Frame(func_list_frame)
list_container.pack(fill=tk.BOTH, expand=True)
# 使用文本框显示函数列表
self.func_list_text = tk.Text(list_container, height=10, width=40, wrap=tk.WORD)
# 创建滚动条并设置它始终可见
scrollbar = ttk.Scrollbar(list_container, command=self.func_list_text.yview)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y) # 先包装滚动条
self.func_list_text.configure(yscrollcommand=scrollbar.set)
self.func_list_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # 后包装文本框
# 设置文本框为只读
self.func_list_text.config(state=tk.DISABLED)
# 创建绘图区
self.plot_frame = ttk.LabelFrame(self.main_frame, text="绘图区", padding="10")
self.plot_frame.pack(fill=tk.BOTH, expand=True, pady=5)
# 创建matplotlib图形
self.fig, self.ax = plt.subplots(figsize=(8, 6))
self.canvas = FigureCanvasTkAgg(self.fig, master=self.plot_frame)
self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
# 添加matplotlib工具栏
toolbar_frame = ttk.Frame(self.plot_frame)
toolbar_frame.pack(fill=tk.X)
toolbar = NavigationToolbar2Tk(self.canvas, toolbar_frame)
toolbar.update()
# 简短提示
tip_text = "提示: 点击“绘制图像”按钮添加新函数,函数会显示在右侧列表中"
self.tip_label = ttk.Label(self.main_frame, text=tip_text, foreground="gray")
self.tip_label.pack(fill=tk.X, pady=2)
# 初始化绘图区,但不立即绘图
self.init_plot()
# 函数计数器
self.function_counter = 0
# 颜色循环列表
self.colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd',
'#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf']
# 强制布局更新
self.root.update_idletasks()
# 设置初始状态
if self.is_square_aspect:
self.aspect_button.config(text="显示模式:等比例")
self.ax.set_aspect('equal')
else:
self.aspect_button.config(text="显示模式:自适应")
self.ax.set_aspect('auto')
self.fig.tight_layout()
self.canvas.draw()
def update_grid(self):
"""更新网格线,确保水平和垂直方向的网格线数量相同"""
# 获取当前坐标范围
x_min, x_max = self.ax.get_xlim()
y_min, y_max = self.ax.get_ylim()
# 固定的网格线数量(可以根据需要调整)
grid_count = 10
# 设置主刻度定位器
self.ax.xaxis.set_major_locator(plt.MaxNLocator(grid_count))
self.ax.yaxis.set_major_locator(plt.MaxNLocator(grid_count))
# 绘制网格
self.ax.grid(True)
def toggle_aspect(self):
"""在等比例和自适应显示模式之间切换"""
self.is_square_aspect = not self.is_square_aspect
if self.is_square_aspect:
# 切换到等比例模式
self.aspect_button.config(text="显示模式:等比例")
self.set_square_aspect()
else:
# 切换到自适应模式
self.aspect_button.config(text="显示模式:自适应")
self.ax.set_aspect('auto')
# 添加以下行以确保布局正确
self.fig.tight_layout()
self.canvas.draw()
def set_square_aspect(self):
"""设置绘图区域为正方形显示(等比例坐标轴)"""
try:
# 获取当前坐标范围
x_min, x_max = self.ax.get_xlim()
y_min, y_max = self.ax.get_ylim()
# 设置等比例
self.ax.set_aspect('equal')
# 确保原始范围不变
self.ax.set_xlim(x_min, x_max)
self.ax.set_ylim(y_min, y_max)
# 调整图形布局以适应正方形显示
self.fig.tight_layout()
# 重新绘制
self.canvas.draw()
except Exception as e:
messagebox.showerror("错误", f"设置正方形显示时出错: {str(e)}")
def show_help(self):
"""显示帮助窗口"""
HelpWindow(self.root)
def clear_plot(self):
"""清除当前图像和函数列表"""
self.init_plot()
# 清除函数列表
self.func_list_text.config(state=tk.NORMAL)
self.func_list_text.delete(1.0, tk.END)
self.func_list_text.config(state=tk.DISABLED)
# 重置函数计数器
self.function_counter = 0
def init_plot(self):
"""初始化绘图区但不绘制实际图形"""
self.ax.clear()
self.ax.grid(True)
self.ax.axhline(y=0, color='k', linestyle='-', alpha=0.3)
self.ax.axvline(x=0, color='k', linestyle='-', alpha=0.3)
self.ax.set_xlim(-10, 10)
self.ax.set_ylim(-10, 10)
self.ax.set_title("点击“绘制图像”按钮以绘制函数图像")
# 应用当前的显示模式
if self.is_square_aspect:
self.ax.set_aspect('equal')
else:
self.ax.set_aspect('auto')
# 更新网格线
self.update_grid()
# 确保布局正确
self.fig.tight_layout()
self.canvas.draw()
def add_function_to_list(self, func_str, func_type, color):
"""添加函数到列表显示区域"""
self.function_counter += 1
# 根据函数类型生成描述
if func_type == "普通函数":
description = f"函数 {self.function_counter}: {func_str}"
elif func_type == "参数方程":
description = f"参数方程 {self.function_counter}: {func_str}"
else: # 极坐标方程
description = f"极坐标方程 {self.function_counter}: {func_str}"
# 添加到文本框
self.func_list_text.config(state=tk.NORMAL)
# 使用颜色标记
color_hex = color if isinstance(color, str) else '#%02x%02x%02x' % (
int(color[0]*255), int(color[1]*255), int(color[2]*255))
# 创建颜色标签
tag_name = f"color_{self.function_counter}"
self.func_list_text.tag_configure(tag_name, foreground=color_hex)
# 插入文本
self.func_list_text.insert(tk.END, description + "\n", tag_name)
self.func_list_text.config(state=tk.DISABLED)
return self.function_counter
def save_figure(self):
"""保存当前图像到文件"""
try:
# 获取当前图形是否有内容
if len(self.ax.get_lines()) == 0 and len(self.ax.collections) == 0:
messagebox.showinfo("提示", "当前没有图像可保存,请先绘制图像。")
return
# 打开文件保存