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.

493 lines
20 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# -*- coding: utf-8 -*-
import random
from tkinter import messagebox
import pandas as pd
from sympy import sympify, SympifyError
import sys
import numpy as np
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 scipy.optimize import leastsq
import input
from FuncNameFormula.formula import *
import data as gl_data
from X1 import *
from X2 import *
from X3 import *
from X4 import *
global Q_root,F_root
global root_window
global label1,label2,label3
# 编程4.15-----------------------------------------
def create_func(code_str):
# 创建一个空的命名空间
namespace = {}
# 使用exec函数执行字符串代码并指定命名空间为locals
exec(code_str, globals(), namespace)
# 返回命名空间中的函数对象
return namespace['func']
def check_formula_syntax(formula):
try:
sympify(formula)
return True
except SympifyError:
return False
# 负责处理用户输入并验证数学表达式的正确性
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 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 fitting(letters,result):
code_str = '''
def func({}):
return {}
'''.format(letters,result)
return code_str
def show_image():
from PIL import ImageTk, Image
# 加载图像
image = Image.open("new_line.png")
image = image.resize((850, 700))
# 在画布上显示图像
image_tk = ImageTk.PhotoImage(image)
root_window.image_tk = image_tk
show_canvas.create_image(0, 0, anchor="nw", image=image_tk)
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='sample curve')
# 将图例放在图的外面
# plt.legend(loc='upper left')
plt.legend(loc='center left', bbox_to_anchor=(1, 0.9))
plt.xlabel('x')
plt.ylabel('y')
# plt.show()
# plt.subplots_adjust(right=0.7)
plt.savefig('new_line.png', bbox_inches='tight')
# 清除当前图形状态
plt.clf()
show_image()
# 基于验证通过的表达式创建函数并在GUI中展示该函数的图形化表示。
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 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 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 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('1400x700') # 更新窗口尺寸
subwindow.title('新增拟合函数')
# 创建画布
global show_canvas
canvas_width = 1400 # 更新画布宽度
canvas_height = 700 # 更新画布高度
canvas = Canvas(subwindow, width=canvas_width, height=canvas_height, bg="#ADD8E6")
canvas.pack()
show_canvas = Canvas(canvas, width=840, height=700, bg="white") # 更新内部画布尺寸
show_canvas.place(x=0, y=0)
global entry1, entry2, entry3
# 创建输入框及标签
label_font_size = 14 # 设置字体大小
label1 = tk.Label(canvas, text="函数名:", font=("", label_font_size))
label1.place(x=910, y=70) # 更新位置
entry1 = tk.Entry(canvas, font=("", label_font_size))
entry1.place(x=910, y=140, width=420, height=56) # 更新位置和尺寸
label2 = tk.Label(canvas, text="函数式:", font=("", label_font_size))
label2.place(x=910, y=210)
entry2 = tk.Entry(canvas, font=("", label_font_size))
entry2.place(x=910, y=280, width=420, height=56)
label3 = tk.Label(canvas, text="测试函数式:", font=("", label_font_size))
label3.place(x=910, y=350)
entry3 = tk.Entry(canvas, font=("", label_font_size))
entry3.place(x=910, y=420, width=420, height=56)
# 创建按钮
button_font_size = 14 # 设置按钮字体大小
button1 = tk.Button(canvas, text="生成函数", command=add_func, font=("", button_font_size))
button1.place(x=910, y=560)
button2 = tk.Button(canvas, text="函数曲线", command=customized_curve, font=("", button_font_size))
button2.place(x=1050, y=560)
button3 = tk.Button(canvas, text="函数确认", command=func_save, font=("", button_font_size))
button3.place(x=1190, y=560)
# 编程4.15 END-----------------------------------------
# 创建弹出窗口
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 read_fitting():
file_path = 'functions_new.xlsx' # 设置路径
df = pd.read_excel(io=file_path, header=0) # 设置表格
gl_data.FITT_SAVE = df.to_dict()
def element1(path):
# print(path)
# 加载图像并创建一个 PhotoImage 对象
image = tk.PhotoImage(file=path)
# 保存图片的引用,防止被垃圾回收
root_window.image = image
return image
def element(path, width, height):
# 加载图元对应的图片文件
from PIL import ImageTk, Image
# print(path)
img = Image.open(path)
# # 使用resize方法调整图片
img = img.resize((width, height))
# 把Image对象转换成PhotoImage对象
img = ImageTk.PhotoImage(img)
# 保存图片的引用,防止被垃圾回收
root_window.img = img
return img
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 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)
## GUI part-----------------------------------------------------
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 16', 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 18', fill='black')
def buttons():
# 随机生成样本点并显示
b_dot = tk.Button(root_window, image=list_image[14], relief=tk.FLAT, bd=0,
command=lambda: BF_generate_plot_dada(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: BF_plot_data(gl_data.SampleData, 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: BF_plot_data_and_curve(gl_data.CurveData, gl_data.SampleData, gl_data.LOW, gl_data.HIGH))
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: BF_fit_X5_new(root_window,screen_size, gl_data.Xian_index, gl_data.SampleData))
b_fit.place(x=int(821*screen_size), y=int(410*screen_size))
# 编程4.16 -----------------------------------------
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('<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"))
# 编程4.16 END-----------------------------------------
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 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(root_window, screen_size)
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