python tkinter matplotlib scipy numpy 实现自定义任意二维曲线拟合工具

9 篇文章 0 订阅
9 篇文章 0 订阅

代码地址

完整源代码地址在这里:码云代码地址
一共有GUI.py,main.py和algorithm.py三个文件,放在同一个文件夹中,运行main.py即可。

使用方法

操作方法:

在输入x和输入y处输入以逗号分隔的数,要求长度相等;

在变量名处输入变量名,以逗号分隔;

函数表达式可以以Python语法输入,其中**代表乘方。要输入sin等函数,请使用np.sin(),其余以此类推。

点击右下角的按钮即可进行计算。

在这里插入图片描述

虽然上面已经写了码云代码地址,但便于各位看官点评起见,我还是附上代码吧。以下三个代码文件在同一个文件夹中

附代码

algorithm.py

##使用curve_fit

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit



def fit(x, y,argsStr='a,b,c,d',funcstr='a * x ** 3 + b * x ** 2 + c * x + d'):
    # 非线性最小二乘法拟合
    st='''
def func(x, %s):
    return %s
    '''%(argsStr,funcstr)
    exec(st,globals())
    popt, pcov = curve_fit(func, x, y)
    # 获取popt里面是拟合系数
    yvals = func(x, *popt)
    # 拟合,将数组作为函数的参数进行传入。
    return popt, pcov, yvals


if __name__ == '__main__':
    fit(x=np.array([1,2,3,4,4]), y=np.array([3,4,5,6,7]))

main.py

'''
曲线拟合工具
作者:侯展意
协议:木兰2.0
'''
from GUI import run
run()

GUI.py

'''
曲线拟合工具
作者:侯展意
协议:木兰2.0
'''
import tkinter as tk
from tkinter import ttk
import traceback
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.backend_bases import key_press_handler
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import time
import threading
import os
import sys
import multiprocessing
import algorithm
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

def splitStrToDataList(st=''):
    if st.find(',')!=-1:
        return st.strip().split(',')
    else:
        return st.strip().split(' ')

def genData(textX='1,2,3',textY='3,4,5'):
    lX=splitStrToDataList(textX)
    lY=splitStrToDataList(textY)
    xArray=np.zeros(len(lX))
    yArray=np.zeros(len(lY))
    for i in range(len(lX)):
        xArray[i]=float(lX[i])
        yArray[i]=float(lY[i])
    return xArray,yArray

class controlFrame(ttk.Frame):
    def __init__(self,master,**kwargs):
        super().__init__(master,**kwargs)
        self.refreshingStatus=True
        self.refreshingStatusButton = ttk.Button(master=self, 
             text="计算",command=self.stopOrContinueRefreshing)#控制是否停止的按钮
        
        
        funcArgsInputLabel=ttk.Label(master=self,text='输入函数变量名')
        self.funcArgsInputEntry=ttk.Entry(master=self)
        
        
        self.xEntryLabel=ttk.Label(master=self,text='输入x')
        self.xEntry=ttk.Entry(master=self)
        self.yEntryLabel=ttk.Label(master=self,text='输入y')
        self.yEntry=ttk.Entry(master=self)


        
        funcInputLabel=ttk.Label(master=self,text='输入函数表达式' )
        
        self.funcInputEntry=tk.Text(master=self,height=4)
        

        refreshingIntervalInputLabel=ttk.Label(master=self,text='设定刷新间隔(ms):')

        self.renderingTimeLabel=ttk.Label(master=self,text='渲染时间消耗:1000 ms')
        
        self.refreshingRateEntry=ttk.Entry(master=self)
        self.refreshingRateEntry.insert(0,'2000')

        
        self.xEntryLabel.pack(expand=1,fill=tk.X)
        self.xEntry.pack(expand=1,fill=tk.X)
        
        self.yEntryLabel.pack(expand=1,fill=tk.X)
        self.yEntry.pack(expand=1,fill=tk.X)
        
        funcArgsInputLabel.pack(expand=1,fill=tk.X)
        self.funcArgsInputEntry.pack(expand=1,fill=tk.X)
        
        funcInputLabel.pack(expand=1,fill=tk.X)
        self.funcInputEntry.pack(expand=1,fill=tk.X)
        
        self.refreshingStatusButton.pack()
        

        self.xEntry.insert(0,'1,2,3,4,5')
        self.yEntry.insert(0,'1.1,2.1,3.3,5.0,4.9')
        self.funcArgsInputEntry.insert(0,'a,b,c,d')
        self.funcInputEntry.insert(tk.END,'a * x ** 3 + b * x ** 2 + c * x + d')
        
    def getArgsAndFunc(self):
        argsStr=self.funcArgsInputEntry.get().strip()
        funcStr=self.funcInputEntry.get(0.0,'end').strip()
        return argsStr,funcStr
        
    def stopOrContinueRefreshing(self):
        self.master.refresh()
    def getXY(self):
        xStr=self.xEntry.get().strip()
        yStr=self.yEntry.get().strip()
        return genData(xStr,yStr)
        

class matplotlibFloatWindow(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("曲线拟合工具")
        self.wm_attributes('-topmost',1)
        
        self.figure=Figure(figsize=(5,4),dpi=100)
        self.canvas=FigureCanvasTkAgg(self.figure, master=self)
        self.font=('Arial',10)
        
        self.canvas.get_tk_widget().pack(side=tk.TOP,  # 上对齐
                            fill=tk.BOTH,  # 填充方式
                            expand=tk.YES)  # 随窗口大小调整而调整

        toolbar = NavigationToolbar2Tk(self.canvas, self)
        toolbar.update()
        self.canvas._tkcanvas.pack(side=tk.LEFT,  # get_tk_widget()得到的就是_tkcanvas
                      fill=tk.BOTH,expand=tk.YES)
                # 创建一个按钮,并把上面那个函数绑定过来
                
        self.statLabel=tk.Label(master=self,text='',anchor=tk.W,height=10)
        self.statLabel.pack(expand=1,fill=tk.BOTH)

        self.controlPanel=controlFrame(master=self)
        self.controlPanel.pack(side=tk.LEFT)
        
        self.refresh()
        
    def reportStatus(self,text,warning=False):#报错函数。
        if(warning==True):
            self.statLabel.config( fg='red',bg='white')
        else:
            self.statLabel.config( fg='black',bg='white')

        self.statLabel.config(text=text)

    def showFitResult(self,popt,pcov,argsStr):
        st=''
        argsList=argsStr.split(',')
        for i in range(len(popt)):
            st+='%s = %f\n'%(argsList[i],popt[i])
        self.reportStatus(st)
    
    def refresh(self):
        self.figure.clf()
        ax=self.figure.add_subplot(111)
        try:
            x,y=self.controlPanel.getXY()
            argsStr,funcStr=self.controlPanel.getArgsAndFunc()
            popt,pcov,yvals=algorithm.fit(x,y,argsStr,funcStr)
            self.showFitResult(popt,pcov,argsStr)
        except Exception as e:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            e=traceback.format_exception(exc_type, exc_value,
                                          exc_traceback)
            st=''
            for s in e:
                st+=s      
            self.reportStatus(st,warning=True)
##            self.after(1000,self.refresh)
            
        plot1 = ax.plot(x, y, 's', label='original values')
        plot2 = ax.plot(x, yvals, 'r', label='polyfit values')
        ax.set_xlabel('x')
        ax.set_ylabel('y')
        ax.legend(loc=4)  # 指定legend的位置右下角
        ax.set_title('curve_fit')
        ax.grid()
        self.canvas.draw()
        self.canvas.flush_events()
##        self.after(1000,self.refresh)

    def on_key_event(self,event):
        """键盘事件处理"""
        print("你按了%s" % event.key)
        key_press_handler(event,self.canvas)


def _quit():
    """点击退出按钮时调用这个函数"""
    root.quit()  # 结束主循环
    root.destroy()  # 销毁窗口

def run():
    root=matplotlibFloatWindow()
    root.canvas.mpl_connect('key_press_event', root.on_key_event)
    root.mainloop()
if __name__=='__main__':
    print(genData())

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值