# -*- 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 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 # 创建弹出窗口 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(): 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('函数拟合') 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", "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) # 防止用户调整尺寸 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, 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(' ')#以空格分割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, 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, 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, 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, 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, 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') 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): 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函数系数: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) # 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=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_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_image(int((140 - rr)*screen_size), int((30 - rr)*screen_size), image=list_image[18]) else: 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 # 设置按钮大小 for widget in F_root.winfo_children(): 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.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.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, 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