diff --git a/X1.py b/X1.py index 2dfd339..fe28256 100644 --- a/X1.py +++ b/X1.py @@ -23,6 +23,7 @@ def compute_curveData(num, step, coefficient): # coefficient代表二次函数 y_values = quadratic_function(x_values) # 调用quadratic_function(x)函数得到y值 curve_data = np.column_stack((x_values, y_values)) # 将两个一维数组堆叠成二维数组,形成(x,y)的形式 return curve_data + if __name__ == '__main__': coefficient = [1, 2, 3] ans = compute_curveData(1000, 250, coefficient) diff --git a/X5.py b/X5.py index f9d96ae..36b47fc 100644 --- a/X5.py +++ b/X5.py @@ -3,7 +3,7 @@ # Author : lirunsheng # User : l'r's # Software: PyCharm -# File : X4.py +# File : X5.py # -*- encoding: utf-8 -*- """ @@ -12,88 +12,192 @@ @DateTime: 2023/5/30 15:29 @SoftWare: PyCharm """ +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 +from tkinter import ttk from tkinter import * +from screeninfo import get_monitors +import os +from PIL import ImageTk, Image import tkinter.filedialog # 注意次数要将文件对话框导入 # from demo1 import Addfunc +from newly_added.formula import * +from scipy.optimize import leastsq global Q_root,F_root global root_window global label1,label2,label3 -class Addfunc(): - def __init__(self): - self.r = 7.5 # 设置按钮大小 - self.rr = 2.5# 设置按钮大小 - self.window = tk.Tk() # 新建函数增加窗口 - self.window.title("新增函数曲线")#设置窗口标题 - self.window.geometry('400x300')#设置窗口大小 - self.show()#显示按钮 - self.window.mainloop()#运行UI界面 - - def show(self): - for widget in self.window.winfo_children(): - widget.destroy()#清空已有组件 - self.add_cv = tk.Canvas(self.window, width=400, height=300) # 新建画布 - self.add_cv.place(x=0, y=0) # 放置画布 - cur = 0#记录已有按钮个数 - for f in function.Fun:#遍历已有函数 - self.add_cv.create_oval(20 - self.r, 20 + 15 + cur * 30 - self.r, 20 + self.r, 20 + 15 + cur * 30 + self.r - , fill="white", width=1, outline="black")# 绘制圆圈 - if f.show == 1:#若为当前选中函数,则标记 - self.add_cv.create_oval(20 - self.rr, 20 + 15 + cur * 30 - self.rr, 20 + self.rr, 20 + 15 + cur * 30 + self.rr - , fill="black", width=1, outline="black") - button = tk.Button(self.window, text=f.name + f.demo, bd=0, font=("微软雅黑", 12) - , command=lambda x=f.no: self.change(x), anchor=W)#设置选择按钮 - button.place(x=40, y=20 + cur * 30, width=300, height=30)#放置选择按钮 - cur += 1#计数加一 - - def change(self, no):#切换函数显示情况 - function.Show[no] = not function.Show[no] - function.Fun[no].show = not function.Fun[no].show - self.show()#更新函数选择面板 - f_button()#更新函数显示界面 -# class AddFunc(Addfunc): -# def change(self, no): -# # super().change(no) -# function.Show[no] = not function.Show[no] -# function.Fun[no].show = not function.Fun[no].show -# self.show() # 更新函数选择面板 -# f_button() + +# 创建弹出窗口 +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(): - Addfunc() - # AddFunc() + 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('函数拟合') - root_window.geometry('900x600') # 设置窗口大小:宽x高,注,此处不能为 "*",必须使用 "x" + width, height = int(900*screen_size), int(560*screen_size) + root_window.geometry(str(width)+'x'+str(height)) # 设置窗口大小:宽x高,注,此处不能为 "*",必须使用 "x" # 设置主窗口的背景颜色,颜色值可以是英文单词,或者颜色值的16进制数,除此之外还可以使用Tk内置的颜色常量 - root_window["background"] = "white" + 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) # 防止用户调整尺寸 - label1 = tk.Label(root_window, text="样本数据\n集文件", font=('Times', 8), bg="white", - width=13, height=3, # 设置标签内容区大小 - padx=0, pady=0, borderwidth=0, ) - label1.place(x=10, y=4) # 设置填充区距离、边框宽度和其样式(凹陷式) - label3 = tk.Label(root_window, text="", font=('Times', 8), bg="white", fg="black", - width=39, height=2, padx=0, pady=0, borderwidth=0, relief="ridge", highlightcolor="blue") - label3.place(x=122, y=10) + 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)) # 使用按钮控件调用函数 - tk.Button(root_window, text="装载", relief=RAISED, command=lambda: askfile()).place(x=370, y=12) - label2 = tk.Label(root_window, text="拟合曲线类型", font=('Times', 12), bg="white", - width=20, height=3, # 设置标签内容区大小 - padx=0, pady=0, borderwidth=0, ) - # 设置填充区距离、边框宽度和其样式(凹陷式) - label2.place(x=450, y=4) - gl_data.Canvas2 = tk.Canvas(root_window, bg='white', width=450, height=330) - gl_data.Canvas2.place(x=4, y=60) + 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(): # 从本地选择一个文件,并返回文件的路径 @@ -125,51 +229,56 @@ def read_sample_data(file_path): def buttons(): # 随机生成样本点并显示 - b_dot = tk.Button(root_window, text="生成\n数据集", relief=RAISED, bd=4, bg="white", font=("微软雅黑", 10), - command=lambda: generate_and_plot_sample_data(gl_data.LOW, gl_data.HIGH)) - b_dot.place(x=455, y=410) + 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)) # 显示当前样本点 - b_show = tk.Button(root_window, text="显示\n数据集", relief=RAISED, bd=4, bg="white", font=("微软雅黑", 10), - command=lambda: selfdata_show(gl_data.X, gl_data.Y, gl_data.LOW, gl_data.HIGH)) - b_show.place(x=510, y=410) + 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)) # 显示数据集与曲线 - b_line = tk.Button(root_window, text="显示数据\n集与曲线", relief=RAISED, bd=4, bg="white", font=("微软雅黑", 10), - 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=565, y=410) + 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)) # 手动输入数据集 - b_input = tk.Button(root_window, text="手动输入数据集", relief=RAISED, bd=4, bg="white", pady=7, font=("微软雅黑", 13), - command=lambda: input.input_num(root_window)) - b_input.place(x=633, y=410) + 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)) # 拟合并输出拟合结果 - b_fit = tk.Button(root_window, text="拟合", relief=RAISED, bd=4, bg="white", pady=7, font=("微软雅黑", 13), - command=lambda: fit_data(gl_data.Xian_index, gl_data.X, gl_data.Y)) - b_fit.place(x=771, y=410) + 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(): - L = tk.Label(root_window, text='结果输出:', bg='white', font=("微软雅黑", 16) - , anchor=W) - L.place(x=20, y=480, width=100, height=30) sout = str(gl_data.Out) ans = tk.Label(root_window, text=sout, font=("微软雅黑", 14) , anchor=W, justify='left') - ans.place(x=120, y=480, width=760, height=100) + ans.place(x=int(120*screen_size), y=int(480*screen_size), width=int(760*screen_size), height=100) print(sout) + def fit_data(xian_index, sx, sy): - # gl_data.yvals_pow = [] - cur = function.Fun[xian_index] # 装载正在选择的函数 - func = cur.get_fun()#获取当前函数func - popt, pcov = curve_fit(func, sx, sy)# 用curve_fit来对点进行拟合 + 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值,用于表示拟合优度 # 输出拟合后的各个参数值 - ans = '\n函数系数:' - for i in range(cur.variable): - if i == 4: - ans += '\n' - if i != 0: - ans += ', ' - ans += chr(ord('a') + i) + '=' + '{:.2e}'.format(popt[i]) # str(round(gl_data.popt[i], 3)) - gl_data.Out = '函数形式:' + cur.name + ' ' - gl_data.Out += cur.demo + 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()# 显示函数信息到输出框 @@ -187,79 +296,253 @@ def q_button(): rr = 2.5 for widget in Q_root.winfo_children(): widget.destroy() - q_cv = tk.Canvas(Q_root, width=400, height=50) + q_cv = tk.Canvas(Q_root, width=int(400*screen_size), height=int(50*screen_size)) q_cv.place(x=0, y=0) - l = tk.Label(Q_root, text='坐标轴', bd=0, font=("微软雅黑", 16) - , anchor=W) - l.place(x=20, y=0, width=80, height=50,) + # 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) - b1.place(x=170, y=0, width=80, height=50,) + 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) - b2.place(x=320, y=0, width=80, height=50,) - # 绘制标记框 - q_cv.create_oval(140 - r, 25 - r, 140 + r, 25 + r - , fill="white", width=1, outline="black") - q_cv.create_oval(290 - r, 25 - r, 290 + r, 25 + r - , fill="white", width=1, outline="black") + 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: - q_cv.create_oval(140 - rr, 25 - rr, 140 + rr, 25 + rr - , fill="black", width=1, outline="black") + q_cv.create_image(int((140 - rr)*screen_size), int((30 - rr)*screen_size), + image=list_image[18]) else: - q_cv.create_oval(290 - rr, 25 - rr, 290 + rr, 25 + rr - , fill="black", width=1, outline="black") + 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(): - r = 7.5#设置按钮大小 - rr = 2.5#设置按钮大小 + r = 7.5 # 设置按钮大小 + rr = 2.5 # 设置按钮大小 for widget in F_root.winfo_children(): - widget.destroy()#清空原有按钮 - f_cv = tk.Canvas(F_root, width=400, height=330)#新建画布 - f_cv.place(x=0, y=0)#放置画布 - f_cv.create_rectangle(2, 2, 398, 328, fill='white', outline="#0099ff")#设置画布边框及底色 - cur = 0#函数计数 - for fun in function.Fit_type_library:#遍历函数库 - f = function.Fit_type_library.get(fun)#获取函数具体信息 - if function.Show[f[0]] == 1:# 如果show为1则显示 - f_cv.create_oval(20 - r, 20 + 15 + cur * 30 - r, 20 + r, 20 + 15 + cur * 30 + r - , fill="white", width=1, outline="black")# 绘制标记框 - if f[0] == gl_data.Xian_index:# 若选择为当前函数则标记 - f_cv.create_oval(20 - rr, 20 + 15 + cur * 30 - rr, 20 + rr, 20 + 15 + cur * 30 + rr - , fill="black", width=1, outline="black") - # 绘制切换按钮,单击则将当前使用函数切换为该函数 - button = tk.Button(F_root, text=f[2] + ' ' + f[1], bd=0, bg="white", font=("微软雅黑", 12) - , command=lambda x=f[0]: change_f(x), anchor=W) - button.place(x=40, y=20 + cur * 30, width=300, height=30) - cur += 1#计数加一 + 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('', 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() + +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 + 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() # 函数选择相关界面 - F_root = tk.Frame(root_window, width=400, height=330, bg='white', ) - F_root.place(x=490, y=60) + 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)) + # 象限选择相关界面 - Q_root = tk.Frame(root_window, width=400, height=50, bg='white', ) - Q_root.place(x=20, y=410) - function.f_read() + 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() - tk.Button(root_window, text="新增拟合曲线类型", bd=4, command=addfunc).place(x=740, y=12) + 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() + if __name__ == '__main__': main() pass diff --git a/background/+.png b/background/+.png new file mode 100644 index 0000000..9293f8c Binary files /dev/null and b/background/+.png differ diff --git a/background/add.png b/background/add.png new file mode 100644 index 0000000..9293f8c Binary files /dev/null and b/background/add.png differ diff --git a/background/img.png b/background/img.png new file mode 100644 index 0000000..a5d79f5 Binary files /dev/null and b/background/img.png differ diff --git a/background/下方输入框.png b/background/下方输入框.png new file mode 100644 index 0000000..7a219fa Binary files /dev/null and b/background/下方输入框.png differ diff --git a/background/右侧未选中.png b/background/右侧未选中.png new file mode 100644 index 0000000..da25b12 Binary files /dev/null and b/background/右侧未选中.png differ diff --git a/background/右侧背景.png b/background/右侧背景.png new file mode 100644 index 0000000..d4ff27d Binary files /dev/null and b/background/右侧背景.png differ diff --git a/background/右侧选中.png b/background/右侧选中.png new file mode 100644 index 0000000..d5035f9 Binary files /dev/null and b/background/右侧选中.png differ diff --git a/background/大背景.png b/background/大背景.png new file mode 100644 index 0000000..d7ec27d Binary files /dev/null and b/background/大背景.png differ diff --git a/background/导航拦.png b/background/导航拦.png new file mode 100644 index 0000000..3158200 Binary files /dev/null and b/background/导航拦.png differ diff --git a/background/导航输入框.png b/background/导航输入框.png new file mode 100644 index 0000000..9051c67 Binary files /dev/null and b/background/导航输入框.png differ diff --git a/background/小直线.png b/background/小直线.png new file mode 100644 index 0000000..e1cc1df Binary files /dev/null and b/background/小直线.png differ diff --git a/background/手动输入数据集.png b/background/手动输入数据集.png new file mode 100644 index 0000000..177e1c9 Binary files /dev/null and b/background/手动输入数据集.png differ diff --git a/background/拟合.png b/background/拟合.png new file mode 100644 index 0000000..6e71af4 Binary files /dev/null and b/background/拟合.png differ diff --git a/background/新增拟合曲线类型.png b/background/新增拟合曲线类型.png new file mode 100644 index 0000000..2d118ec Binary files /dev/null and b/background/新增拟合曲线类型.png differ diff --git a/background/显示数据集.png b/background/显示数据集.png new file mode 100644 index 0000000..f778aad Binary files /dev/null and b/background/显示数据集.png differ diff --git a/background/显示数据集与曲线.png b/background/显示数据集与曲线.png new file mode 100644 index 0000000..881c7d4 Binary files /dev/null and b/background/显示数据集与曲线.png differ diff --git a/background/未选中.png b/background/未选中.png new file mode 100644 index 0000000..a1355cf Binary files /dev/null and b/background/未选中.png differ diff --git a/background/生成数据集.png b/background/生成数据集.png new file mode 100644 index 0000000..c5454aa Binary files /dev/null and b/background/生成数据集.png differ diff --git a/background/矩形.png b/background/矩形.png new file mode 100644 index 0000000..2907e8e Binary files /dev/null and b/background/矩形.png differ diff --git a/background/背景图片.png b/background/背景图片.png new file mode 100644 index 0000000..a12bebe Binary files /dev/null and b/background/背景图片.png differ diff --git a/background/装载.png b/background/装载.png new file mode 100644 index 0000000..7c605f4 Binary files /dev/null and b/background/装载.png differ diff --git a/background/选中.png b/background/选中.png new file mode 100644 index 0000000..abd6f42 Binary files /dev/null and b/background/选中.png differ diff --git a/data.py b/data.py index 183df80..f09a227 100644 --- a/data.py +++ b/data.py @@ -9,7 +9,8 @@ import tkinter as tk from tkinter import * import random import numpy as np - +FITT_SAVE = {} +FITT_LIST = [] X = [] # 等待拟合的x值 Y = [] # 等待拟合的y值 global Canvas2 # 用于显示函数的画布 @@ -18,10 +19,11 @@ Img2 = None # 绘制的line.png图像 Xian_index = 0 # 当前选择的函数编号 Quadrant = 0 # 当前选择的象限信息0为四象限,1为一象限 MAXV = 1000 # 最大值 +INDEX = 5 # 拟合函数索引 Out = '' # 拟合输出信息 LOW = -MAXV # 坐标轴显示上界 HIGH = MAXV # 坐标轴显示下界 - +MAGNIDICATION = 1.0 # 放大倍数 def random_points(): x=[] diff --git a/demo.py b/demo.py new file mode 100644 index 0000000..ba0db63 --- /dev/null +++ b/demo.py @@ -0,0 +1,10 @@ +def func_save(): + ···与add_func相似 + else: + save(result1, letters, result, result2) + subwindow.destroy() # 关闭子页面 + gl_data.Xian_index = gl_data.INDEX + f_button() + tk.Button(root_window, text="新增拟合曲线类型", + bd=4, command=show_subwindow).place( + x=int(740*screen_size), y=int(12)*screen_size) \ No newline at end of file diff --git a/dot.png b/dot.png index 9855778..a46b61e 100644 Binary files a/dot.png and b/dot.png differ diff --git a/fitting.py b/fitting.py index 7011ae8..d5578b8 100644 --- a/fitting.py +++ b/fitting.py @@ -13,7 +13,7 @@ import pylab as mpl from tkinter import * import mpl_toolkits.axisartist as axisartist import data as gl_data -import function +# import function mpl.rcParams['font.sans-serif'] = ['SimHei'] # 解决中文不显示问题 plt.rcParams['axes.unicode_minus'] = False #解决负数坐标显示问题 @@ -54,12 +54,30 @@ def selfdata_show(x, y, low, high):# 显示数据集 plt.clf() set_phtot(1) +def fitting(letters,result): + code_str = ''' +def func({}): + return {} + '''.format(letters,result) + return code_str + +def create_func(code_str): + # 创建一个空的命名空间 + namespace = {} + # 使用exec函数执行字符串代码,并指定命名空间为locals + exec(code_str, globals(), namespace) + # 返回命名空间中的函数对象 + return namespace['func'] + def draw_line(xian_index, low, high, sx, sy): draw_axis(low, high)#绘制坐标轴 popt = []#初始化curve_fit pcov = []#初始化curve_fit - cur = function.Fun[xian_index] # 装载正在选择的函数 - func = cur.get_fun()#获取当前函数func + 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 + # cur = function.Fun[xian_index] # 装载正在选择的函数 + # func = cur.get_fun()#获取当前函数func popt, pcov = curve_fit(func, sx, sy)# 用curve_fit来对点进行拟合 curve_x = np.arange(low, high)#按照步长生成的一串数字 curve_y = [func(i, *popt) for i in curve_x]# 根据x0(按照步长生成的一串数字)来计算y1值 @@ -71,20 +89,37 @@ def draw_line(xian_index, low, high, sx, sy): set_phtot(2)#将图片显示到程序中 def draw_dots_and_line(xian_index, low, high, sx, sy): + 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) + print(sx) ############## begin ##################### draw_axis(low, high) popt = [] pcov = [] - cur = function.Fun[xian_index] # 装载正在选择的函数 - func = cur.get_fun()#获取当前函数func + 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 + print(code_str) + # cur = function.Fun[xian_index] # 装载正在选择的函数 + # func = cur.get_fun()#获取当前函数func popt, pcov = curve_fit(func, sx, sy)# 用curve_fit来对点进行拟合 positive_mask = sy >= 0.0#给样本点分类 negative_mask = sy < 0.0#给样本点分类 - positive_colors = ['red' if x >= 0.0 else 'blue' for x in sx]#给样本点分类 - negative_colors = ['green' if x >= 0.0 else 'purple' for x in sx]#给样本点分类 + positive_colors = ['red' if x >= 0.0 else 'red' for x in sx]#给样本点分类 + negative_colors = ['red' if x >= 0.0 else 'red' for x in sx]#给样本点分类 #根据样本点类型决定样本点颜色 - plt.scatter(gl_data.X[positive_mask], gl_data.Y[positive_mask], color=np.array(positive_colors)[positive_mask], lw=1) - plt.scatter(gl_data.X[negative_mask], gl_data.Y[negative_mask], color=np.array(negative_colors)[negative_mask], lw=1) + plt.scatter(sx[positive_mask], sy[positive_mask], color=np.array(positive_colors)[positive_mask], lw=1) + plt.scatter(sx[negative_mask], sy[negative_mask], color=np.array(negative_colors)[negative_mask], lw=1) curve_x = np.arange(low, high)#按照步长生成的一串数字 curve_y = [func(i, *popt) for i in curve_x]# 根据x0(按照步长生成的一串数字)来计算y1值 ############## end ####################### @@ -102,13 +137,17 @@ def set_phtot(index1): if index1 == 1: gl_data.Img1=PhotoImage(file=r"dot.png") # print(gl_data.Img1) + gl_data.Img1 = gl_data.Img1.zoom(2, 2) set_img1=Canvas2.create_image(2, 10, image=gl_data.Img1, anchor=NW) + # Canvas2.itemconfigure(set_img1,int(420 * gl_data.MAGNIDICATION), int(300 * gl_data.MAGNIDICATION)) Canvas2.update() print("已输出数据点") elif index1 == 2: gl_data.Img2=PhotoImage(file=r"line.png") # print(gl_data.Img2) + gl_data.Img2 = gl_data.Img2.zoom(2, 2) set_img2=Canvas2.create_image(2, 10, image=gl_data.Img2, anchor=NW) + # Canvas2.itemconfigure(set_img2, 0,0,int(420*gl_data.MAGNIDICATION), int(300*gl_data.MAGNIDICATION)) Canvas2.update() print("已输出数据点和曲线") if __name__ == '__main__': diff --git a/functions.xlsx b/functions.xlsx index 7c656f3..f157103 100644 Binary files a/functions.xlsx and b/functions.xlsx differ diff --git a/functions_new.xlsx b/functions_new.xlsx new file mode 100644 index 0000000..38298f2 Binary files /dev/null and b/functions_new.xlsx differ diff --git a/functions_new1.xlsx b/functions_new1.xlsx new file mode 100644 index 0000000..51d3557 Binary files /dev/null and b/functions_new1.xlsx differ diff --git a/input.py b/input.py index c29a72c..bf65c74 100644 --- a/input.py +++ b/input.py @@ -6,11 +6,12 @@ @SoftWare: PyCharm """ import tkinter as tk - +from tkinter import * import numpy as np import data as gl_data -from tkinter import * +from fitting import selfdata_show + global top def input_num(root_tk): global top @@ -31,6 +32,7 @@ def input_data(root_tk, num): sample_data = [] top.destroy() top = tk.Toplevel(root_tk) + def add_sample_data(): try: x = float(entry_x.get()) @@ -40,7 +42,7 @@ def input_data(root_tk, num): return entry_x.delete(0, tk.END) entry_y.delete(0, tk.END) - if min(x, y) < gl_data.low or max(x, y) > gl_data.high: + if min(x, y) < gl_data.LOW or max(x, y) > gl_data.HIGH: label_status.config(text="输入超过范围") return elif len(sample_data) < num: @@ -53,10 +55,11 @@ def input_data(root_tk, num): def check_sample_data(): if len(sample_data) == num: label_status.config(text="已达到最大数量") - gl_data.x = np.array(sample_x) - gl_data.y = np.array(sample_y) + gl_data.X = np.array(sample_x) + gl_data.Y = np.array(sample_y) print('已添加', sample_data) top.destroy() + selfdata_show(gl_data.X, gl_data.Y, gl_data.LOW, gl_data.HIGH) else: label_status.config(text="还需输入{}个点对".format(num - len(sample_data))) print(sample_data) @@ -77,7 +80,6 @@ def input_data(root_tk, num): label_status.pack() top.mainloop() - if __name__ == '__main__': root = tk.Tk() input_num(root) diff --git a/line.png b/line.png index bd6ae22..510d1c0 100644 Binary files a/line.png and b/line.png differ diff --git a/new_line.png b/new_line.png new file mode 100644 index 0000000..574b872 Binary files /dev/null and b/new_line.png differ diff --git a/new_x5.py b/new_x5.py new file mode 100644 index 0000000..adbba29 --- /dev/null +++ b/new_x5.py @@ -0,0 +1,23 @@ +import tkinter as tk +from screeninfo import get_monitors + +# 创建Tkinter窗口 +window = tk.Tk() +window.title("设置按钮尺寸为当前屏幕的百分之85") +window.geometry('190x160') +def jis(): + # 获取当前屏幕的宽度和高度 + monitors = get_monitors() + screen_width = monitors[0].width + screen_height = monitors[0].height + + # 计算按钮的宽度和高度 + button_width = int(screen_width * 0.85) + button_height = int(screen_height * 0.85) + # 打印按钮的宽度和高度 + print(f"按钮的尺寸:{button_width}x{button_height}") +# 创建一个按钮,并设置尺寸 +button = tk.Button(window, text="按钮", command=jis) +button.pack() +# 运行Tkinter事件循环 +window.mainloop() \ No newline at end of file diff --git a/newly_added/X5.py b/newly_added/X5.py new file mode 100644 index 0000000..49f9ff2 --- /dev/null +++ b/newly_added/X5.py @@ -0,0 +1,364 @@ +# -*- coding: utf-8 -*- +# Time : 2023/8/9 10:20 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : X5.py + +# -*- encoding: utf-8 -*- +""" + @Author: packy945 + @FileName: main.py + @DateTime: 2023/5/30 15:29 + @SoftWare: PyCharm +""" +import re +from tkinter import messagebox + +from PIL import ImageTk, Image +from pandas import DataFrame +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 +from tkinter import * +import tkinter.filedialog # 注意次数要将文件对话框导入 +# from demo1 import Addfunc +from newly_added.formula import expression_output + +global Q_root,F_root +global root_window +global label1,label2,label3 +from screeninfo import get_monitors +import os + +# 创建弹出窗口 +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'#设置路径 + df = pd.DataFrame(gl_data.FITT_SAVE) # 将字典转换为DataFrame + df.to_excel(file_path, index=False) # 将DataFrame保存为Excel文件 + +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(): + 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 element(path): + # print(path) + # 加载图像并创建一个 PhotoImage 对象 + image = tk.PhotoImage(file=path) + # 保存图片的引用,防止被垃圾回收 + root_window.image = image + return image + +def window(): + global root_window + global label1, label2, label3 + root_window = Tk() + root_window.title('函数拟合') + width, height= int(900*screen_size),int(560*screen_size) + root_window.geometry(str(width)+'x'+str(height)) # 设置窗口大小:宽x高,注,此处不能为 "*",必须使用 "x" + # 设置主窗口的背景颜色,颜色值可以是英文单词,或者颜色值的16进制数,除此之外还可以使用Tk内置的颜色常量 + 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" + ] + global list_image + list_image = [] + for path in img_path: + list_image.append(element(path)) + root_window["background"] = "white" + root_window.resizable(0, 0) # 防止用户调整尺寸 + label1 = tk.Label(root_window, text="样本数据\n集文件", font=('Times', 8), bg="white", + width=13, height=3, # 设置标签内容区大小 + padx=0, pady=0, borderwidth=0, ) + label1.place(x=int(10*screen_size), y=int(4*screen_size)) # 设置填充区距离、边框宽度和其样式(凹陷式) + label3 = tk.Label(root_window, text="", font=('Times', 8), bg="white", fg="black", + width=39, height=2, padx=0, pady=0, borderwidth=0, relief="ridge", highlightcolor="blue") + label3.place(x=int(122*screen_size), y=int(10*screen_size)) + # 使用按钮控件调用函数 + tk.Button(root_window, image=list_image[17], relief=RAISED, command=lambda: askfile()).place(x=int(370*screen_size), y=int(12*screen_size)) + label2 = tk.Label(root_window, text="拟合曲线类型", font=('Times', 12), bg="white", + width=20, height=3, # 设置标签内容区大小 + padx=0, pady=0, borderwidth=0, ) + # 设置填充区距离、边框宽度和其样式(凹陷式) + label2.place(x=int(450*screen_size), y=int(4*screen_size)) + gl_data.Canvas2 = tk.Canvas(root_window, bg='white', width=int(450*screen_size), height=int(330*screen_size)) + gl_data.Canvas2.place(x=int(4*screen_size), y=int(60*screen_size)) +# 定义一个处理文件的相关函数 +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(' ')#以空格分割x,y + print(f'sx: {float(sx)}, sy: {float(sy)}')#将sx、sy转换为浮点数并打印 + +def read_sample_data(file_path): + 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转换为浮点数并加入数组 + gl_data.X, gl_data.Y = np.array(x), np.array(y)#x,y数组转为array并赋值给全局变量 + +def buttons(): + # 随机生成样本点并显示 + b_dot = tk.Button(root_window, text="生成\n数据集", relief=RAISED, bd=4, bg="white", font=("微软雅黑", 10), + command=lambda: generate_and_plot_sample_data(gl_data.LOW, gl_data.HIGH)) + b_dot.place(x=int(455*screen_size), y=int(410*screen_size)) + # 显示当前样本点 + b_show = tk.Button(root_window, text="显示\n数据集", relief=RAISED, bd=4, bg="white", font=("微软雅黑", 10), + command=lambda: selfdata_show(gl_data.X, gl_data.Y, gl_data.LOW, gl_data.HIGH)) + b_show.place(x=int(510*screen_size), y=int(410*screen_size)) + # 显示数据集与曲线 + b_line = tk.Button(root_window, text="显示数据\n集与曲线", relief=RAISED, bd=4, bg="white", font=("微软雅黑", 10), + 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(565*screen_size), y=int(410*screen_size)) + # 手动输入数据集 + b_input = tk.Button(root_window, text="手动输入数据集", relief=RAISED, bd=4, bg="white", pady=7, font=("微软雅黑", 13), + command=lambda: input.input_num(root_window)) + b_input.place(x=int(633*screen_size), y=int(410*screen_size)) + # 拟合并输出拟合结果 + b_fit = tk.Button(root_window, text="拟合", relief=RAISED, bd=4, bg="white", pady=7, font=("微软雅黑", 13), + command=lambda: fit_data(gl_data.Xian_index, gl_data.X, gl_data.Y)) + b_fit.place(x=int(771*screen_size), y=int(410*screen_size)) +def show_fit(): + L = tk.Label(root_window, text='结果输出:', bg='white', font=("微软雅黑", 16) + , anchor=W) + L.place(x=20, y=int(480*screen_size), width=int(100*screen_size), height=int(30*screen_size)) + sout = str(gl_data.Out) + ans = tk.Label(root_window, text=sout, font=("微软雅黑", 14) + , anchor=W, justify='left') + ans.place(x=int(120*screen_size), y=int(480*screen_size), width=int(760*screen_size), height=100) + print(sout) +def fit_data(xian_index, sx, sy): + 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值,用于表示拟合优度 + # 输出拟合后的各个参数值 + 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() + q_cv = tk.Canvas(Q_root, width=int(400*screen_size), height=int(50*screen_size)) + q_cv.place(x=0, y=0) + 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) + 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) + b2.place(x=int(320*screen_size), y=0, width=int(80*screen_size), height=int(50*screen_size),) + # 绘制标记框 + q_cv.create_oval(int((140 - r)*screen_size), int((25 - r)*screen_size), int((140 + r)*screen_size), int((25 + r)*screen_size) + , fill="white", width=1, outline="black") + q_cv.create_oval(int((290 - r)*screen_size), int((25 - r)*screen_size), int((290 + r)*screen_size), int((25 + r)*screen_size) + , fill="white", width=1, outline="black") + # 根据当前的象限选择值来填充标记框 + if gl_data.Quadrant == 0: + q_cv.create_oval(int((140 - rr)*screen_size), int((25 - rr)*screen_size), int((140 + rr)*screen_size), int((25 + rr)*screen_size) + , fill="black", width=1, outline="black") + else: + q_cv.create_oval(int((290 - rr)*screen_size), int((25 - rr)*screen_size), int((290 + rr)*screen_size), int((25 + rr)*screen_size) + , fill="black", width=1, outline="black") +def change_f(no): + gl_data.Xian_index = no#设置全局函数编号为该函数 + f_button()#重新绘制函数显示界面 + +def f_button(): + r = 7.5 # 设置按钮大小 + rr = 2.5 # 设置按钮大小 + + for widget in F_root.winfo_children(): + widget.destroy() # 清空原有按钮 + gl_data.INDEX = len(gl_data.FITT_SAVE['no']) + # 创建滚动条 + scrollbar = tk.Scrollbar(F_root) + scrollbar.pack(side=tk.RIGHT, fill=tk.Y) + # 创建Canvas对象 + f_cv = tk.Canvas(F_root, width=int(370 * screen_size), height=int(310 * screen_size), yscrollcommand=scrollbar.set) + f_cv.pack(side=tk.LEFT) + # 设置滚动条与Canvas的关联 + scrollbar.config(command=f_cv.yview) + # # 绘制矩形边框及底色 + # f_cv.create_rectangle(2, 2, int(398 * screen_size), int(328 * screen_size), fill='white', + # outline="#0099ff") + cur = 0 # 函数计数 + for i in range(gl_data.INDEX): # 遍历函数库 + f_cv.create_oval(int((20 - r) * screen_size), int((20 + 15 + cur * 30 - r) * screen_size), + int((20 + r) * screen_size), int((20 + 15 + cur * 30 + r) * screen_size) + , fill="white", width=1, outline="black") # 绘制标记框 + if gl_data.FITT_SAVE['no'][i] == gl_data.Xian_index: # 若选择为当前函数则标记 + f_cv.create_oval(int((20 - rr) * screen_size), int((20 + 15 + cur * 30 - rr) * screen_size), + int((20 + rr) * screen_size), int((20 + 15 + cur * 30 + rr) * screen_size) + , fill="black", width=int(screen_size), outline="black") + # 绘制切换按钮,单击则将当前使用函数切换为该函数 + button = tk.Button(F_root, text=gl_data.FITT_SAVE['name'][i] + ' ' + gl_data.FITT_SAVE['demo'][i], bd=0, + bg="white", font=("微软雅黑", 12), + command=lambda x=gl_data.FITT_SAVE['no'][i]: change_f(x), anchor=W) + + # 将按钮放置在Canvas中 + f_cv.create_window(int(40 * screen_size), int((35 + cur * 30) * screen_size), window=button, anchor="w") + cur += 1 # 计数加一 + + f_cv.configure(scrollregion=f_cv.bbox("all")) + +def close_window(): + sys.exit() + + +def main(): + global Q_root, F_root,root_window + 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() + # 函数选择相关界面 + F_root = tk.Frame(root_window, width=int(400*screen_size), height=int(330*screen_size), bg='white', ) + F_root.place(x=int(490*screen_size), y=int(60*screen_size)) + # 象限选择相关界面 + Q_root = tk.Frame(root_window, width=int(400*screen_size), height=int(50*screen_size), bg='white', ) + Q_root.place(x=int(20*screen_size), y=int(410*screen_size)) + buttons() + f_button() + q_button() + show_fit() + tk.Button(root_window, text="新增拟合曲线类型", bd=4, command=addfunc).place(x=int(740*screen_size), y=int(12)*screen_size) + # 设置Grid布局的列和行权重 + root_window.protocol("WM_DELETE_WINDOW", close_window) + root_window.mainloop() + +if __name__ == '__main__': + main() + pass diff --git a/newly_added/__init__.py b/newly_added/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/newly_added/__pycache__/__init__.cpython-38.pyc b/newly_added/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..7d72ad6 Binary files /dev/null and b/newly_added/__pycache__/__init__.cpython-38.pyc differ diff --git a/newly_added/__pycache__/formula.cpython-38.pyc b/newly_added/__pycache__/formula.cpython-38.pyc new file mode 100644 index 0000000..6d5db72 Binary files /dev/null and b/newly_added/__pycache__/formula.cpython-38.pyc differ diff --git a/newly_added/demo.py b/newly_added/demo.py new file mode 100644 index 0000000..8669a44 --- /dev/null +++ b/newly_added/demo.py @@ -0,0 +1,70 @@ +# import re +# +# def add_spaces(expression): +# # 匹配运算符的正则表达式 +# pattern = r'([+\-*/])' +# # 替换匹配到的运算符 +# new_expression = re.sub(pattern, r' \1 ', expression) +# return new_expression +# +# # 示例用法 +# expression = 'a+b^c-c*d/e' +# new_expression = add_spaces(expression) +# print(new_expression) + +import re +from formula import * + +def remove_inner_spaces(expression): + # 匹配括号内的空格的正则表达式 + pattern = r'\((.*?)\)' + # 替换匹配到的括号内的空格 + new_expression = re.sub(pattern, lambda match: '(' + match.group(1).replace(' ', '') + ')', expression) + return new_expression + +def add_spaces(expression): + # 匹配运算符的正则表达式 + pattern = r'([+\-*/])' + # 替换匹配到的运算符 + new_expression = re.sub(pattern, r' \1 ', expression) + return new_expression +# 示例用法 +expression = 'a+b^c-c*d/e+sin(x+y)+e^(0.2*x)' +expression = add_spaces(expression) +new_expression = remove_inner_spaces(expression) +list_expression = new_expression.split(' ') +conforms = ['e^', 'log', 'sin', 'cos', 'tan', '^'] +new_list = [] +# result2 = ' '.join([word for word in expression.split() if not any(word.startswith(s) for s in conforms)]) +# print(result2) +# letters = [char for char in result2 if char.isalpha()] +# print(letters) +for expres in list_expression: + new_list.append(expres) + for i in range(6): + if conforms[i] in expres: + index = list_expression.index(expres) + if i == 0: + list_expression[index] = convert_e(expres) + elif i==1: + list_expression[index] = convert_log(expres) + elif i==5: + list_expression[index] = convert_x(expres) + else: + list_expression[index] = convert_trigonometric(expres) + new_list[-1] = new_list[-1].replace(conforms[i], "") + break +new_list = [char for item in new_list for char in item if char.isalpha()] +parameter ='' +for item in new_list: + if item not in parameter: + parameter = parameter+item+',' +print(parameter[:-1]) +for item in new_list: + if item not in parameter: + parameter = parameter+item+',' +str_expression = '' +for item in list_expression: + str_expression = str_expression + item +print(str_expression[:-1]) +print(list_expression) diff --git a/newly_added/demo1.py b/newly_added/demo1.py new file mode 100644 index 0000000..2ed043c --- /dev/null +++ b/newly_added/demo1.py @@ -0,0 +1,44 @@ +import mpl_toolkits.axisartist as axisartist +from scipy.optimize import curve_fit +from pandas import DataFrame +import matplotlib.pyplot as plt +import numpy as np +import pylab as mpl +from tkinter import * +import data as gl_data +import pandas as pd +import tkinter +import sys +import tkinter as tk +from tkinter import filedialog + +mpl.rcParams['font.sans-serif'] = ['SimHei'] # 解决matplotlib中文不显示问题 +plt.rcParams['axes.unicode_minus'] = False # 解决matplotlib负数坐标显示问题 + +# 创建一个二维数组sampleData,用于存储样本数据 +sampleData = [] + +def askfile():# 从本地选择一个文件,并返回文件的路径 + global sampleData + # 弹出文件选择对话框,选择要打开的文本文件 + file_path = filedialog.askopenfilename(filetypes=[('Text Files', '*.txt')]) + + # 如果没有选择文件,直接返回 + if not file_path: + return + # 步骤1:打开样本数据集文件并读取每行数据 + with open(file_path, 'r') as file: + lines = file.readlines() + + # 步骤2:遍历所有行,将每行数据解析为sx和sy值,转换为浮点数并存储到sampleData中 + for line in lines: + sx, sy = line.strip().split(' ') + sx = float(sx) + sy = float(sy) + sampleData.append([sx, sy]) + + # # 步骤3:使用循环遍历样本数据并打印出来 + # for data in sampleData: + # print(data) +if __name__ == '__main__': + askfile() \ No newline at end of file diff --git a/newly_added/demo2.py b/newly_added/demo2.py new file mode 100644 index 0000000..d6173dc --- /dev/null +++ b/newly_added/demo2.py @@ -0,0 +1,19 @@ +import re + + +def find_letter_index(string): + pattern = r'([a-zA-Z])x\b' + matches = re.findall(pattern, string) + + letter_indices = [] + for match in matches: + letter_index = string.index(match) + letter_indices.append(letter_index) + + return letter_indices + + +# 示例使用 +string = "ax+b + cx - dx + e^x" +indices = find_letter_index(string) +print(indices) \ No newline at end of file diff --git a/newly_added/demo3.py b/newly_added/demo3.py new file mode 100644 index 0000000..5b8f29d --- /dev/null +++ b/newly_added/demo3.py @@ -0,0 +1,4 @@ +screen_size = 1.8 +r = 7.5 +rr = 2.5 +print(int((20 - rr) * screen_size)) \ No newline at end of file diff --git a/newly_added/demo5.py b/newly_added/demo5.py new file mode 100644 index 0000000..8f34249 --- /dev/null +++ b/newly_added/demo5.py @@ -0,0 +1,41 @@ +import numpy as np +from scipy.optimize import leastsq +import matplotlib.pyplot as plt + +# 定义待拟合的函数形式 +def func(x, coeffs): + return coeffs[0] * np.sin(coeffs[1] * x) + coeffs[2] + +# 定义目标函数,即待拟合数据 +def target_func(x): + return 2.5 * np.sin(0.7 * x) + 1.2 + +# 生成带噪声的待拟合数据 +np.random.seed(0) +x_data = np.linspace(-10, 10, 100) +y_data = target_func(x_data) + np.random.normal(0, 0.5, 100) + +# 定义误差函数,即最小二乘法的目标函数 +def residuals(coeffs, y, x): + return y - func(x, coeffs) + +# 初始参数的估计值 +initial_coeffs = [1, 1, 1] + +# 使用最小二乘法拟合函数 +result = leastsq(residuals, initial_coeffs, args=(y_data, x_data)) +best_coeffs = result[0] + +# 打印拟合得到的系数 +print("拟合得到的系数:", best_coeffs) + +# 绘制拟合结果 +x_fit = np.linspace(-10, 10, 100) +y_fit = func(x_fit, best_coeffs) + +plt.plot(x_data, y_data, 'bo', label='原始数据') +plt.plot(x_fit, y_fit, 'r-', label='拟合曲线') +plt.legend() +plt.xlabel('x') +plt.ylabel('y') +plt.show() \ No newline at end of file diff --git a/newly_added/demo6.py b/newly_added/demo6.py new file mode 100644 index 0000000..cd41a87 --- /dev/null +++ b/newly_added/demo6.py @@ -0,0 +1,51 @@ +import numpy as np +import matplotlib.pyplot as plt + +# 定义待拟合的函数形式 +def func(x, coeffs): + return coeffs[0] * x + coeffs[1] + +# 定义目标函数,即待拟合数据 +def target_func(x): + return 2.5 * x + 1.2 + +# 生成带噪声的待拟合数据 +np.random.seed(0) +x_data = np.linspace(0, 10, 100) +y_data = target_func(x_data) + np.random.normal(0, 0.5, 100) + +# 定义损失函数,即拟合函数与目标函数之间的差距 +def loss_func(coeffs, x, y): + return np.mean((y - func(x, coeffs))**2) + +# 定义梯度计算函数,即损失函数对于系数的偏导数 +def gradient_func(coeffs, x, y): + gradient = np.zeros_like(coeffs) + gradient[0] = -2 * np.mean((y - func(x, coeffs)) * x) + gradient[1] = -2 * np.mean(y - func(x, coeffs)) + return gradient + +# 初始化参数 +learning_rate = 0.01 +num_iterations = 1000 +initial_coeffs = np.array([1.0, 1.0], dtype=float) + +# 使用梯度下降法拟合函数 +coeffs = initial_coeffs +for i in range(num_iterations): + gradient = gradient_func(coeffs, x_data, y_data) + coeffs -= learning_rate * gradient + +# 打印拟合得到的系数 +print("拟合得到的系数:", coeffs) + +# 绘制拟合结果 +x_fit = np.linspace(0, 10, 100) +y_fit = func(x_fit, coeffs) + +plt.plot(x_data, y_data, 'bo', label='原始数据') +plt.plot(x_fit, y_fit, 'r-', label='拟合曲线') +plt.legend() +plt.xlabel('x') +plt.ylabel('y') +plt.show() \ No newline at end of file diff --git a/newly_added/demo7.py b/newly_added/demo7.py new file mode 100644 index 0000000..e78f1d2 --- /dev/null +++ b/newly_added/demo7.py @@ -0,0 +1,51 @@ +# def make_func(letters,result): +# global func_x +# # print(letters,result) +# draw_axis(-100, 100, 20) +# 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) +# # output.configure(state="normal") +# # output.delete("1.0", tk.END) # 删除当前输出框中的所有文本 +# # output.insert(tk.END, target_result) +# # output.configure(state="disabled") +# code_str = fitting('x', target_result) +# # print(code_str) +# target_func = create_func(code_str) # 获取当前函数func +# # # print(target_result) +# # # 生成带噪声的待拟合数据 +# # np.random.seed(0) +# # x_data = np.linspace(-100, 100, 50) +# # y_data = target_func(x_data) + np.random.normal(0, 0.5, 50) +# # # print(y_data) +# # code_str = fitting(letters, result) +# # print(code_str) +# # func_x = create_func(code_str) # 获取当前函数func +# # +# # # 初始参数的估计值 +# # initial_coeffs = [1.0 for _ in range(n)] +# # # 使用最小二乘法拟合函数 +# # result = leastsq(residuals, initial_coeffs, args=(y_data, x_data)) +# # best_coeffs = result[0] +# # # 打印拟合得到的系数 +# # print("拟合得到的系数:", best_coeffs) +# +# # 绘制拟合结果 +# x_fit = np.linspace(-100, 100, 50) +# y_fit = target_func(x_fit) +# +# # plt.plot(x_data, y_data, 'bo', label='原始数据') +# 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 residuals(coeffs, y, x): +# return y - func_x(x, *coeffs) \ No newline at end of file diff --git a/newly_added/demo8.py b/newly_added/demo8.py new file mode 100644 index 0000000..4e32af2 --- /dev/null +++ b/newly_added/demo8.py @@ -0,0 +1,17 @@ +from sympy import symbols, Eq, simplify + +x = symbols('x') +a = symbols('a') +b = symbols('b') + +expr1 = a*x + b +expr2 = 12*x + 1.3 + +eq = Eq(expr1, expr2) # 创建一个等式表达式 + +simplified_eq = simplify(eq) # 化简等式 + +if simplified_eq == True: + print("两个表达式相等") +else: + print("两个表达式不相等") \ No newline at end of file diff --git a/newly_added/dot5.png b/newly_added/dot5.png new file mode 100644 index 0000000..a5b535b Binary files /dev/null and b/newly_added/dot5.png differ diff --git a/newly_added/formula.py b/newly_added/formula.py new file mode 100644 index 0000000..9ef1e99 --- /dev/null +++ b/newly_added/formula.py @@ -0,0 +1,103 @@ +import re +def convert_log(expression): + # 匹配log(x,b)的正则表达式 + pattern = r'log\((\w+),(\w+)\)' + match = re.match(pattern, expression) + if match: + x = match.group(1) + b = match.group(2) + # 构造新表达式 + new_expression = f'(np.log({x}) + 1e-5) / (np.log({b}) + 1e-5)' + return new_expression + else: + return None + +def convert_e(expression): + new_expression = 'np.exp'+expression[2:] + return new_expression + +def convert_trigonometric(expression): + # 构造新表达式 + new_expression = 'np.'+expression + return new_expression + +def convert_x(expression): + # 匹配log(x,b)的正则表达式 + pattern = r'(\w+)\^(\w+)' + match = re.match(pattern, expression) + if match: + base = match.group(1) + exponent = match.group(2) + # 构造新表达式 + new_expression = f'np.power({base}, {exponent})' + return new_expression + else: + return None +def remove_inner_spaces(expression): + # 匹配括号内的空格的正则表达式 + pattern = r'\((.*?)\)' + # 替换匹配到的括号内的空格 + new_expression = re.sub(pattern, lambda match: '(' + match.group(1).replace(' ', '') + ')', expression) + return new_expression + +def add_spaces(expression): + # 匹配运算符的正则表达式 + pattern = r'([+\-*/])' + # 替换匹配到的运算符 + new_expression = re.sub(pattern, r' \1 ', expression) + return new_expression + +def find_letter_index(string): + pattern = r'([a-zA-Z])x\b' + matches = re.findall(pattern, string) + # print(matches) + letter_indices = [] + for i in range(len(string)): + if string[i] in matches: + if string[i+1]=='x': + letter_indices.append(i) + return letter_indices + +def expression_output(expression): + # 示例使用 + modified_list = find_letter_index(expression) # 将x前没有*的加上 + index = 1 + for i in modified_list: + expression = expression[0:index+i]+'*'+expression[index+i:] + index += 1 + # print(expression) + expression = add_spaces(expression) + new_expression = remove_inner_spaces(expression) + list_expression = new_expression.split(' ') + conforms = ['e^', 'log', 'sin', 'cos', 'tan', '^'] + new_list = [] + for expres in list_expression: + new_list.append(expres) + for i in range(6): + if conforms[i] in expres: + index = list_expression.index(expres) + if i == 0: + list_expression[index] = convert_e(expres) + elif i == 1: + list_expression[index] = convert_log(expres) + elif i == 5: + list_expression[index] = convert_x(expres) + else: + list_expression[index] = convert_trigonometric(expres) + new_list[-1] = new_list[-1].replace(conforms[i], "") + break + new_list = [char for item in new_list for char in item if char.isalpha()] + parameter ='x,' + for item in new_list: + if item not in parameter and item != 'x': + parameter = parameter+item+',' + for item in new_list: + if item not in parameter: + parameter = parameter+item+',' + str_expression = '' + for item in list_expression: + str_expression = str_expression + item + if len(parameter[:-1]) > 1: + return parameter[:-1], str_expression + else: + return None, None \ No newline at end of file diff --git a/newly_added/functions.py b/newly_added/functions.py new file mode 100644 index 0000000..8d59fff --- /dev/null +++ b/newly_added/functions.py @@ -0,0 +1,144 @@ +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() \ No newline at end of file