You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
fitt/X5.py

549 lines
23 KiB

# -*- coding: utf-8 -*-
# Time : 2023/8/9 10:20
# Author : lirunsheng
# User : l'r's
# Software: PyCharm
11 months ago
# File : X5.py
# -*- encoding: utf-8 -*-
"""
@Author: packy945
@FileName: main.py
@DateTime: 2023/5/30 15:29
@SoftWare: PyCharm
"""
11 months ago
import random
import re
from tkinter import messagebox
import pandas as pd
from sympy import sympify, SympifyError
import sys
from fitting import *
import numpy as np
import data as gl_data
import input
import tkinter as tk
11 months ago
from tkinter import ttk
from tkinter import *
11 months ago
from screeninfo import get_monitors
import os
from PIL import ImageTk, Image
import tkinter.filedialog # 注意次数要将文件对话框导入
# from demo1 import Addfunc
11 months ago
from newly_added.formula import *
from scipy.optimize import leastsq
global Q_root,F_root
global root_window
global label1,label2,label3
11 months ago
# 创建弹出窗口
def creat_window(title):
top = tk.Toplevel(root_window)
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 check_formula_syntax(formula):
try:
sympify(formula)
return True
except SympifyError:
return False
def create_func(code_str):
# 创建一个空的命名空间
namespace = {}
# 使用exec函数执行字符串代码并指定命名空间为locals
exec(code_str, globals(), namespace)
# 返回命名空间中的函数对象
return namespace['func']
def fitting(letters,result):
code_str = '''
def func({}):
return {}
'''.format(letters,result)
return code_str
def save(result1, letters, result , result2):
gl_data.INDEX = len(gl_data.FITT_SAVE['no'])
gl_data.FITT_SAVE['no'][gl_data.INDEX] = gl_data.INDEX
gl_data.FITT_SAVE['name'][gl_data.INDEX] = result1
gl_data.FITT_SAVE['variable'][gl_data.INDEX] = letters
gl_data.FITT_SAVE['function'][gl_data.INDEX] = result
gl_data.FITT_SAVE['demo'][gl_data.INDEX] = result2
file_path = 'functions_new.xlsx'#设置路径
# 将字典转换为DataFrame
df = pd.DataFrame(gl_data.FITT_SAVE)
# 将DataFrame保存为Excel文件
df.to_excel(file_path, index=False)
def read_fitting():
file_path = 'functions_new.xlsx' # 设置路径
df = pd.read_excel(io=file_path, header=0) # 设置表格
gl_data.FITT_SAVE = df.to_dict()
def create_func(code_str):
# 创建一个空的命名空间
namespace = {}
# 使用exec函数执行字符串代码并指定命名空间为locals
exec(code_str, globals(), namespace)
# 返回命名空间中的函数对象
return namespace['func']
def addfunc():
11 months ago
top = creat_window('新增拟合函数') # 创建弹出窗口
function_entry = create_input_box(top, "拟合函数名称:", '') # 创建一个输入框,获取拟合函数名称
math_entry = create_input_box(top, "数学式:", '') # 创建一个输入框,获取数学式
def get_input(): # 创建
# 获取输入框的内容
result1 = function_entry.get()
result2 = math_entry.get()
if result1 and result2:
if check_formula_syntax(result2):
top.destroy() # 关闭窗口
letters, result = expression_output(result2)
save(result1, letters, result, result2)
gl_data.Xian_index = gl_data.INDEX
f_button()
else:
messagebox.showinfo('提示', '你输入的数学公式错误!')
else:
messagebox.showinfo('提示','你未输入的完成!')
button = tk.Button(top, text="确定", command=get_input)
button.pack()
def jis():
global screen_size
# 获取当前屏幕的宽度和高度
monitors = get_monitors()
screen_width = monitors[0].width
# 计算按钮的宽度和高度
button_width = int(screen_width * 0.85)
# 打印按钮的宽度和高度
screen_size = round(button_width*0.9/900, 1)
gl_data.MAGNIDICATION = screen_size
read_fitting()
def element1(path):
# print(path)
# 加载图像并创建一个 PhotoImage 对象
image = tk.PhotoImage(file=path)
# 保存图片的引用,防止被垃圾回收
root_window.image = image
return image
def element(path, width, height):
# 加载图元对应的图片文件
img = Image.open(path)
# # 使用resize方法调整图片
img = img.resize((width, height))
# 把Image对象转换成PhotoImage对象
img = ImageTk.PhotoImage(img)
# 保存图片的引用,防止被垃圾回收
root_window.img = img
return img
def window():
global root_window
global label1, label2, label3
root_window = Tk()
root_window.title('函数拟合')
11 months ago
width, height = int(900*screen_size), int(560*screen_size)
root_window.geometry(str(width)+'x'+str(height)) # 设置窗口大小:宽x高,注,此处不能为 "*",必须使用 "x"
# 设置主窗口的背景颜色,颜色值可以是英文单词或者颜色值的16进制数,除此之外还可以使用Tk内置的颜色常量
11 months ago
img_path = ["background/add.png", "background/下方输入框.png", "background/右侧未选中.png", "background/右侧背景.png",
"background/右侧选中.png","background/大背景.png", "background/导航拦.png", "background/导航输入框.png",
"background/小直线.png", "background/手动输入数据集.png", "background/拟合.png", "background/新增拟合曲线类型.png",
"background/显示数据集与曲线.png", "background/未选中.png", "background/生成数据集.png", "background/矩形.png",
"background/背景图片.png", "background/装载.png", "background/选中.png", "background/显示数据集.png",
"background/img.png"
]
global list_image
list_image = []
for i in range(len(img_path)):
if i in [17, 14, 19, 10, 12]:
list_image.append(element(img_path[i], 110, 80))
elif i == 16:
list_image.append(element(img_path[i], width, height))
elif i in [11, 9]:
list_image.append(element(img_path[i], 240, 80))
else:
list_image.append(element1(img_path[i]))
# root_window["background"] = "#87ceeb"
canvas = tk.Canvas(root_window, width=width-10, height=int(560*screen_size)-10)
canvas.place(x=0, y=0)
canvas.create_image(0, 0, image=list_image[16], anchor=tk.NW)
root_window.resizable(0, 0) # 防止用户调整尺寸
11 months ago
canvas.create_text(int(40*screen_size), int(26*screen_size), text='样本数据\n集文件', font='Arial 14', fill='black')
label3 = tk.Label(root_window, text="", font=('Times', 12), bg="#ADD8E6", fg="black",
width=50, height=3, padx=0, pady=0, borderwidth=0, relief="ridge", highlightcolor="blue")
label3.place(x=int(122*screen_size), y=int(10*screen_size))
# 使用按钮控件调用函数
11 months ago
tk.Button(root_window, image=list_image[17], relief=tk.FLAT, bd=0, command=lambda: askfile()).place(x=int(490*screen_size), y=int(12*screen_size))
canvas.create_text(int(610*screen_size), int(40 * screen_size), text='拟合曲线类型', font='Arial 14', fill='black')
gl_data.Canvas2 = tk.Canvas(root_window, bg='white', width=int(550*screen_size), height=int(330*screen_size))
gl_data.Canvas2.place( x=int(4*screen_size), y=int(60*screen_size))
canvas.create_text(int(60*screen_size), int(490*screen_size),text="结果输出:", font='Arial 16', fill='black')
# 定义一个处理文件的相关函数
def askfile():
# 从本地选择一个文件,并返回文件的路径
filename = tkinter.filedialog.askopenfilename()
if filename != '':#若选中了一个文件,则对文件进行读取
label3.config(text=filename)#显示文件路径
read_sample_data(filename)#将文件读取并存到sampleData中
# print_sample_data(filename)#将文件读取并逐行输出
selfdata_show(gl_data.X, gl_data.Y, gl_data.LOW, gl_data.HIGH)
else:#若未选择文件,则显示为空
label3.config(text='')
def print_sample_data(file_path):#打开对应文件
with open(file_path, 'r') as file:
for line in file:#逐行读取文件
line = line.strip('\n')#移除换行符
sx, sy = line.split(' ')#以空格分割xy
print(f'sx: {float(sx)}, sy: {float(sy)}')#将sx、sy转换为浮点数并打印
def read_sample_data(file_path):
x, y = [], []#初始化xy
with open(file_path, 'r') as file:
for line in file:#逐行读文件
line = line.strip('\n')#移除换行符
sx, sy = line.split(' ')#以空格分割xy
x.append(float(sx))#将sx转换为浮点数并加入数组
y.append(float(sy))#将sy转换为浮点数并加入数组
gl_data.X, gl_data.Y = np.array(x), np.array(y)#xy数组转为array并赋值给全局变量
def buttons():
# 随机生成样本点并显示
11 months ago
b_dot = tk.Button(root_window, image=list_image[14], relief=tk.FLAT, bd=0, command=lambda: generate_and_plot_sample_data(gl_data.LOW, gl_data.HIGH))
b_dot.place(x=int(435*screen_size), y=int(410*screen_size))
# 显示当前样本点
11 months ago
b_show = tk.Button(root_window, image=list_image[19], relief=tk.FLAT, bd=0, command=lambda: selfdata_show(gl_data.X, gl_data.Y, gl_data.LOW, gl_data.HIGH))
b_show.place(x=int(515*screen_size), y=int(410*screen_size))
# 显示数据集与曲线
11 months ago
b_line = tk.Button(root_window, image=list_image[12], relief=tk.FLAT, bd=0, command=lambda: draw_dots_and_line(gl_data.Xian_index, gl_data.LOW, gl_data.HIGH,
gl_data.X, gl_data.Y))
b_line.place(x=int(595*screen_size), y=int(410*screen_size))
# 手动输入数据集
11 months ago
b_input = tk.Button(root_window, image=list_image[9], relief=tk.FLAT, bd=0, command=lambda: input.input_num(root_window))
b_input.place(x=int(675*screen_size), y=int(410*screen_size))
# 拟合并输出拟合结果
11 months ago
b_fit = tk.Button(root_window, image=list_image[10], relief=tk.FLAT, bd=0, command=lambda: fit_data(gl_data.Xian_index, gl_data.X, gl_data.Y))
b_fit.place(x=int(821*screen_size), y=int(410*screen_size))
def show_fit():
sout = str(gl_data.Out)
ans = tk.Label(root_window, text=sout, font=("微软雅黑", 14)
, anchor=W, justify='left')
11 months ago
ans.place(x=int(120*screen_size), y=int(480*screen_size), width=int(760*screen_size), height=100)
print(sout)
11 months ago
def fit_data(xian_index, sx, sy):
11 months ago
if gl_data.Quadrant:
x = sx.tolist()
y = sy.tolist()
negative_x = [i for i, num in enumerate(x) if num < 0]
negative_y = [i for i, num in enumerate(y) if num < 0]
negative = list(set(negative_x + negative_y))
for index in range(len(negative)):
# print(negative[-index-1])
x.pop(negative[-index-1])
y.pop(negative[-index-1])
sx = np.array(x)
sy = np.array(y)
letters, result = gl_data.FITT_SAVE['variable'][xian_index], gl_data.FITT_SAVE['function'][xian_index]
code_str = fitting(letters, result)
func = create_func(code_str) # 获取当前函数func
popt, pcov = curve_fit(func, sx, sy) # 用curve_fit来对点进行拟合
yvals_pow = func(gl_data.X, *popt)
rr = gl_data.goodness_of_fit(yvals_pow, gl_data.Y)# 计算本次拟合的R*R值用于表示拟合优度
# 输出拟合后的各个参数值
11 months ago
ans = '\n函数系数F(x) = '
letter = str(letters[2:]).split(',')
pattern = re.compile('|'.join(letter))
s = pattern.sub('{:.2f}', gl_data.FITT_SAVE['demo'][xian_index])
ans += str(s.format(*popt))
gl_data.Out = '函数形式:' + gl_data.FITT_SAVE['name'][xian_index] + ' '
gl_data.Out += gl_data.FITT_SAVE['demo'][xian_index]
gl_data.Out += ans
gl_data.Out += '\n拟合优度(R\u00b2)' + str(round(rr, 5))
show_fit()# 显示函数信息到输出框
def change_Q(no):
gl_data.Quadrant = no #更改全局变量的象限显示
if no:#若为一象限则修改显示下限为0
gl_data.LOW = 0
else:#若为四象限,则修改显示下限为-gl_data.maxV
gl_data.LOW = -gl_data.MAXV
q_button()#更新象限显示面板
def q_button():
r = 7.5
rr = 2.5
for widget in Q_root.winfo_children():
widget.destroy()
11 months ago
q_cv = tk.Canvas(Q_root, width=int(400*screen_size), height=int(50*screen_size))
q_cv.place(x=0, y=0)
11 months ago
# q_cv.create_image(0, 0, image=list_image[20], anchor=tk.NW)
l = tk.Label(Q_root, text='坐标轴', bd=0, font=("微软雅黑", 16), anchor=W)
l.place(x=20, y=0, width=int(80*screen_size), height=int(50*screen_size),)
# 四象限按钮
b1 = tk.Button(Q_root, text='四象限', bd=0, font=("微软雅黑", 16)
, command=lambda: change_Q(0), anchor=W)
11 months ago
b1.place(x=int(170*screen_size), y=0, width=int(80*screen_size), height=int(50*screen_size),)
# 一象限按钮
b2 = tk.Button(Q_root, text='一象限', bd=0, font=("微软雅黑", 16)
, command=lambda: change_Q(1), anchor=W)
11 months ago
b2.place(x=int(320*screen_size), y=0, width=int(80*screen_size), height=int(50*screen_size),)
# # 绘制标记框
q_cv.create_image(int((145 - r)*screen_size), int((35 - r)*screen_size),
image=list_image[2])
q_cv.create_image(int((295 - r)*screen_size), int((35 - r)*screen_size),
image=list_image[2])
# 根据当前的象限选择值来填充标记框
if gl_data.Quadrant == 0:
11 months ago
q_cv.create_image(int((140 - rr)*screen_size), int((30 - rr)*screen_size),
image=list_image[18])
else:
11 months ago
q_cv.create_image(int((290 - rr)*screen_size), int((30 - rr)*screen_size),
image=list_image[18])
def change_f(no):
gl_data.Xian_index = no#设置全局函数编号为该函数
f_button()#重新绘制函数显示界面
def f_button():
11 months ago
r = 7.5 # 设置按钮大小
rr = 2.5 # 设置按钮大小
for widget in F_root.winfo_children():
11 months ago
widget.destroy() # 清空原有按钮
gl_data.INDEX = len(gl_data.FITT_SAVE['no'])
canvas = Canvas(F_root, width=int(320 * screen_size), height=int(270 * screen_size))
# # 将图像添加到 Canvas 组件中
canvas.pack(side="left", fill="both", expand=True)
scrollbar = Scrollbar(F_root, orient="vertical", command=canvas.yview)
scrollbar.pack(fill="y", side="right")
canvas.configure(yscrollcommand=scrollbar.set)
canvas.bind('<Configure>', lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
if gl_data.INDEX - 10 <= 0:
result = 0
else:
result = gl_data.INDEX - 10
canvas_height = int(328 * screen_size) + result*50 # 或根据需要调整适当的高度增加值
# # 绘制矩形边框及底色
canvas.create_rectangle(2, 2, int(398 * screen_size), canvas_height, fill='#ADD8E6', outline="#0099ff")
# canvas.create_image(4, 4, image=list_image[3], anchor=tk.NW)
cur = 0 # 函数计数
for i in range(gl_data.INDEX): # 遍历函数库
if gl_data.FITT_SAVE['no'][i] == gl_data.Xian_index: # 若选择为当前函数则标记
# 在指定位置绘制图片
canvas.create_image(int((20 - rr) * screen_size), int((20 + 15 + cur * 30 - rr) * screen_size), image=list_image[4])
else:
# 在指定位置绘制图片
canvas.create_image(int((20 - rr) * screen_size), int((20 + 15 + cur * 30 - rr) * screen_size),
image=list_image[2])
# 绘制切换按钮,单击则将当前使用函数切换为该函数
button = tk.Button(F_root, text=gl_data.FITT_SAVE['name'][i] + ' ' + gl_data.FITT_SAVE['demo'][i], bd=0,
bg="#ADD8E6", font=("微软雅黑", 10),
command=lambda x=gl_data.FITT_SAVE['no'][i]: change_f(x), anchor=W)
# 将按钮放置在Canvas中
canvas.create_window(int(40 * screen_size), int((30 + cur * 30) * screen_size), window=button, anchor="w")
cur += 1 # 计数加一
canvas.configure(scrollregion=canvas.bbox("all"))
def close_window():
sys.exit()
11 months ago
def functional_formula(letter,result,decimals):
# 使用 zip() 函数将两个列表组合成元组的迭代器
pairs = zip(letter, decimals)
# 使用字典推导式构建字典
target = {key: str(value) for key, value in pairs}
# print(target)
# # 依次替换字符串中的元素
for key, value in target.items():
indices = [i for i, c in enumerate(result) if c == key]
for i in indices:
if (i + 1) == len(result) or result[i + 1] in ['*', '+', '-', '/', ')', '']:
result = result[0:i] + value + result[i + 1:]
return result
def make_func(letters,result):
# print(letters,result)
letter = str(letters).split(',')
n = len(letter)-1
decimals = [round(random.uniform(-10, 10), 1) for _ in range(n)]
target_result = functional_formula(letter[1:], result, decimals)
# 清除以前的值
entry3.delete(0, tk.END)
# 填入函数
entry3.insert(0, target_result)
code_str = fitting('x', target_result)
target_func = create_func(code_str) # 获取当前函数func
curve_func(target_func)
def curve_func(func):
# 绘制拟合结果
draw_axis(-100, 100, 20)
x_fit = np.linspace(-100, 100, 100)
y_fit = func(x_fit)
plt.plot(x_fit, y_fit, 'r-', label='拟合曲线')
plt.legend()
plt.xlabel('x')
plt.ylabel('y')
# plt.show()
plt.savefig('new_line.png')
# 清除当前图形状态
plt.clf()
show_image()
def show_image():
# 加载图像
image = Image.open("new_line.png")
image = image.resize((500, 500))
# 在画布上显示图像
image_tk = ImageTk.PhotoImage(image)
root_window.image_tk = image_tk
show_canvas.create_image(0, 0, anchor="nw", image=image_tk)
def add_func():
result1 = entry1.get()
result2 = entry2.get()
if result1 and result2:
if check_formula_syntax(result2):
letters, result = expression_output(result2)
if result == None:
messagebox.showinfo('提示', '你输入的数学公式错误!')
else:
make_func(letters,result)
else:
messagebox.showinfo('提示', '你输入的数学公式错误!')
else:
messagebox.showinfo('提示','你未输入的完成!')
def customized_curve():
result1 = entry2.get()
result2 = entry3.get()
if result1 and result2:
if check_formula_syntax(result1):
letters, result = expression_output(result1)
if result == None:
messagebox.showinfo('提示', '你输入的数学公式错误!')
else:
# 删除空格
result2 = result2.replace(' ', '')
# 使用正则表达式替换数字为空字符串
result3 = re.sub(r'[-]?\d+(\.\d+)?', '', result2)
letter = str(letters).split(',')
decimals = [1 for _ in range(len(letter)-1)]
result4 = functional_formula(letter[1:], result, decimals)
result4 = result4.replace('1', '')
result4 = re.sub(r'[-]?\d+(\.\d+)?', '', result4)
result4 = result4.replace(' ', '')
# print(result3,result4)
if result3 == result4:
code_str = fitting('x', result2)
func = create_func(code_str) # 获取当前函数func
curve_func(func)
else:
messagebox.showinfo('提示', '请检查你修改的函数是否与函数式相对应!')
def func_save():
result1 = entry1.get()
result2 = entry2.get()
if result1 and result2:
if check_formula_syntax(result2):
letters, result = expression_output(result2)
if result== None:
messagebox.showinfo('提示', '你输入的数学公式错误!')
else:
save(result1, letters, result, result2)
subwindow.destroy() # 关闭子页面
gl_data.Xian_index = gl_data.INDEX
f_button()
else:
messagebox.showinfo('提示', '你输入的数学公式错误!')
else:
messagebox.showinfo('提示', '你未输入的完成!')
def show_subwindow():
global subwindow
subwindow = tk.Toplevel(root_window)
subwindow.geometry('1000x500')
subwindow.title('新增拟合函数')
# 创建画布
global show_canvas
canvas_width = 1000
canvas_height = 500
canvas = Canvas(subwindow, width=canvas_width, height=canvas_height, bg="#ADD8E6")
canvas.pack()
show_canvas = Canvas(canvas, width=600, height=500, bg="white")
show_canvas.place(x=0, y=0)
global entry1, entry2, entry3
# 创建输入框
label1 = tk.Label(canvas, text="函数名:")
label1.place(x=650, y=50)
entry1 = tk.Entry(canvas)
entry1.place(x=650, y=100, width=300, height=40)
label2 = tk.Label(canvas, text="函数式:")
label2.place(x=650, y=150)
entry2 = tk.Entry(canvas)
entry2.place(x=650, y=200, width=300, height=40)
label3 = tk.Label(canvas, text="测试函数式:")
label3.place(x=650, y=250)
entry3 = tk.Entry(canvas)
entry3.place(x=650, y=300, width=300, height=40)
button1 = tk.Button(canvas, text="生成函数", command=add_func)
button1.place(x=650, y=400)
button2 = tk.Button(canvas, text="函数曲线", command=customized_curve)
button2.place(x=750, y=400)
button3 = tk.Button(canvas, text="函数确认", command=func_save)
button3.place(x=850, y=400)
# subwindow.mainloop()
def main():
global Q_root, F_root,root_window
11 months ago
jis()
# gl_data.LOW, gl_data.HIGH = int(gl_data.LOW*screen_size), int(gl_data.HIGH*screen_size)
gl_data.X = []
gl_data.X = []
window()
# 函数选择相关界面
11 months ago
F_root = tk.Canvas(root_window, width=int(310*screen_size), height=int(290*screen_size))
F_root.place(x=int(560*screen_size), y=int(60*screen_size))
# 象限选择相关界面
11 months ago
Q_root = tk.Canvas(root_window, width=int(400*screen_size), height=int(50*screen_size))
Q_root.place(x=int(20*screen_size), y=int(410*screen_size))
buttons()
f_button()
q_button()
show_fit()
11 months ago
tk.Button(root_window, image=list_image[11], command=show_subwindow, relief=tk.FLAT, highlightthickness=0).place(x=int(640*screen_size), y=int(350)*screen_size)
# 设置Grid布局的列和行权重
root_window.protocol("WM_DELETE_WINDOW", close_window)
root_window.mainloop()
11 months ago
if __name__ == '__main__':
main()
pass