精华内容
下载资源
问答
  • 前一章节,我们解读了tkinter内嵌Matplotlib的教程,了解其内嵌的原理,就是在tkinter创建matplotlib的画布控件,再利用其返回的画布对象进行绘图,其他附加功能,使用tkinter控件实现。 (一)对matplotlib画布的...

    目录

    前言

    前一章节,我们解读了tkinter内嵌Matplotlib的教程,了解其内嵌的原理,就是在tkinter创建matplotlib的画布控件,再利用其返回的画布对象进行绘图,其他附加功能,使用tkinter控件实现。

    (一)对matplotlib画布的封装:

    (1)说明:

    我们希望对官方的实例代码进行封装成一个函数,并返回一个画布对象,外部再调用该函数,并获取画布对象,进行绘制操作。

    (2)封装后的代码:

    """
        画布文件,实现绘图区域的显示,并返回画布的对象。
    """
    import tkinter as tk
    

    # 创建画布需要的库
    from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
    # 创建工具栏需要的库
    from matplotlib.backends.backend_tkagg import NavigationToolbar2Tk
    # 快捷键需要的库
    from matplotlib.backend_bases import key_press_handler
    # 导入画图常用的库
    from matplotlib.figure import Figure

    def plot_fun(root):
    “”"
    该函数实现的是内嵌画布,不负责画图,返回画布对象。
    :param root:父亲控件对象, 一般是容器控件或者窗体
    :return: 画布对象
    “”"

    # 画布的大小和分别率
    fig = Figure(dpi=100)
    axs = fig.add_subplot(111)

    (二)思路分析:

    1.需求说明:

    (1)背景:

    作为学生的我们,你是否有那么一个场景,唉……,这个数学函数好难求哦,要是知道它的图像这么画就好了。

    (2)需求:

    给出数学表达式,绘制出该数学表达式的函数曲线,一来可以观察函数的变化趋势,二来可以根据两条曲线的交点,来求解出方程的大致结果。

    2.框架的设置:

    (1)说明:

    分模块编程,向来是众人所提倡的,再python里更是很好的实现。

    再动手敲代码之前,我们先来大致的设置一下,小项目的框架。

    (2)框架图解:

    01.png

    3.文件说明:

    (1)main.py

    主程序文件,负责程序的启动与结束和窗体的大致设置。

    (2)widget.py

    控件文件,负责程序控件的创建与布局

    (3)figure.py

    画布文件,实现绘图区域的显示,并返回画布的对象。

    (4)plot.py

    绘图文件,负责函数曲线的绘制

    (三)各文件的源代码

    1.main.py

    """
        主程序文件,负责程序的启动与结束和窗体的大致设置。
    """
    

    import tkinter as tk
    import widget

    def win_w_h(root):
    “”"
    控制窗口的大小和出现的位置
    :param root:
    :return: 窗口的大小和出现的位置
    “”"

    # 设置标题:
    win.title(“数学函数绘图”)
    win = tk.Tk()
    # 大小 位置
    win.geometry("%dx%d+%d+%d" % (win_w_h(win)))

    # 创建一个容器, 没有画布时的背景
    frame1 = tk.Frame(win, bg="#c0c0c0")
    frame1.place(relx=0.00, rely=0.05, relwidth=0.62, relheight=0.89)

    # 控件区
    frame2 = tk.Frame(win, bg="#808080")
    frame2.place(relx=0.62, rely=0.05, relwidth=0.38, relheight=0.89)

    # 调用控件模块
    widget.widget_main(win, frame2)
    win.mainloop()

    2.widget.py

    """
        控件文件,负责程序控件的创建与布局
    """
    import tkinter as tk
    # 对话框所需的库
    import tkinter.messagebox as mb
    # 画布文件
    import figure
    # 绘图文件
    import plot
    

    def widget_main(win, root):
    “”"
    负责程序控件的创建与布局
    :param win: 主窗体的对象。
    :param root: 绘图区的容器对象。
    :return: 无
    “”"

    # 控件区的容器对象
    frame1 = None

    # =功能区==================
    # 绘图的功能函数
    def plot_f():
    string = entry.get()
    # 判断输入框是否为空
    if string “”:
    mb.showerror(“提示”, “没有输入值,请重新输入:”)
    else:
    # 判断是否已经创建画布
    if frame1None:
    mb.showerror(“提示”, “没有创建画布,不能画图,请先创建画布”)
    else:
    axs = figure.plot_fun(frame1)
    plot.plot_main(string, axs)

    # =控件区==========
    # 标签控件
    label = tk.Label(root,
    text=“请输入含x的数学公式:”,
    font=(“微软雅黑”, 18),
    fg=“blue”)
    label.place(relx=0.2, rely=0.1)

    3.figure.py

    """
        画布文件,实现绘图区域的显示,并返回画布的对象。
    """
    import tkinter as tk
    

    # 创建画布需要的库
    from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
    # 创建工具栏需要的库
    from matplotlib.backends.backend_tkagg import NavigationToolbar2Tk
    # 快捷键需要的库
    from matplotlib.backend_bases import key_press_handler
    # 导入画图常用的库
    from matplotlib.figure import Figure

    def plot_fun(root):
    “”"
    该函数实现的是内嵌画布,不负责画图,返回画布对象。
    :param root:父亲控件对象, 一般是容器控件或者窗体
    :return: 画布对象
    “”"

    # 画布的大小和分别率
    fig = Figure(dpi=100)
    axs = fig.add_subplot(111)

    4.plot.py

    """
        绘图文件,负责函数曲线的绘制
    """
    import numpy as np
    

    def plot_main(string, plt):
    “”"
    负责函数曲线的绘制
    :param string: 数学表达式
    :param plt: 画布的对象
    :return: 无
    “”"

    list_expr = []
    list_expr = string.split(",")
    string1 = []
    for sub_expr in list_expr:
    string1.append(sub_expr)
    x = np.linspace(-10, 10, 100)
    y = []
    num = string.count(‘x’)
    for i in x:
    t = (i, ) * num
    string = string.replace(“x”, “(%f)”)
    i = eval(string % t)
    y.append(i)
    plt.plot(x, y)
    plt.grid(True)
    plt.legend(labels=string1)

    (四)文件结构

    四个文件均处于同一个文件夹下,用main.py来运行。

    展开全文
  • Python绘制三阶贝塞尔曲线

    千次阅读 2019-09-01 21:13:09
    利用Python自带的Tkinter绘制函数曲线

     

    作者本科毕业设计是做机器人轨迹跟踪控制,轨迹由函数曲线来描述,本文选取三阶贝塞尔曲线为代表进行讲解,并展示部分基于Python Tkinter的实现代码。

    首先简单了解一下什么是贝塞尔曲线(余弦函数曲线我就不多说了哈!),贝塞尔曲线又称贝兹曲线,是法国工程师皮埃尔.贝塞尔于1962年发表。贝塞尔曲线广泛应用于二维绘图软件,早期用于汽车车体设计。

    三阶贝塞尔曲线由如下方程描述:

    B(t)=P_{0}(1-t)^{^{3}}+3P_{1}t(1-t)^{^{2}}+3P_{2}t^{2}(1-t)+P_{3}t^{3}      其中t的范围是0到1的闭区间。P0和P3是三阶贝塞尔曲线的起点和终点,P1和P2是曲线的控制点。

    然后我们讲一下计算机绘制曲线的原理。从数学定义上,一条连续函数曲线有无数个点,从算法的特点将,算法具有有穷性。所以我们不可能把所有的点全部刻画在屏幕上。另一方面,计算机的屏幕像素是离散的,无法表示连续的曲线。于是引入一个概念,那就是微分思想。将曲线分为一个个小段,将曲线“化曲为直”。

    最后说明一下计算机屏幕的坐标系。数学里的笛卡尔坐标系通常以水平向右为x轴正方向,垂直于x轴向上为y轴正方向。而计算机屏幕表示像素点时,其坐标原点位于屏幕左上角,x轴水平向右,而y轴垂直于x轴向下。

    下面展示贝赛尔曲线函数代码:

    def tri_bezier(p1,p2,p3,p4,t):
        parm_1 = (1-t)**3
        parm_2 = 3*(1-t)**2 * t
        parm_3 = 3 * t**2 * (1-t)
        parm_4 = t**3
    
        px = p1[0] * parm_1 + p2[0] * parm_2 + p3[0] * parm_3 + p4[0] * parm_4
        py = p1[1] * parm_1 + p2[1] * parm_2 + p3[1] * parm_3 + p4[1] * parm_4
        
        return (px,py)

    效果展示:

    以上图像没有进行屏幕坐标系向笛卡尔坐标系转换,具体如何通过Tkinter库实现,后面我会更新博客,敬请期待!才疏学浅,难免有错误疏漏之处,还请各位大佬提出指正!

    展开全文
  • 主要介绍了Python实现在tkinter中使用matplotlib绘制图形的方法,结合实例形式分析了Python使用tkinter与matplotlib进行正弦曲线图形绘制的相关操作技巧,需要的朋友可以参考下
  • 用Python的Tkinter、Numpy、Matplotlib库对曲线拟合的一点探索 需要用到的库:如标题 三大方面 功能 需要的库 一、 简单交互,获取函数或者样本点 tkinter[python自带] 二、 处理所获得的信息,并得到...

    用Python的Tkinter、Numpy、Matplotlib库对曲线拟合的一点探索【已改进】

    需要用到的库:如标题
    三大方面 功能 需要的库
    一、 简单交互,获取函数或者样本点 tkinter[python自带]
    二、 处理所获得的信息,并得到绘图所需数据 numpy,math
    三、 根据所得到的数据进行绘图 matplotlib

    首先得把需要的库给 import 喽,没有安装的库 pip install 一下也就OK啦

    上码

    from tkinter import *
    import tkinter.messagebox as messagebox
    from numpy import *
    import numpy as np
    import sys
    import math
    import matplotlib.pyplot as plt
    import matplotlib
    import matplotlib.gridspec as gridspec
    

    一、 交互界面的设计[tkinter]

    在这里插入图片描述

    函数字符串为数据源部分 功能 tkinter相应实现模块
    1 函数字符串获取 单选按钮
    2 插值区间选择 滑动条
    3 随机获取样本点的数量 微调框
    4 文字部分 标签
    5 按钮部分 按钮

    在这里插入图片描述

    样本点为数据源部分 功能 tkinter相应实现模块
    1 样本点获取 文本框
    2 拟合阶数选取 滑动条
    3 删除,添加,确认按键 按钮
    代码来了
    class Miao(get_list, plot):
        def __init__(self):
            get_list.__init__(self)
            plot.__init__(self)
    
            self.master = Tk()
            self.xlst = [StringVar() for i in range(400)]
            self.ylst = [StringVar() for i in range(400)]
            self.etx = [Entry() for i in range(400)]
            self.ety = [Entry() for i in range(400)]
            self.entryfx = Entry()
    
            self.f_ = IntVar()  # 判断函数字符串是选择还是写入的标志
            self._fx = StringVar()  # 存储用户所选函数串
            self.fff = 0  # 判断是否进行函数选择, 否则
            self.num = StringVar()  # 判断数字框输入是否非法的字符串变量
            self.nm = IntVar()  # 存储样本点数量的整数型
            self._lx, self.lx_ = IntVar(), IntVar()  # 存储 x左右 范围(取点、插值、画图
            self.fx = IntVar()  # 选择项值获取
            self.paint = 0  # 判断,如果不是第一次绘图,就先清空画板
            self.ifpoint = 0  # 判断是样点作图还是函数作图
            self.point_ct = 0  # 统计样本点个数
            self.fdict = {
                0: 'cos(2*pi*x)*(e**(-x)) + 0.8',
                1: '1/(1+25*x**2)',
                2: '(e**(-x**2/2))/(2*pi)**0.5',
            }
            self.f_x = StringVar()  # 文本框获取值
            self.lst = []  # 存储 画图数据的列表
            self.ep = 10  # 插值区间长度初始值
            self.ojbk = 0
            self.master.title('插值法及函数图像简单拟合函数')
            # self.master.geometry('630x700+100+30')
    
            # 滚动条设定
            canvas = Canvas(self.master, confine=True, width=660, height=800, scrollregion=(-1, -1, 660, 1100))  # , bg='snow')
            scrollbar = Scrollbar(self.master, command=canvas.yview, bd=2)
            scrollbar.pack(side=RIGHT, fill=Y)
            canvas.pack(fill=Y)
            canvas.config(yscrollcommand=scrollbar.set)
            # 文字 (Label)
            title = Label(self.master, text='简单插值法以及曲线拟合', font=('黑体', 18), bg='turquoise', fg='black')
            title.pack(fill=X)
            canvas.create_window(280, 15, height=24, window=title, anchor=CENTER)
    
            # frame1 请您输入
            self.frame1 = Frame(canvas)
            self.frame1.pack(fill=X)
            label1 = Label(self.frame1, text='【1】请选择/输入一个函数:', font=('楷体', 14), fg='darkblue', bg='seashell')
            label1.grid(row=1, column=0, sticky=W)
    
            # 单选按钮
            fxx = [
                (0, "cos(2*pi*x)*(e**(-x)) + 0.8"),
                (1, "1/(1+25*x**2)"),
                (2, "(e**(-x**2/2))/(2*pi)**0.5"),
            ]
            j = 2
            for fid, fx in fxx:
                fx = Radiobutton(self.frame1, text=fx, fg='cornflowerblue', variable=self.fx, font=('楷体', 13), value=fid,
                                 command=self.getfx)
                fx.grid(row=j, column=0, stick=W)
                j += 1
            fx = Radiobutton(self.frame1, text='手动输入函数', fg='cornflowerblue', variable=self.fx, font=('楷体', 13), value=3,
                             command=self.if_5)
            fx.grid(row=6, column=0, stick=W)
            fx = Radiobutton(self.frame1, text='输入样点数据', fg='cornflowerblue', variable=self.fx, font=('楷体', 13), value=4,
                             command=self.get_point)
            fx.grid(row=5, column=0, stick=W)
            canvas.create_window(20, 120, window=self.frame1, anchor=W)
    
            # 输入说明
            frame2 = Frame(canvas)
            frame2.pack()
            # 容器框 (LabelFrame)
            self.group = LabelFrame(frame2, text="函数输入说明", font='5', padx=5, pady=5, fg='red')
            self.group.grid()
            labels = [
                "1.平方‘**’来表示,绝对值:abs(x),乘号‘*’,除号‘/’,加减号‘+’ ‘-’",
                "2.支持无理数e的输入,圆周率用pi表示",
                "3.未知变量统一用x表示(只限用x表示),不支持中文符号输入(如中文输入法的括号。",
                "4.一些简单示例: sin(x), cos(x), cos(pi*x), e**x, (10-abs(x))**0.5,log(x)",
                "5.函数解析基于Python Numpy,有兴趣可以多尝试一些函数表达(4. 没列出的",
                "6.注意暂不支持纯数字输入! 若是输入log(x),请勿选择负区间,否则函数失效(开根号也如此)"
            ]
            for lb in labels:
                w = Label(self.group, text=lb, fg='darkcyan', font=('宋体', 10))
                w.grid(sticky=W)
            canvas.create_window(11, 290, window=frame2, anchor=W)
    
            # 区间选择  注意判断大小
            # frame4 请您输入
            self.frame4 = Frame(canvas)
            self.frame4.pack(fill=X)
            label4 = Label(self.frame4, text='【2】请选择一个取样区间:', font=('黑体', 14), fg='darkblue', bg='seashell')
            label4.grid(row=0, column=0, sticky=W)
            label4 = Label(self.frame4, text='  \n ')
            label4.grid(row=1, column=0, sticky=W)
    
            # 单选按钮  挡位
            frm = Frame(canvas)
            frm.pack()
            lab = Button(frm, relief='sunken', borderwidth=3, text='请选择档位:', font='12', fg='tomato')
            lab.grid(row=1, column=0, sticky=W)
            self.dwdict = {
                0: 'x1',
                1: 'x5',
                2: 'x10',
                3: 'x20',
                4: 'x0.1',
                5: 'x0.01',
                6: 'x0.001'
            }
            cl = 1
            self.dw = IntVar()
            for d in self.dwdict:
                dx = Radiobutton(frm, text=self.dwdict[d], fg='blue', variable=self.dw, font=('楷体', 14), value=d,
                                 command=self.getdw)
                dx.grid(row=1, column=cl, stick=W)
                cl += 1
            canvas.create_window(15, 410, window=frm, anchor=W)
    
            # 滑动条 (Scale)
            self.lx = Scale(self.frame4, from_=-self.ep, to=self.ep, orient=HORIZONTAL, resolution=1, length=280, label="从",
                            tickinterval=5, relief=GROOVE)  # 默认垂直
            self.lx.grid(row=2, column=0, sticky=W)
            self.rx = Scale(self.frame4, from_=-self.ep, to=self.ep, orient=HORIZONTAL, resolution=1, length=280, label="到",
                            tickinterval=5, relief=RIDGE)  # 默认垂直
            self.rx.grid(row=2, column=1, sticky=W)
            canvas.create_window(10, 440, window=self.frame4, anchor=W)
    
            # 改变选区
            frameb = Frame(canvas)
            frameb.pack()
            low = Button(frameb, text='减小选择区间', borderwidth=4, font=('隶书', 11), command=self.lower, )
            low.grid(row=1, column=0, sticky=W)
            up = Button(frameb, text='扩大选择区间', borderwidth=4, font=('隶书', 11), command=self.upper, )
            up.grid(row=1, column=1, sticky=W)
            canvas.create_window(185, 530, window=frameb, anchor=W)
    
            #  插值点个数
            frame5 = Frame(canvas)
            frame5.pack(fill=X)
            label5 = Label(frame5, text='【3】请选择取样点个数', font=('黑体', 14), fg='darkblue', bg='seashell')
            label5.grid(row=2, column=0, sticky=W)
            self.number = Spinbox(frame5, borderwidth=2, relief='raised', font=10, textvariable=self.num, from_=3, to=500)
            self.number.grid(row=2, column=1, sticky=W)
            label51 = Label(frame5, text='[请选择或输入 3~500 间的整数]', fg='red')
            label51.grid(row=2, column=2)
            canvas.create_window(12, 565, window=frame5, anchor=W)
    
            # 输入说明
            framep = Frame(canvas)
            framep.pack()
            # 容器框 (LabelFrame)
            self.group = LabelFrame(framep, text="整体框架说明", font='4', padx=5, pady=5, fg='steelblue')
            self.group.grid()
            labels = [
                "一、插值法部分",
                " 1.由用户输入合法的函数字符串,选择取样区间及样本点个数;",
                " 2.确认输入无误后开始执行,相对随机进行样本点的获取;",
                " 3.由所得样本点经过拉格朗日/牛顿插值法、分段二次插值法、\n分段法再拟合出函数图;",
                " 4.最后展示拟合图像以及原图像\n",
                " 标注:分段二次插值法是照搬书上套路,分段法是将函数为三段:\n\r中间一段用拉格朗日/牛顿插值法得到\n\r两边两段用二次插值法得到",
                "\n二、最小二乘法部分",
                " 1.用户输入一系列样本点,并选择拟合函数的阶数进行拟合",
                " 2.由于一些原因,可能出现无法拟合,这时可以调整一下你和阶数",
                " 3.阶数说明:由于这里拟合默认拟合曲线方程为x的幂次多项式,阶数即是\nx的最高幂次",
                "",
            ]
            for lb in labels:
                w = Label(self.group, text=lb, fg='seagreen', font=('楷书', 11))
                w.grid(sticky=W)
            canvas.create_window(20, 840, window=framep, anchor=W)
    
            # ok按钮  (Button)
            framed = Frame(canvas)
            framed.pack()
            okk = Button(framed, relief='raised', borderwidth=5, text='确认输入', font=('楷书', 14), width=55, command=self.ok)
            okk.grid(row=1, column=0)
            canvas.create_window(10, 620, window=framed, anchor=W)
    
            framed1 = Frame(canvas)
            framed1.pack()
            okk = Button(framed1, relief='raised', borderwidth=4, text='点\n击\n退\n出', font=('楷书', 14), bg='red',
                         command=self.for_end)
            okk.grid(row=1, column=0)
            canvas.create_window(500, 100, window=framed1, anchor=W)
    
            framed2 = Frame(canvas)
            framed2.pack()
            end = Button(framed2, relief='raised', borderwidth=4, text='   确认输入   ', bg='aqua', font=('楷书', 14),
                         command=self.ok)
            end.grid(row=1, column=0)
            canvas.create_window(440, 170, window=framed2, anchor=W)
    
            self.master.mainloop()
    
        # 区间上升(主框架
        def upper(self):
            if self.ep < 20:
                self.ep += 5
                lb = 5
                self.lx = Scale(self.frame4, from_=-self.ep, to=self.ep, orient=HORIZONTAL, resolution=1, length=280,
                                label="从", tickinterval=lb, relief=GROOVE)  # 默认垂直
                self.lx.grid(row=2, column=0, sticky=W)
                self.rx = Scale(self.frame4, from_=-self.ep, to=self.ep, orient=HORIZONTAL, resolution=1, length=280,
                                label="到", tickinterval=lb, relief=RIDGE)  # 默认垂直
                self.rx.grid(row=2, column=1, sticky=W)
    
        # 区间下降(主框架
        def lower(self):
            if self.ep > 5:
                self.ep -= 5
                if self.ep > 5:
                    lb = 5
                else:
                    lb = 1
                self.lx = Scale(self.frame4, from_=-self.ep, to=self.ep, orient=HORIZONTAL, resolution=1, length=280,
                                label="从", tickinterval=lb, relief=GROOVE)  # 默认垂直
                self.lx.grid(row=2, column=0, sticky=W)
                self.rx = Scale(self.frame4, from_=-self.ep, to=self.ep, orient=HORIZONTAL, resolution=1, length=280,
                                label="到", tickinterval=lb, relief=RIDGE)  # 默认垂直
                self.rx.grid(row=2, column=1, sticky=W)
    
        # 按钮获取函数(主框架
        def getfx(self):
            self.entryfx.destroy()
            if self.ifpoint:
                try:
                    self.pt.destroy()
                except:
                    pass
            self.f_ = 0
            self.fff = 1
            self.ifpoint = 0
            self._fx = self.fdict[self.fx.get()]
    
        # 文本框中函数获取(主框架
        def if_5(self):
            self.f_ = 1  # 如果点击了 自己输入  标记self.f_ 为 1 否则为 0
            if self.ifpoint:
                try:
                    self.pt.destroy()
                except:
                    pass
            self.ifpoint = 0  # 判断是输入样本点还是函数字符串  字符 0  样本点 1
            # 输入框 (Entry)  #自我输入函数框图
            self.entryfx = Entry(self.frame1, textvariable=self.f_x, width=30)
            self.entryfx.grid(row=7, column=0, columnspan=4, sticky=W)
    
        # 获取插值点个数档位函数(主框架
        def getdw(self):
            self.dw_ = float(self.dwdict[self.dw.get()].strip('x'))
    
        # 判断 函数字符串输入结束, 开始画图(主框架
        # 改+++++
        def ok(self):
            self.ifpoint = 0
            self._lx, self.lx_ = self.lx.get() * self.dw_, self.rx.get() * self.dw_
            if self.f_ == 1:  # 捕捉获取的字符串
                self._fx = self.f_x.get()
            if self.fff == 0 and self.f_ != 1:
                self._fx = "cos(2*np.pi*x)*np.exp(-x) + 0.8"
            if self._lx > self.lx_:
                self._lx, self.lx_ = self.lx_, self._lx
            if not self.num.get().isdigit():
                messagebox.showwarning('Warining', '条目[3]非法输入')
            else:
                if not isinstance(type(eval(self.num.get())), int) and not 2 < eval(self.num.get()) < 501:
                    messagebox.showwarning('Warining', '条目[3]非法输入')
                elif not self._fx:
                    messagebox.showwarning('Warining', '函数输入为空')
                elif self._lx == self.lx_:
                    self.warn1()
                else:
                    self.nm = int(self.num.get())
                    if messagebox.askokcancel('这是一个弹窗', '输入完成,是否开始下一步'):
                        self.ojbk = 1
                        lt = self.get_flist()
                        try:
                            self.s_plot(lt)
                        except Exception as t:
                            print(t)
                            messagebox.showerror('(*_*)', '运行失败了T_T')
    
        # 结束运行 关闭窗口(主框架
        def for_end(self):
            if messagebox.askokcancel('这是一个弹窗', '是否确认退出?'):
                sys.exit(0)
    
        #\ 获取样本点(开小框架获取样本店 框架0.1
        def get_point(self):
            if self.ifpoint == 1:
                try:
                    self.pt.destroy()
                except:
                    pass
            self.ifpoint = 1
            self.noinfo_window = 1  # 判断提示弹窗是否出现
            self.entryfx.destroy()
            self.point_ct = 0  # 统计样本点个数
            self.ex = []  # 置空刷新
            self.ey = []  # 置空刷新
            self.m = 1  # 阶数 默认为1
            global pd
            pd = Spinbox()
            self.pointdic = {}
            self.pt = Tk()
            self.pt.title('样本点获取')
            self.pt.geometry('350x500+800+30')
    
            # 滚动条设定
            canvas1 = Canvas(self.pt, confine=True, width=500, height=3000,
                             scrollregion=(0, 0, 500, 3000))  # , bg='snow')
            scrollbar = Scrollbar(self.pt, command=canvas1.yview, bd=2)
            scrollbar.pack(side=RIGHT, fill=Y)
            canvas1.pack(fill=Y)
            canvas1.config(yscrollcommand=scrollbar.set)
            # 文字 (Label)
            title = Label(self.pt, text='样本点获取', font=('黑体', 18), bg='lightblue', fg='black')
            title.pack(fill=X)
            canvas1.create_window(150, 15, height=24, window=title, anchor=CENTER)
            lbxy = Label(self.pt, text='X       \t   Y', font='10', )
            lbxy.pack(fill=X)
            canvas1.create_window(150, 140, height=24, window=lbxy, anchor=CENTER)
    
            # 拟合函数阶数
            frm1 = Frame(canvas1)
            frm1.pack(fill=X)
    
            lbl1 = Label(frm1, text='*请选择一个拟合函数阶数:', font=('黑体', 11), fg='darkblue')
            lbl1.grid(row=0, column=0, sticky=W)
            self.pdw = IntVar()
            pd = Spinbox(frm1, borderwidth=2, width=15, relief='raised', font=10, textvariable=self.pdw, from_=1, to=150)
            pd.grid(row=1, column=0, sticky=W)
            label51 = Label(frm1, text='[1~150 间的整数]', fg='red')
            label51.grid(row=1, column=1, sticky=W)
            canvas1.create_window(15, 100, window=frm1, anchor=W)
    
            # 数据文本框 系列
            self.frm2 = Frame(canvas1)
            self.frm2.pack()
            canvas1.create_window(150, 160, window=self.frm2, anchor=N)
    
            # 添加数据按钮
            button1 = Button(canvas1, text='添加数据', borderwidth=5, command=self.newp)
            canvas1.create_window(20, 50, window=button1, anchor=W)
    
            # 删除数据按钮
            button1 = Button(canvas1, text='删除上一数据', borderwidth=5, fg='red', command=self.del_last)
            canvas1.create_window(90, 50, window=button1, anchor=W)
    
            # 完成按钮
            button1 = Button(canvas1, text='输入完成', borderwidth=5, fg='blue', bg='snow', command=self.f_end)
            canvas1.create_window(250, 50, window=button1, anchor=W)
    
            self.pt.mainloop()
    
        #\ 创建新数据表  样本点(框架0.1
        def newp(self):
            if self.point_ct == 0 and self.noinfo_window:
                self.noinfo_window = 0
                messagebox.showinfo('输入小提示', "1.不支持数字外其他输入,比如符号,字母;\n2.可以x,y都为空,但不能只有一个值;\n3.x值可以不按照数轴顺序输入", )
            if self.point_ct < 400:
                self.etx[self.point_ct] = Entry(self.frm2, textvariable=self.xlst[self.point_ct])
                self.etx[self.point_ct].grid(row=self.point_ct, column=0, columnspan=1, )
                self.ety[self.point_ct] = Entry(self.frm2, textvariable=self.ylst[self.point_ct])
                self.ety[self.point_ct].grid(row=self.point_ct, column=2, columnspan=1, )
                self.point_ct += 1
    
        #\ 删除 最后输入样本点(框架0.1
        def del_last(self):
            if self.point_ct > 1:
                self.point_ct -= 1
                self.etx[self.point_ct].destroy()
                self.ety[self.point_ct].destroy()
                self.xlst[self.point_ct] = None
                self.ylst[self.point_ct] = None
    
        #\ 样本点输入结束(框架0.1
        def f_end(self):
            self.ifpoint = 1
            self.ex = []  # 置空刷新
            self.ey = []  # 置空刷新
            self.pointdic = {}
            if self.point_ct < 3:
                messagebox.showwarning('Warining', '样本点个数过少')
            elif not isinstance(type(eval(pd.get())), int) and not 0 < eval(pd.get()) < 151:
                messagebox.showwarning('Warining', '阶数非法输入')
            else:
                m = eval(pd.get())
                try:
                    for i in range(self.point_ct):
                        et_x, et_y = self.etx[i].get(), self.ety[i].get()
                        if len(et_x) == len(et_y) == 0:
                            continue
                        else:
                            if not isinstance(type(eval(et_x)), (int, float)) and isinstance(type(eval(et_y)),
                                                                                             (int, float)):
                                messagebox.showwarning('Warining', '请检查输入是否合法(只能输入数字)\n[字母,符号,不输入都将报错]!')
                                self.pointdic.clear()
                                break
                            else:  # 已经确定两个数类型可靠
                                et_x, et_y = eval(et_x), eval(et_y)
                                if et_x in self.pointdic.keys():
                                    messagebox.showwarning('Warining', 'X重复,请检查!')
                                    self.pointdic.clear()
                                    break
                                else:
                                    self.pointdic[et_x] = et_y
                    else:
                        keylst = list(self.pointdic.keys())
                        keylst = sorted(keylst)
                        for key in keylst:
                            self.ex.append(key)
                            self.ey.append(self.pointdic[key])
                        else:
                            if messagebox.askokcancel('这是一个弹窗', '输入完成,是否开始拟合'):
                                self.ojbk = 1
                                self.m = m
                                lt = self.get_flist()
                                lt1 = self.get_plist()
                                lt.append(lt[0])
                                lt[0], lt[1] = lt1[0], lt1[1]
                                self.p_plot(lt)  # 开始绘图
                                self.point_ct = 0  # 保证再一次进入到结束判断时字典、列表的存储位置从0开始
                except Exception as t:
                    print(t)
                    messagebox.showwarning('Warining', '错误!')
    
        # 警告 (主框架
        #def warn0(self):
            #messagebox.showwarning('Warining', '函数解析错误,函数不能为纯数字,或者包含其它非法字符')
    
        # 警告(主框架
        def warn1(self):
            messagebox.showwarning('Warining', '所选区间长度为空')
    
        # 错误(主框架
        def fail(self):
            messagebox.showerror('喵喵喵', '执行失败')
    

    二、 数据处理部分[math,numpy]

    函数数据处理
    1 获取原函数横纵坐标x,y x=numpy.arange(x0,xn, 0.001) +eval(fx)
    2 拉格朗日插值法数据获取 ------
    3 分段二次插值法 ------
    4 分段法 (简单结合拉格朗日插值法与分段二次插值法)
    样本点数据处理 最小二乘法
    代码来了
    class get_list():
        def __init__(self):
            self.nm = None
            self.ojbk = None
            self.ifpoint = None
            self._lx = None
            self.lx_ = None
            self._fx = None
            self.dw_ = 1
            self.ex = None
            self.ey = None
            self.nh_fx = None
            self.m = 1
            self.fx_lst = []
    
        def s_set(self, *, _lx=None, lx_=None, _fx=None, nm=None):
            self.ojbk = 1
            self.ifpoint = 0
            self._lx = _lx
            self.lx_ = lx_
            self._fx = _fx
            self.nm = nm
    
        def p_set(self, *, ex=None, ey=None):
            self.ex = ex
            self.ey = ey
    
        # 处理字符串/样本点,返回一系列数据的列表【绘图数据获取】
        # 参数 self.nm(point_count), self.ojbk, self.ifpoint, self._lx,self.lx_, self._fx,
        # self.dw_, self.ex, self.ey,
        # return [x0, y0, x1, y1, x4, y4, x5, y5, y3]
        def get_flist(self):
            if not self.ojbk:
                quit(self.get_flist)
    
            if self.ifpoint == 0:  # 获取函数字符串数据
                n = self.nm  # 30  # 取样点个数( 相对随机 xk = xk + rand(0~1)*[(xn-x0)/n]
                m = 3  # 分段时左右两段取样点所占比重  sum(x1)/m
                limits = [self._lx, self.lx_]
                _x, x_, x_l = limits[0], limits[1], abs(limits[1] - limits[0])
                x1, y1 = [], []  # 存储样本点
                # 函数
                fx = self._fx
                if self.dw_ > 0.01:
                    cont = 0.0001
                else:
                    cont = 0.00001
                dex = x_l * 0.25
                # 获取原函数画图所需点列表 x0, y0  (插值拟合数据源
                x = np.arange(_x - dex, x_ + dex, cont)
                eval(fx)
                try:
                    y0 = eval(fx)
                    if len(y0) == 1:
                        messagebox.showwarning('Warining', '函数解析错误,函数不能为纯数字,或者包含其它非法字符')
                        return None
                except Exception as t:
                    print(t)
                    messagebox.showwarning('Warining', '函数解析错误,函数不能为纯数字,或者包含其它非法字符')
                    return None
                x0 = x
                # 获取样本点并存入列表 x1, y1  (折线图数据源
                for xo in np.linspace(_x, x_, n):
                    x = xo + np.random.rand() * (x_l / n)
                    x1.append(x)
                    y1.append(eval(fx))
            else:  # 样本点  初始化 参数:self.ex; self.ey; self.m; self.ifpoint; self.nh_fx
                x1, y1 = self.ex, self.ey
                dex = (self.ex[-1] - self.ex[0]) * 0.2
                n = len(x1)
                m = 2  # 不需要
                cont = 0.0001
                x0 = np.arange(x1[0] - dex, x1[-1] + dex, cont)
            # 拟合所需 1_原函数 x0, y0; 2_取样点 x1, y1; 3_取样点个数 n, 取样精度 cnt
            '''
            # 拉格朗日插值法 获得 y2
            y2 = x0 * 0
            for i in range(n):
                fz, fm = x0* 0 + 1, 1
                for j in range(n):
                    if not j==i:
                        fz *= (x0 - x1[j])
                        fm *= (x1[i] - x1[j])
                y2 += y1[i] * (fz / fm)
    
            '''
            # 牛顿插值法 获得 y3
            y3 = x0 * 0 + y1[0]
            x3 = x0 * 0 + 1
            y4 = [y1, []]
            #   获得差商标
            for i in np.arange(1, n)[::-1]:
                x3 *= (x0 - x1[n - 1 - i])
                for j in range(i):
                    ff = (y4[0][j + 1] - y4[0][j]) / (x1[n - i + j] - x1[j])
                    y4[1].append(ff)
                y3 += y4[1][0] * x3
                y4 = [y4[1], []]
            # 分段二次数插值法  ----1段式  (相当于 m==2)
            x5, y5 = [], []  # 存取插值函数数据
            # 左右两部分取样点下标列表
            lln = len(x1)
            sq = list(range(0, lln - 2, 2))
            for i in range(0, lln - 2, 2):  #
                if i == 0:
                    if i == sq[-1]:
                        dx1_ = 3 * dex
                    else:
                        dx1_ = 0
                    dx1 = dex
                elif i == sq[-1]:
                    dx1 = 0
                    dx1_ = 3*dex
                else:
                    dx1 = 0
                    dx1_ = 0
                fz5, fm5 = np.arange(x1[i] - dx1, x1[i + 2] + dx1_, cont) * 0 + 1, 1
    
                y_y = np.arange(x1[i] - dx1, x1[i + 2] + dx1_, cont) * 0  # 累加获得小段函数值数据源
                x_x = np.arange(x1[i] - dx1, x1[i + 2] + dx1_, cont)  # 获取当前区间 x 的列表 x
                for k in range(i, i + 3):
                    fz5, fm5 = fz5 * 0 + 1, 1
                    for j in range(i, i + 3):
                        if not j == k:
                            fz5 *= (x_x - x1[j])
                            fm5 *= (x1[k] - x1[j])
                    y_y += y1[k] * (fz5 / fm5)
                y5 += list(y_y)
                x5 += list(x_x)
    
            # 分段插值法  ----三段式
            y4, y4_ = [], []  # 存取左半边插值函数数据
            x4, x4_ = [], []  # 右半边数据存储
            # 左右两部分取样点下标列表
            ltx, rtx = list(range(0, int(len(x1) // m), 2)), list(range(int((1 - 1 / m) * len(x1)), len(x1), 2))
            rtx.pop(-1)
            for i in ltx:  # 左半部分
                if i == 0:
                    dx0 = dex
                else:
                    dx0 = 0
                fz, fm = np.arange(x1[i] - dx0, x1[i + 2], cont) * 0 + 1, 1
                yy = np.arange(x1[i] - dx0, x1[i + 2], cont) * 0  # 累加获得小段函数值数据源
                l_x = np.arange(x1[i] - dx0, x1[i + 2], cont)  # 获取当前区间 x 的列表 left_x
                for k in range(i, i + 3):
                    fz, fm = fz * 0 + 1, 1
                    for j in range(i, i + 3):
                        if not j == k:
                            fz *= (l_x - x1[j])
                            fm *= (x1[k] - x1[j])
                    yy += y1[k] * (fz / fm)
                y4 = y4 + list(yy)
                x4 += list(l_x)
            for i_ in rtx:  # 右半部分
                if i_ == rtx[-1]:
                    dx0 = dex
                else:
                    dx0 = 0
                fz_, fm_ = np.arange(x1[i_], x1[i_ + 2] + dx0, cont) * 0 + 1, 1
                yy_ = np.arange(x1[i_], x1[i_ + 2] + dx0, cont) * 0
                x_r = np.arange(x1[i_], x1[i_ + 2] + dx0, cont)
                for k_ in range(i_, i_ + 3):
                    fz_, fm_ = fz_ * 0 + 1, 1
                    for j_ in range(i_, i_ + 3):
                        if not j_ == k_ and j_ < len(x1) and k_ < len(x1):
                            fz_ *= (x_r - x1[j_])
                            fm_ *= (x1[k_] - x1[j_])
                    if k_ < len(x1):
                        yy_ += y1[k_] * (fz_ / fm_)
                y4_ = y4_ + list(yy_)
                x4_ += list(x_r)
    
            # 由中间向两边遍历循环,寻找 左 中,中 右最佳连接点
            if m == 2:
                left = right = 0
            else:
                start1 = start = len(x0) // 2
                while 1:
                    if n < 7:
                        left = right = 0
                        break
                    if x0[start] < x4[-1] and start:  # 左连接点
                        left = start + 1
                        start = 0
                    if x0[start1] > x4_[0] and start1:  # 右连接点
                        right = start1 - 1
                        start1 = 0
                    if start:
                        start -= 1
                    if start1:
                        start1 += 1
                    if not (start + start1):
                        break
            y4 = y4 + list(y3[left:right]) + y4_  # 拼接 左中右 函数值数据源
            x4 = x4 + list(x0[left:right]) + x4_  # 拼接 左中右 自变量值
            if n < 7:
                x4, y4 = [], []
            if self.ifpoint:
                y0 = []
            return [x0, y0, x1, y1, x4, y4, x5, y5, y3]
    
        # 获取输入样本点 最小二乘法拟合曲线数据【绘图数据获取】
        # 参数 self.nh_fx, self.ex, self.ey, self.m,
        # return [x, y]
        def get_plist(self):
            self.nh_fx = ''
            ex = self.ex  # np.arange(-10, -4,0.1)       #[1,3,4,5,6,7,8,9,10]
            ey = self.ey  # eval("cos(2*np.pi*ex)*np.exp(-ex) + 0.8")#eval("sin(ex)")##[10,5,4,2,1,1,2,3,4]
            x_or_cplx = 1  # 判断是简单 x 线性函数拟合还是  自定义函数拟合
            if x_or_cplx == 1:  # x 线性拟合
                m = self.m
                b = [0 for i in range(m + 1)]  # 存储
                fai = [0 for i in range(2 * m + 1)]  # 存储 x 的 0~2m次方
                A = [[0 for i in range(m + 1)] for i in range(m + 1)]  # 存储 A 矩阵 Ax = b
                for i in range(2 * m + 1):
                    for j in range(len(ex)):
                        fai[i] += ex[j] ** i
                        if i < m + 1:
                            b[i] += ey[j] * (ex[j] ** i)
                for i in range(m + 1):
                    for j in range(m + 1):
                        A[i][j] = fai[i + j]
                try:
                    a = np.linalg.solve(A, b)  # .resize(1, len(b))  [32, 147, 1025, 8421, 74261]
                except Exception as t:
                    print(t)
                    a = [1,]
                    pass
                #print(A, b, a, sep='\n')
                self.nh_fx += f"$({a[0]:.4e})"
                for i in range(m):
                    if i!=0 and not i%9:
                        self.nh_fx += '\n'
                    self.nh_fx += f"+({a[i+1]:.4e})X^{i+1}"
                self.nh_fx = self.nh_fx + "$  \n[Least-Square-method]"
                if m>9:
                    self.nh_fx = self.nh_fx.replace('$', '')
                dex = (ex[-1] - ex[0]) * 0.1
                x = np.arange(ex[0] - dex, ex[-1] + dex, 0.001)
                y = x * 0
                for i in range(m + 1):
                    y += a[i] * (x ** i)
            else:
                m = 3
                b = [0 for i in range(m + 1)]  # 存储 fn * yn
                A = [[0 for i in range(m + 1)] for i in range(m + 1)]  # 存储 A 矩阵 Ax = b
                ff = ['self.f0(xm)', 'self.f1(xm)', 'self.f2(xm)', 'self.f3(xm)',] # 'self.f4(xm)', 'self.f5(xm)', 'self.f6(xm)']
                for i in range(m + 1):
                    for j in range(i, m + 1):
                        n = 0
                        for xm in ex:
                            A[i][j] += eval(ff[j]) * eval(ff[i])
                            if i == 0:
                                b[j] += ey[n] * eval(ff[j])
                                n += 1
                for i in range(m + 1):
                    for j in range(0, i):
                        A[i][j] = A[j][i]
    
                try:
                    a = np.linalg.solve(A, b)  # .resize(1, len(b))
                except:
                    pass
                self.nh_fx += f"$({a[0]:.4e})"
                for i in range(m):
                    if i!=0 and not i%9:
                        self.nh_fx += '\n'
                    self.nh_fx += f"+({a[i+1]:.4e})x({self.fx_lst[i]})"
                self.nh_fx = self.nh_fx + "$  \n[Least-Square-method]"
                if m>9:
                    self.nh_fx = self.nh_fx.replace('$', '')
                dex = (ex[-1] - ex[0]) * 0.11
                x = np.arange(ex[0] - dex, ex[-1] + dex, 0.001)
                y = x * 0
    
                xm = x
                for i in range(m + 1):
                    y += a[i] * eval(ff[i])
            return [x, y]
    
        def f0(self, xn):
            return 1
    
        def f1(self, xn):
            if 'cos(2pi*x)(1/e^x)' not in self.fx_lst:
                self.fx_lst.append('cos(2pi*x)(1/e^x)')
            return cos(2*np.pi*xn)*np.e**(-xn)
    
        def f2(self, xn):
            if 'cos(2pi*x)' not in self.fx_lst:
                self.fx_lst.append('cos(2pi*x)')
            return cos(2*pi*xn)
    
        def f3(self, xn):
            if 'e^x' not in self.fx_lst:
                self.fx_lst.append('e^x')
            return np.e**(xn)
    

    三、 绘图[matplotlib]

    函数字符串部分
    • 拉格朗日、分段二次、分段法与原函数同坐标系绘图比较+所选样本点散点图
    • 不同方法函数 单独图像
    • 原函数小图
    • 图标
    样本点处理后绘图部分
    • 最小二乘法、拉格朗日、分段二次法函数图同坐标系绘图比较
    • 样本点散点图
    • 图标
    代码来了
    class plot():
        def __init__(self):
            self.ojbk = None
            self.ifpoint = None
            self._lx = None
            self.lx_ = None
            self.paint = None
            self.nh_fx = None
    
            # 画图【绘图】
            # 参数 lst, self.ojbk, self.ifpoint, self._lx, self.lx_, self.paint
    
        def s_plot(self, lst):  # 画图
            if not self.ojbk or not lst:
                return None
            matplotlib.rcParams['font.family'] = 'SimHei'
            matplotlib.rcParams['font.sans-serif'] = ['SimHei']
            matplotlib.rcParams['axes.unicode_minus'] = False  # 中文负号问题
    
            plt.figure(num='插值法以及曲线拟合的一点探索', figsize=(15, 15), dpi=110, facecolor='w', edgecolor='cyan')
            gs = gridspec.GridSpec(2, 4)
            ax1 = plt.subplot(gs[0:, :2])  # mix  原图 x0, y0
            ax2 = plt.subplot(gs[0, 2:])  # 牛顿/拉格朗日插值 x0, y2
            ax3 = plt.subplot(gs[1, 2])  # 分段二次插值    x4, y4
            ax4 = plt.subplot(gs[1, 3])  #    x1, y1
            x0, y0 = lst[0], lst[1]  # mix  原图 x0, y0
            x1, y1 = lst[2], lst[3]  #  x1, y1  散点图
            x4, y4 = lst[4], lst[5]  # 分段插值    x4, y4
            x5, y5 = lst[6], lst[7]  #
            y2 = lst[8]  # 牛顿/拉格朗日插值 x0, y2
    
            if self.ifpoint == 0:
                x0_ = x0
                mn, mx = min(y0), max(y0)
                if math.isnan(mn):
                    mn = -20
                if math.isnan(mx):
                    mx = 20
                dy = abs(mx - mn) / 8
                foc_y = mn + (mx - mn) * 0.35
                mn, mx = mn - dy, mx + dy
                foc = (self._lx + self.lx_) / 2  # 横坐标与纵坐标交点
            else:
                x0_ = []
                mn, mx = min(y1), max(y1)
                dy = abs(mx - mn) / 8
                foc_y = mn + (mx - mn) * 0.35
                mn, mx = mn - dy, mx + dy
                foc = (x1[0] + x1[-1]) / 2  # 横坐标 与纵坐标交点处x 的值
    
            if self.paint != 0:
                for ax in [ax1, ax2, ax3, ax4]:
                    ax.cla()
            ax1.plot(x0_, y0, color='r', label=r'$Original-function$', linewidth=1.5, alpha=.98)
            ax1.scatter(x1, y1, s=50, marker="o", label=r'Sample-point', color='lime', alpha=.79)
            ax1.plot(x0, y2, color='darkviolet', label=r"$Lagrange's/Newton-interpolation$", linewidth=.8)
            ax1.plot(x5, y5, color='blue', label=r'$Piecewise-interpolation$', linewidth=.7)
            ax1.plot(x4, y4, color='peru', label=r'$Simple-Piecewise-interpolation$', linewidth=.7, alpha=0.95)
            # 小图
            mini = plt.figure(num='插值法以及曲线拟合的一点探索').add_axes([.02, .8, .15, .15])
            if self.paint != 0:
                mini.cla()
            mini.plot(x0_, y0, color='r', linewidth=1)
            mini.spines['top'].set_color('none')
            mini.spines['right'].set_color('none')
            mini.xaxis.set_ticks_position('bottom')
            mini.yaxis.set_ticks_position('left')
            mini.spines['bottom'].set_position(('data', foc_y))
            mini.spines['left'].set_position(('data', foc))
            mini.set_title(r'原函数')
    
            ax1.set_title('函数图比较')
    
            ax2.plot(x0, y2, color='darkviolet', linewidth=1, alpha=.9)
            ax2.scatter(x1, y1, s=50, marker="o", color='lime', alpha=.8)
            ax2.set_title('拉格朗日/牛顿插值法')
    
            ax3.plot(x5, y5, color='blue', linewidth=1)
            ax3.scatter(x1, y1, s=50, marker="o", color='lime', alpha=.8)
            ax3.set_title('分段二次插值法')
    
            ax4.plot(x4, y4, color='peru', linewidth=1)
            ax4.scatter(x1, y1, s=50, marker="o", color='lime', alpha=.8)
            ax4.set_title('简单分段插值法')
            plt.figlegend(loc='upper center')
    
            for ax in [ax1, ax2, ax3, ax4]:
                ax.spines['top'].set_color('none')
                ax.spines['right'].set_color('none')
                ax.xaxis.set_ticks_position('bottom')
                ax.yaxis.set_ticks_position('left')
                ax.spines['bottom'].set_position(('data', foc_y))
                ax.spines['left'].set_position(('data', foc))
                ax.axis([x0[0], x0[-1], mn, mx])
    
            # 拉格朗日 插值 大图
            plt.figure(num='拉格朗日/牛顿插值法', figsize=(10, 10))
            if self.paint != 0:
                plt.clf()
            plt.plot(x0, y2, color='blueviolet', label=r"$Lagrange's/Newton-interpolation$", linewidth=.8)
            plt.title('拉格朗日/牛顿插值法')
            plt.plot(x0_, y0, color='red', label=r'$Original-function$', linewidth=.8)
            plt.scatter(x1, y1, s=50, marker="o", label=r'Sample-point', color='lime', alpha=.8)
            plt.legend(loc='best')
            plt1 = plt.gca()
            plt1.spines['top'].set_color('none')
            plt1.spines['right'].set_color('none')
            plt1.xaxis.set_ticks_position('bottom')
            plt1.yaxis.set_ticks_position('left')
            plt1.spines['bottom'].set_position(('data', foc_y))
            plt1.spines['left'].set_position(('data', foc))
            plt.axis([x0[0], x0[-1], mn - 2 * dy, mx])
            # 小图
            mini = plt.figure(num='拉格朗日/牛顿插值法').add_axes([.02, .75, .22, .22])
            if self.paint != 0:
                mini.cla()
            mini.plot(x0_, y0, color='r', linewidth=1)
            mini.spines['top'].set_color('none')
            mini.spines['right'].set_color('none')
            mini.xaxis.set_ticks_position('bottom')
            mini.yaxis.set_ticks_position('left')
            mini.spines['bottom'].set_position(('data', foc_y))
            mini.spines['left'].set_position(('data', foc))
            mini.set_title(r'原函数')
    
    
            # 分段法2 大图
            plt.figure(num='简单分段插值法', figsize=(10, 10))
            if self.paint != 0:
                plt.clf()
            plt.plot(x4, y4, color='teal', label=r'$Simple-Piecewise-interpolation$', linewidth=1)
            plt.title('简单分段插值法')
            plt.plot(x0_, y0, color='red', label=r'$Original-function$', linewidth=1)
            plt.scatter(x1, y1, s=50, marker="o", label=r'Sample-point', color='lime', alpha=.8)
            plt.legend(loc='best')
            plt1 = plt.gca()
            plt1.spines['top'].set_color('none')
            plt1.spines['right'].set_color('none')
            plt1.xaxis.set_ticks_position('bottom')
            plt1.yaxis.set_ticks_position('left')
            plt1.spines['bottom'].set_position(('data', foc_y))
            plt1.spines['left'].set_position(('data', foc))
            plt.axis([x0[0], x0[-1], mn - 2 * dy, mx])
            # 小图
            mini = plt.figure(num='简单分段插值法').add_axes([.02, .75, .22, .22])
            if self.paint != 0:
                mini.cla()
            mini.plot(x0_, y0, color='r', linewidth=1)
            mini.spines['top'].set_color('none')
            mini.spines['right'].set_color('none')
            mini.xaxis.set_ticks_position('bottom')
            mini.yaxis.set_ticks_position('left')
            mini.spines['bottom'].set_position(('data', foc_y))
            mini.spines['left'].set_position(('data', foc))
            mini.set_title(r'原函数')
    
    
            # 分段法1 大图
            plt.figure(num='分段二次插值法', figsize=(10, 10))
            if self.paint != 0:
                plt.clf()
            plt.plot(x5, y5, color='teal', label=r'$Piecewise-interpolation$', linewidth=1)
            plt.title('分段二次插值法')
            plt.plot(x0_, y0, color='red', label=r'$Original-function$', linewidth=1)
            plt.scatter(x1, y1, s=50, marker="o", label=r'Sample-point', color='lime', alpha=.8)
            plt.legend(loc='best')
            plt1 = plt.gca()
            plt1.spines['top'].set_color('none')
            plt1.spines['right'].set_color('none')
            plt1.xaxis.set_ticks_position('bottom')
            plt1.yaxis.set_ticks_position('left')
            plt1.spines['bottom'].set_position(('data', foc_y))
            plt1.spines['left'].set_position(('data', foc))
            plt.axis([x0[0], x0[-1], mn - 2 * dy, mx])
            # 小图
            mini = plt.figure(num='分段二次插值法').add_axes([.02, .75, .22, .22])
            if self.paint != 0:
                mini.cla()
            mini.plot(x0_, y0, color='r', linewidth=1)
            mini.spines['top'].set_color('none')
            mini.spines['right'].set_color('none')
            mini.xaxis.set_ticks_position('bottom')
            mini.yaxis.set_ticks_position('left')
            mini.spines['bottom'].set_position(('data', foc_y))
            mini.spines['left'].set_position(('data', foc))
            mini.set_title(r'原函数')
    
            self.paint = 1
            plt.show()
            plt.close()
    
            # 最小二乘法绘图【绘图】
            # 参数 lst, self.nh_fx
        def p_plot(self, lst):
            matplotlib.rcParams['font.family'] = 'SimHei'
            matplotlib.rcParams['font.sans-serif'] = ['SimHei']
            matplotlib.rcParams['axes.unicode_minus'] = False  # 中文负号问题
            x0, y0 = lst[0], lst[1]  # 二乘法
            x1, y1 = lst[2], lst[3]  # 散点图
            x5, y5 = lst[6], lst[7]  # 分段二次插值
            x2, y2 = lst[9], lst[8]  # 拉格朗日
            up, low = max(y0), min(y0)
            if math.isnan(up):
                up = -20
            if math.isnan(low):
                low = 20
            ddy = (up - low) * 0.2
            xup, xlow = max(x1), min(x1)
            ddx = (xup - xlow) * 0.23
            plt.figure(num='最小二乘法、拉格朗日插值法、分段二次插值法拟合曲线比较', figsize=(10, 10))
            # plt.title('')
            plt.clf()
            plt.plot(x0, y0, color='red', label=self.nh_fx, linewidth=2, alpha=.98)  # r'$Least-Square-method$' 最小二乘
            plt.scatter(x1, y1, s=50, marker="x", label=r'Sample-point', color='black', alpha=1)  # 散点
            plt.plot(x2, y2, color='darkviolet', label=r"$Lagrange's/Newton-interpolation$", linewidth=0.7)   # 拉格朗日
            plt.plot(x5, y5, color='blue', label=r'$Piecewise-interpolation$', linewidth=.7)   #分段二次
            plt.figlegend(loc='upper left')
            plt1 = plt.gca()
            plt1.spines['top'].set_color('none')
            plt1.spines['right'].set_color('none')
            plt1.xaxis.set_ticks_position('bottom')
            plt1.yaxis.set_ticks_position('left')
            plt1.spines['bottom'].set_position(('data', low-ddy))
            plt1.spines['left'].set_position(('data', xlow-ddx))
            plt.axis([x1[0] - ddx, x1[-1] + ddx, low - ddy, up + ddy])
            plt.show()
            plt.close()
    

    最后调用一下就OK了

    def main():
        cat = Miao()
    
    main()
    

    框架大概就是上边这些
    flag又又又来了:等有时间就把细节补上
    基本能保证通过这玩意儿、可以了解到大部分常用tkinter板块、一部分matplotlib,还有一部分numpy【至少本菜鸡是这样的(T_T)】

    下边就先放一点效果图,还有一部分代码图片吧

    (代码要把绘图、获取列表放在前头,最后才是ui)

    大体框架如下
    大体框架
    函数字符串图片部分
    样本点部分

    展开全文
  • 使用Python的tkinter和matplotlib模块在GUI界面中绘制PID交互曲线,用以观察各参数对PID控制的影响 最近学PID控制,想要直观的观察PID各部分参数对PID控制的影响,就用Python写了一个界面,如下: pid_GUI.py #!/...

    使用Python的tkinter和matplotlib模块在GUI界面中绘制PID交互曲线,用以观察各参数对PID控制的影响

    最近学PID控制,想要直观的观察PID各部分参数对PID控制的影响,就用Python写了一个界面,总的来说并不复杂,如下:

    pid_GUI.py

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # author:makang
    # Description: 绘制PID交互曲线,可以观察到Kp,Ki,Kd各参数对PID控制的影响
    
    import tkinter as tk
    import PID
    import matplotlib as plt
    from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
    from matplotlib.figure import Figure
    import numpy as np
    
    # 绘图函数
    def drawPic():
        global r, g, b
    
        # 清空图像,以使得前后两次绘制的图像不会重叠
        # drawPic.f.clf()
        drawPic.a = drawPic.f.add_subplot(111)
        drawPic.a.grid(color='k', linestyle='-.')
        TestPID(Kp, Ki, Kd)
    
        # 每次绘图改变线条颜色用以区别----第一种方法
        # drawPic.a.plot(PositionalXaxis, PositionalYaxis, color=(r, g, b) )  # 绘制图形
        # r -= 0.15; g-=0.15; b-=0.15
        # if r < 0: r=0
        # if g < 0: g=0
        # if b < 0: b=0
    
        # 每次绘图改变线条颜色用以区别----第二种方法
        color = ['b', 'r', 'y', 'g', 'grey', 'coral', 'darkgreen', 'c', 'cyan', 'steelblue']
        drawPic.a.plot(PositionalXaxis, PositionalYaxis, color=color[np.random.randint(len(color))] )  # 绘制图形
        drawPic.canvas.draw()
        #每次绘图完毕清空x,y
        PositionalXaxis.clear()
        PositionalYaxis.clear()
    
    
    # 测试PID程序
    def TestPID(P, I, D):
        global PositionalXaxis, PositionalYaxis, fig_num
        PositionalPid = PID.PositionalPID(P, I, D)
        for i in range(1, 500):
            # 位置式
            PositionalPid.SetStepSignal(100.2)
            PositionalPid.SetInertiaTime(3, 0.1)
            PositionalYaxis.append(PositionalPid.SystemOutput)
            PositionalXaxis.append(i)
    
    #   改变Kp
    def Kp_enlarge():
        global Kp, Ki, Kd
        Kp += 0.2
        var_kp.set('%.2f'%Kp)
        drawPic()
        return Kp
    def Kp_reduce():
        global Kp
        Kp -= 0.2
        if Kp<=0:
            Kp=0
        var_kp.set('%.2f'%Kp)
        drawPic()
        return Kp
    
    #   改变Ki
    def Ki_enlarge():
        global Ki
        Ki += 0.2
        var_ki.set('%.2f'%Ki)
        drawPic()
        return Ki
    def Ki_reduce():
        global Ki
        Ki -= 0.2
        if Ki<=0:
            Ki=0
        var_ki.set('%.2f'%Ki)
        drawPic()
        return Ki
    
    #   改变Kd
    def Kd_enlarge():
        global Kd
        Kd += 0.2
        var_kd.set('%.2f'%Kd)
        drawPic()
        return Kd
    def Kd_reduce():
        global Kd
        Kd -= 0.2
        if Kd<=0:
            Kd=0
        var_kd.set('%.2f'%Kd)
        drawPic()
        return Kd
    
    
    #   清空图像
    def clear_pic():
        global r, g, b
        drawPic.f.clf()
        drawPic.canvas.draw()
        drawPic.a = drawPic.f.add_subplot(111)
        r = 0.6;
        g = 0.8;
        b = 0.8
    
    #   重置
    def reset():
        global Kp, Ki, Kd, r, g, b
        drawPic.f.clf()
        drawPic.canvas.draw()
        Kp=0.1
        Ki=0.1
        Kd=0.1
        var_kp.set('%.2f' % Kp)
        var_ki.set('%.2f' % Ki)
        var_kd.set('%.2f' % Kd)
        r = 0.6;
        g = 0.8;
        b = 0.8
    
    if __name__ == '__main__':
    
        # 实例化object,建立窗口window
        window = tk.Tk()
        # 给窗口的可视化起名字
        window.title('PID_GUI')
        # 设定窗口的大小(长 * 宽)
        window.geometry('800x700')  # 这里的乘是小x
        plt.use('TkAgg')
    
        var_kp = tk.StringVar()    # 将label标签的内容设置为字符类型,用var来接收函数的传出内容用以显示在标签上
        var_ki = tk.StringVar()
        var_kd = tk.StringVar()
        Kp=0.1
        Ki=0.1
        Kd=0.1
        var_kp.set('%.2f' % Kp)
        var_ki.set('%.2f' % Ki)
        var_kd.set('%.2f' % Kd)
        PositionalXaxis = [0]
        PositionalYaxis = [0]
    
        #定义颜色
        r=0.6; g=0.8; b=0.8
    
        # 绘图区域
        pic_area = tk.Label(window, bg='grey')
        pic_area.place(x=0, y=0, width='800', height='600')
        # 在Tk的GUI上放置一个画布
        drawPic.f = Figure(figsize=(5, 4), dpi=100)
        drawPic.canvas = FigureCanvasTkAgg(drawPic.f, master=pic_area)
        drawPic.canvas.draw()
        drawPic.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
    
    
        #调节控件
        Kp_label = tk.Label(textvariable=var_kp, bg='grey')
        Kp_label.place(x=20, y= 630, width='50', height='50')
        Kp_add_Btn = tk.Button(window, text='Kp Enlarge', font=('Arial', 10), width=10, height=1, command=Kp_enlarge)
        Kp_add_Btn.place(x=80, y=620, width='100', height='30')
        Kp_down_Btn = tk.Button(window,text='Kp Reduce', font=('Arial', 10), width=10, height=1, command=Kp_reduce)
        Kp_down_Btn.place(x=80, y=660, width='100', height='30')
    
        Ki_label = tk.Label(textvariable=var_ki, bg='grey')
        Ki_label.place(x=210, y= 630, width='50', height='50')
        Ki_add_Btn = tk.Button(window, text='Ki Enlarge', font=('Arial', 10), width=10, height=1, command=Ki_enlarge)
        Ki_add_Btn.place(x=270, y=620, width='100', height='30')
        Ki_down_Btn = tk.Button(window,text='Ki Reduce', font=('Arial', 10), width=10, height=1, command=Ki_reduce)
        Ki_down_Btn.place(x=270, y=660, width='100', height='30')
    
        Kd_label = tk.Label(textvariable=var_kd, bg='grey')
        Kd_label.place(x=400, y= 630, width='50', height='50')
        Kd_add_Btn = tk.Button(window, text='Kd Enlarge', font=('Arial', 10), width=10, height=1, command=Kd_enlarge)
        Kd_add_Btn.place(x=460, y=620, width='100', height='30')
        Kd_down_Btn = tk.Button(window,text='Kd Reduce', font=('Arial', 10), width=10, height=1, command=Kd_reduce)
        Kd_down_Btn.place(x=460, y=660, width='100', height='30')
    
        Clear_Btn = tk.Button(window, text='Clear', font=('Arial', 10), width=10, height=1, command=clear_pic)
        Clear_Btn.place(x=580, y=620, width='100', height='70')
    
        Reset_Btn = tk.Button(window, text='Reset', font=('Arial', 10), width=10, height=1, command=reset)
        Reset_Btn.place(x=680, y=620, width='100', height='70')
    
        window.mainloop()
        # 注意,loop因为是循环的意思,window.mainloop就会让window不断的刷新,如果没有mainloop,就是一个静态的window,传入进去的值就不会有循环,mainloop就相当于一个很大的while循环,有个while,每点击一次就会更新一次,所以我们必须要有循环
        # 所有的窗口文件都必须有类似的mainloop函数,mainloop是窗口文件的关键的关键。
    
    

    其中用到的PID是自定义模块,参考的别人的,用于对一阶惯性系统进行控制测试。
    PID.py

    #  PID控制一阶惯性系统测试程序
    
    import numpy as np
    
    # *****************************************************************#
    #                      增量式PID系统                              #
    # *****************************************************************#
    class IncrementalPID:
        def __init__(self, P, I, D):
            self.Kp = P
            self.Ki = I
            self.Kd = D
    
            self.PIDOutput = 0.0  # PID控制器输出
            self.SystemOutput = 0.0  # 系统输出值
            self.LastSystemOutput = 0.0  # 上次系统输出值
    
            self.Error = 0.0  # 输出值与输入值的偏差
            self.LastError = 0.0
            self.LastLastError = 0.0
    
        # 设置PID控制器参数
        def SetStepSignal(self, StepSignal):
            self.Error = StepSignal - self.SystemOutput
            IncrementValue = self.Kp * (self.Error - self.LastError) + self.Ki * self.Error + self.Kd * (
                        self.Error - 2 * self.LastError + self.LastLastError)
            self.PIDOutput += IncrementValue
            self.LastLastError = self.LastError
            self.LastError = self.Error
    
        # 设置一阶惯性环节系统  其中InertiaTime为惯性时间常数
        def SetInertiaTime(self, InertiaTime, SampleTime):
            self.SystemOutput = (InertiaTime * self.LastSystemOutput + SampleTime * self.PIDOutput) / (
                        SampleTime + InertiaTime)
            self.LastSystemOutput = self.SystemOutput
    
    
    # *****************************************************************#
    #                      位置式PID系统                              #
    # *****************************************************************#
    class PositionalPID:
        def __init__(self, P, I, D):
            self.Kp = P
            self.Ki = I
            self.Kd = D
    
            self.SystemOutput = 0.0
            self.ResultValueBack = 0.0
            self.PidOutput = 0.0
            self.PIDErrADD = 0.0
            self.ErrBack = 0.0
    
        def SetInertiaTime(self, InertiaTime, SampleTime):
            self.SystemOutput = (InertiaTime * self.ResultValueBack + SampleTime * self.PidOutput) / (
                        SampleTime + InertiaTime)
            self.ResultValueBack = self.SystemOutput
    
        def SetStepSignal(self, StepSignal):
            Err = StepSignal - self.SystemOutput
            KpWork = self.Kp * Err
            KiWork = self.Ki * self.PIDErrADD
            KdWork = self.Kd * (Err - self.ErrBack)
            self.PidOutput = KpWork + KiWork + KdWork
            self.PIDErrADD += Err
            self.ErrBack = Err
    

    有点难度的地方在于:一是要把Matplotlib图像放到界面上去;二是定义绘图函数,也用过别的方法,都不完美,现在这种方法可以实现需求。其中每次绘图使用不同的颜色可以有两种方法,一种是使用rgb数值,每次改变数值,另一种是定义颜色列表,每次随机选取。

    运行后的效果如下:

    在这里插入图片描述
    现在可以调节Kp,Ki,Kd的值来观察各参数对PID控制的影响。

    展开全文
  • from tkinter.filedialog import askopenfilename import pandas as pd import matplotlib.pyplot as plt import numpy as np tag = 1 while tag: data_file_path = askopenfilename(title='Select a data', file...
  • 将Matplotlib绘制的图显示到Tkinter中(详细教程)

    万次阅读 多人点赞 2019-06-13 20:56:38
    运行环境:win10、python3 用Matplotlib自定义绘制图形 三次贝塞尔曲线有四个控制点,曲线在起始点与1,2两个点相切,在结束点与3,4两个点相切。 from matplotlib.path import Path from matplotlib.patc...
  • 我用Python(Tkinter)写了一个代码,它是绘制一个3度贝塞尔曲线,并且它是工作的。。。。kinda.我的问题是:我如何直接从键盘输入x和y坐标为控制点,而不是先x后y(像我的代码)。第二个问题是关于参数(u或t值。通常它是...
  • 三次贝塞尔曲线有四个控制点,曲线在起始点与1,2两个点相切,在结束点与3,4两个点相切。 from matplotlib.path import Path from matplotlib.patches import PathPatch from matplotlib import pyplot as plt f...
  • 它是用于绘制图表和曲线图的 Python 应用。使用画布的语法如下:语法w=canvas(parent,options)可能选项的列表如下。Sn选项描述1bd表示该边界的宽度,默认的宽度是 22bg它代表了画布的背景颜色3confine它被设置为使...
  • 在画布上实现正弦曲线和文字的绘制 from tkinter import * import math WIDTH=400;HEIGHT=210 #画布的宽度和高度 ORIGIN_X=2;ORIGIN_Y=HEIGHT/2 #原点x,y SCALE_X=40;SCALE_Y=100 #x,y轴缩放倍数 END_ARC=360*2 #...
  • 我想为我的程序画一个红色的4瓣单位:我需要能够分别绘制每一条曲线,这样我就可以加粗它们,使大胆的白色图案在图片中显示出来。在我在想把原点坐标放在中心,然后在圆点周围画8个四分之一圆。在希望我只有一个函数...
  • 安装 pip install tk_tools 使用 import tkinter as tk import tk_tools import random import time ...def add_series():# 绘制曲线 global n line_1 = [(1.0*x/n, x/n*random.random()) for x in ran
  • 目录 目录 前言 (一)小目标 1.首页卷面: 2.绘制一条函数曲线: 3.绘制多条曲线: (二)官方教材 1.对GUI框架的支持: 2.内嵌于tkinter的说明文档: ...
  • 它是用于绘制图表和曲线图的 Python 应用。画布部件则用于将结构化图形的 Python 应用。它是用于绘制图表和曲线图的 Python 应用。使用画布的语法如下:语法w = canvas(parent,options)可能选项的列表如下。Sn选项...
  • 我想:在键盘上手动改变拟合曲线的起始参数(即猜测参数),并根据实验数据绘制相应的曲线,以便从一个好的点开始拟合程序(已实现)当我通过键盘更改参数时,显示小部件中参数的实际值(未实现)我在网上搜索了一下,发现...
  • 如何将UG中的图纸导入到CAD中CAD-实力讲解----起重钩的绘制UG工程图模型细节部分表达UG模型实例---耳机的绘制UG“通过曲线网格”命令在网上买了条裤子,结果裤子太大了,为了不浪费买裤子的钱,我拼命的吃,半个月后...
  • 主要用于绘制图表和曲线图。语法格式:可能选项的列表如下选项描述bd表示该边界的宽度,默认的宽度是 2bg它代表了画布的背景颜色confine它被设置为使用画布unscrollable以外的滚动区域cursor在画布上设置光标为箭头...
  • 13 数分与科计视化

    2020-06-16 17:55:04
    文章目录13.5 matplotlib13.5.1 绘制正弦曲线 13.5 matplotlib 依赖 numpy和 tkinter, 可绘制, 线图、直方图、併状图、散点图、误差线图, 图形质量可满足出版要求 13.5.1 绘制正弦曲线 import numpy as np import ...
  • Matplotlib应用

    2020-03-09 14:05:31
    matplotlib模块依赖于numpy模块和tkinter模块,可以绘制多种形式的图形, 包括线图、直方图、饼状图、散点图、误差线图等等,图形质量可满足出版要求,是数据可视化的重要工具 Matplotlib模块: 1.绘制正弦余弦曲线 ...
  • Matplotlib案例入门

    2020-04-17 18:14:18
    Matplotlib模块依赖于NumPy模块和tKinter模块,可以通过该模块绘制多种形式的图形,包括线图、直方图、饼状图、散点图和误差线图等。是计算结果可视化的重要工具 绘制带有中文标签和图例的正弦余弦曲线 关于line2D...
  • 学习曲线低,非专业人士也能上手 开源系统,拥有强大的生态圈 解释型语言,完美的平台可移植性 动态类型语言,支持面向对象和函数式编程 代码规范程度高,可读性强 Python在以下领域都有用武之地。 后端开发 - ...

空空如也

空空如也

1 2
收藏数 30
精华内容 12
关键字:

tkinter绘制曲线