数学函数画图器

Python实现数学函数画图器

设计一个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)) ,注:为了输入方便考虑,不使用θ和ρ。

运行效果

数学函数画图器_第1张图片

数学函数画图器_第2张图片

这个程序涉及到多个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
            
            # 打开文件保存

你可能感兴趣的:(Python学习,数学广角,编程实践系列,python,编程实践,数学广角)