|
|
import tkinter as tk
|
|
|
from tkinter import messagebox
|
|
|
from collections import OrderedDict
|
|
|
from mpl_toolkits import axisartist
|
|
|
from sympy import sympify, SympifyError
|
|
|
from scipy.optimize import curve_fit
|
|
|
from tkinter import filedialog
|
|
|
import numpy as np
|
|
|
import matplotlib.pyplot as plt
|
|
|
|
|
|
from formula import expression_output
|
|
|
|
|
|
index = 1
|
|
|
|
|
|
def check_formula_syntax(formula):
|
|
|
try:
|
|
|
sympify(formula)
|
|
|
return True
|
|
|
except SympifyError:
|
|
|
return False
|
|
|
# 创建弹出窗口
|
|
|
def creat_window(title):
|
|
|
top = tk.Toplevel(Root)
|
|
|
top.geometry("300x350")
|
|
|
top.title(title)
|
|
|
return top
|
|
|
|
|
|
|
|
|
# 输入框
|
|
|
def create_input_box(top, text, value):
|
|
|
box_label = tk.Label(top, text=text)
|
|
|
box_label.pack(padx=10, pady=10)
|
|
|
box_size = tk.IntVar(top, value=value) # 创建一个IntVar对象,并设置默认值为3
|
|
|
box_size_entry = tk.Entry(top, textvariable=box_size) # 关联IntVar对象
|
|
|
box_size_entry.pack(padx=20, pady=20)
|
|
|
return box_size_entry
|
|
|
|
|
|
|
|
|
def askfile():# 从本地选择一个文件,并返回文件的路径
|
|
|
global sampleData
|
|
|
# 弹出文件选择对话框,选择要打开的文本文件
|
|
|
file_path = filedialog.askopenfilename(filetypes=[('Text Files', '*.txt')])
|
|
|
|
|
|
# 如果没有选择文件,直接返回
|
|
|
if not file_path:
|
|
|
return
|
|
|
x, y = [], []#初始化x,y
|
|
|
with open(file_path, 'r') as file:
|
|
|
for line in file:#逐行读文件
|
|
|
line = line.strip('\n')#移除换行符
|
|
|
sx, sy = line.split(' ')#以空格分割x,y
|
|
|
x.append(float(sx))#将sx转换为浮点数并加入数组
|
|
|
y.append(float(sy))#将sy转换为浮点数并加入数组
|
|
|
X, Y = np.array(x), np.array(y)#x,y数组转为array并赋值给全局变量
|
|
|
sampleData.append(X)
|
|
|
sampleData.append(Y)
|
|
|
print(sampleData)
|
|
|
|
|
|
|
|
|
def create_func(code_str):
|
|
|
# 创建一个空的命名空间
|
|
|
namespace = {}
|
|
|
# 使用exec函数执行字符串代码,并指定命名空间为locals
|
|
|
exec(code_str, globals(), namespace)
|
|
|
# 返回命名空间中的函数对象
|
|
|
return namespace['func']
|
|
|
def draw_axis(low, high, step=250):
|
|
|
fig = plt.figure(figsize=(4.4, 3.2))
|
|
|
ax = axisartist.Subplot(fig, 111)# 使用axisartist.Subplot方法创建一个绘图区对象ax
|
|
|
fig.add_axes(ax)# 将绘图区对象添加到画布中
|
|
|
ax.axis[:].set_visible(False)# 通过set_visible方法设置绘图区所有坐标轴隐藏
|
|
|
ax.axis["x"] = ax.new_floating_axis(0, 0)# 添加新的x坐标轴
|
|
|
ax.axis["x"].set_axisline_style("-|>", size=1.0)# 给x坐标轴加上箭头
|
|
|
ax.axis["y"] = ax.new_floating_axis(1, 0)# 添加新的y坐标轴
|
|
|
ax.axis["y"].set_axisline_style("-|>", size=1.0)# y坐标轴加上箭头
|
|
|
ax.axis["x"].set_axis_direction("bottom")# 设置x、y轴上刻度显示方向
|
|
|
ax.axis["y"].set_axis_direction("left")# 设置x、y轴上刻度显示方向
|
|
|
plt.xlim(low, high) # 把x轴的刻度范围设置
|
|
|
plt.ylim(low, high) # 把y轴的刻度范围设置
|
|
|
ax.set_xticks(np.arange(low, high + 5, step))# 把x轴的刻度间隔设置
|
|
|
ax.set_yticks(np.arange(low, high + 5, step))# 把y轴的刻度间隔设置
|
|
|
def fitting():
|
|
|
global letters
|
|
|
askfile()
|
|
|
letters, result=expression_output(result2)
|
|
|
# print(letters,result)
|
|
|
# 定义要转换的字符串
|
|
|
code_str = '''
|
|
|
def func({}):
|
|
|
return {}
|
|
|
'''.format(letters,result)
|
|
|
# print(code_str)
|
|
|
my_func = create_func(code_str)
|
|
|
popt, pcov = curve_fit(my_func, sampleData[0], sampleData[1])# 用curve_fit来对点进行拟合
|
|
|
draw_axis(-1000, 1000) # 绘制坐标轴
|
|
|
positive_mask = sampleData[0] >= 0.0#给样本点分类
|
|
|
negative_mask = sampleData[1] < 0.0#给样本点分类
|
|
|
positive_colors = ['red' if x >= 0.0 else 'blue' for x in sampleData[0]]#给样本点分类
|
|
|
negative_colors = ['green' if x >= 0.0 else 'purple' for x in sampleData[0]]#给样本点分类
|
|
|
#根据样本点类型决定样本点颜色
|
|
|
plt.scatter(sampleData[0][positive_mask], sampleData[1][positive_mask], color=np.array(positive_colors)[positive_mask], lw=1)
|
|
|
plt.scatter(sampleData[0][negative_mask], sampleData[1][negative_mask], color=np.array(negative_colors)[negative_mask], lw=1)
|
|
|
curve_x = np.arange(-1000, 1000)#按照步长生成的一串数字
|
|
|
curve_y = [my_func(i, * popt) for i in curve_x]# 根据x来计算y1值
|
|
|
plt.plot(curve_x, curve_y, color='blue', label='Fitted Curve')#绘制拟合曲线
|
|
|
plt.savefig(r"dot5.png", facecolor='w') # 保存到本地
|
|
|
plt.legend()
|
|
|
plt.show()
|
|
|
|
|
|
def add():
|
|
|
top = creat_window('新增拟合函数') # 创建弹出窗口
|
|
|
function_entry = create_input_box(top, "拟合函数名称:", '') # 创建一个输入框,获取拟合函数名称
|
|
|
math_entry = create_input_box(top, "数学式:", '') # 创建一个输入框,获取数学式
|
|
|
def get_input(): # 创建
|
|
|
# 获取输入框的内容
|
|
|
global result1, result2
|
|
|
result1 = function_entry.get()
|
|
|
result2 = math_entry.get()
|
|
|
if result1 and result2:
|
|
|
if check_formula_syntax(result2):
|
|
|
top.destroy() # 关闭窗口
|
|
|
# fitting()
|
|
|
letters, result = expression_output(result2)
|
|
|
print(letters)
|
|
|
print(result)
|
|
|
else:
|
|
|
messagebox.showinfo('提示', '你输入的数学公式错误!')
|
|
|
else:
|
|
|
messagebox.showinfo('提示','你未输入的完成!')
|
|
|
|
|
|
button = tk.Button(top, text="确定", command=get_input)
|
|
|
button.pack()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
# 创建一个二维数组sampleData,用于存储样本数据
|
|
|
sampleData = []
|
|
|
Root = tk.Tk()
|
|
|
window_width = 400 # 窗口的宽度
|
|
|
window_height = 350 # 窗口的高度
|
|
|
Root.title("函数拟合")
|
|
|
Root.geometry("400x350") # 设置窗口的大小和位置
|
|
|
button = tk.Button(Root,text='新增拟合函数',bg='blue',command=add)
|
|
|
button.place(x=250,y=10)
|
|
|
Root.mainloop() |