代码地址
完整源代码地址在这里:码云代码地址
一共有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())