parent
73f9a5e10e
commit
d83179620e
Binary file not shown.
@ -0,0 +1,57 @@
|
||||
import json
|
||||
import re
|
||||
|
||||
|
||||
class FunctionUtil:
|
||||
def __init__(self):
|
||||
self.filename = "./function.json"
|
||||
self.data = {}
|
||||
self.load()
|
||||
|
||||
def load(self):
|
||||
"""
|
||||
加载文件数据
|
||||
"""
|
||||
try:
|
||||
with open(self.filename, 'r', encoding="utf-8") as f:
|
||||
self.data = json.load(f)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
def save(self):
|
||||
"""
|
||||
将函数保存至json文件当中
|
||||
"""
|
||||
with open(self.filename, "w", encoding="utf-8") as f:
|
||||
json.dump(self.data, f)
|
||||
|
||||
def check_function_exit(self, function):
|
||||
"""
|
||||
检查用户输入的函数是否包含用户自定义函数
|
||||
"""
|
||||
for key in self.data.keys():
|
||||
if function.find(key) >=0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def add_function(self, key, value):
|
||||
"""
|
||||
添加函数
|
||||
"""
|
||||
self.data[key.strip()] = value.strip()
|
||||
self.save()
|
||||
|
||||
def get_function_by_iter(self, key):
|
||||
result = key
|
||||
for dict_key in self.data.keys():
|
||||
if result.find(dict_key) >= 0:
|
||||
value = result.replace(dict_key, self.data[dict_key])
|
||||
result = self.replace_values_recursive(value)
|
||||
return result
|
||||
|
||||
def replace_values_recursive(self, value):
|
||||
for key in self.data:
|
||||
if key in value:
|
||||
replacement = self.data[key]
|
||||
value = value.replace(key, replacement)
|
||||
return value
|
@ -0,0 +1,47 @@
|
||||
import tkinter as tk
|
||||
|
||||
import sympy
|
||||
from matplotlib.figure import Figure
|
||||
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
||||
from mpl_toolkits.mplot3d import Axes3D
|
||||
import numpy as np
|
||||
from sympy import symbols, lambdify
|
||||
|
||||
|
||||
def plot_3d_graph(expression):
|
||||
# 创建 sympy 符号
|
||||
x, y = symbols('x y')
|
||||
# 将表达式转换为可计算的函数
|
||||
expr = lambdify((x, y), expression, modules=['numpy'])
|
||||
# 创建数据网格
|
||||
X = np.linspace(-10, 10, 1000)
|
||||
Y = np.linspace(-10, 10, 1000)
|
||||
X, Y = np.meshgrid(X, Y)
|
||||
Z = expr(X, Y)
|
||||
# 创建一个 Figure 对象
|
||||
fig = Figure(figsize=(6, 6), dpi=100)
|
||||
# 在 Figure 上创建一个 3D 子图
|
||||
ax = fig.add_subplot(111, projection='3d')
|
||||
# 绘制三维图形
|
||||
ax.plot_surface(X, Y, Z, cmap='viridis')
|
||||
|
||||
# 创建一个 tkinter 窗口
|
||||
root = tk.Tk()
|
||||
root.title("3D Graph")
|
||||
|
||||
# 在 tkinter 窗口上创建一个 canvas
|
||||
canvas = FigureCanvasTkAgg(fig, master=root)
|
||||
canvas.draw()
|
||||
canvas.get_tk_widget().pack()
|
||||
|
||||
# 运行 tkinter 主循环
|
||||
tk.mainloop()
|
||||
|
||||
# 输入要绘制的数学公式
|
||||
expression = "gamma(x)"
|
||||
|
||||
# 使用 sympy 解析数学公式
|
||||
parsed_expression = sympy.sympify(expression)
|
||||
|
||||
# 调用函数绘制三维图形
|
||||
plot_3d_graph(parsed_expression)
|
@ -0,0 +1,360 @@
|
||||
import math
|
||||
from collections import OrderedDict
|
||||
from time import sleep
|
||||
from tkinter import *
|
||||
import tkinter.font as tkFont
|
||||
from tkinter import messagebox
|
||||
from tkinter.ttk import Treeview
|
||||
|
||||
import sympy
|
||||
from numpy import arange
|
||||
from functionUtil import FunctionUtil
|
||||
|
||||
|
||||
# 获取文本的宽度和高度
|
||||
def get_text_dimensions(canvas, text, font):
|
||||
text_id = canvas.create_text(0, 0, text=text, font=font, anchor="center")
|
||||
bbox = canvas.bbox(text_id)
|
||||
width = bbox[2] - bbox[0]
|
||||
height = bbox[3] - bbox[1]
|
||||
canvas.delete(text_id)
|
||||
return width, height
|
||||
|
||||
|
||||
# 在画布中央绘制文本
|
||||
def center_text(canvas, text, font):
|
||||
canvas_width = int(canvas.cget("width"))
|
||||
text_width, text_height = get_text_dimensions(canvas, text, font)
|
||||
x = canvas_width // 2
|
||||
y = 200
|
||||
canvas.create_text(x, y, text=text, font=font, anchor="center")
|
||||
|
||||
|
||||
class Graph(Canvas):
|
||||
def __init__(self, master=None, **kwargs):
|
||||
super().__init__(master, **kwargs)
|
||||
self.width = int(self.cget('width'))
|
||||
self.height = int(self.cget('height'))
|
||||
self.origin = (self.width / 2, self.height / 2)
|
||||
self.bili_x = 20
|
||||
self.bili_y = 20
|
||||
self.draw_axis()
|
||||
self.draw_scale()
|
||||
|
||||
def draw_axis(self):
|
||||
"""
|
||||
绘制坐标轴
|
||||
"""
|
||||
self.delete("all")
|
||||
self.create_line(0, self.origin[1], self.width, self.origin[1], fill='white', arrow=LAST) # 一象限xy轴
|
||||
self.create_line(self.origin[0], 0, self.origin[0], self.height, fill='white', arrow=FIRST)
|
||||
|
||||
def draw_scale(self):
|
||||
"""
|
||||
绘制刻度值
|
||||
"""
|
||||
for i in range(-math.ceil((self.origin[0] / self.bili_x)) + 1, math.ceil((self.width - self.origin[0]) / self.bili_x)):
|
||||
j = i * self.bili_x
|
||||
if (i % 10 == 0):
|
||||
self.create_line(j + self.origin[0], self.origin[1], j + self.origin[0], self.origin[1] - 5, fill='black')
|
||||
self.create_text(j + self.origin[0], self.origin[1] + 10, text=i)
|
||||
for i in range(-math.ceil((self.height - self.origin[1]) / self.bili_y) + 1, math.ceil((self.origin[1] / self.bili_y))):
|
||||
j = -(i * self.bili_y)
|
||||
if (i == 0):
|
||||
continue
|
||||
elif (i % 2 == 0):
|
||||
self.create_line(self.origin[0], j + self.origin[1], self.origin[0] + 5, j + self.origin[1], fill='black')
|
||||
self.create_text(self.origin[0] - 10, j + self.origin[1], text=i)
|
||||
|
||||
def switch_quadrant(self, quadrant):
|
||||
"""
|
||||
切换象限
|
||||
"""
|
||||
if quadrant == 1:
|
||||
self.origin = (50, self.height - 50)
|
||||
else:
|
||||
self.origin = (self.width / 2, self.height / 2)
|
||||
self.draw_axis()
|
||||
self.draw_scale()
|
||||
|
||||
def draw_graph(self, func, draw_precision=0.1, count=1000, bili_x=20, bili_y=40, c='blue'):
|
||||
'xmin,xmax 自变量的取值范围; c 图像颜色'
|
||||
'x0,y0 原点坐标 w,h 横纵轴半长 draw_precision 步进'
|
||||
self.bili_x, self.bili_y = int(bili_x), int(bili_y)
|
||||
self.draw_axis()
|
||||
self.draw_scale()
|
||||
w1, w2 = self.bili_x, self.bili_y # w1,w2为自变量和函数值在横纵轴上的放大倍数
|
||||
xmin, xmax = -math.ceil((self.origin[0] / self.bili_x)) + 1, math.ceil((self.width - self.origin[0]) / self.bili_x)
|
||||
co2 = []
|
||||
try:
|
||||
for x in arange(xmin, xmax, draw_precision): # draw_precision----画图精度
|
||||
y = sympy.sympify(func).subs("x", x).evalf()
|
||||
coord = self.origin[0] + w1 * x, self.origin[1] - w2 * y, self.origin[0] + w1 * x + 1, self.origin[1] - w2 * y + 1
|
||||
if abs(coord[1]) < self.height: # 超过w,h就截断
|
||||
co2.append((self.origin[0] + w1 * x, self.origin[1] - w2 * y))
|
||||
if count is None:
|
||||
length = len(co2)
|
||||
else:
|
||||
length = len(co2[:int(count)])
|
||||
for i in range(length):
|
||||
if (draw_precision >= 1):
|
||||
self.create_line(co2, fill=c, width=1)
|
||||
if (i + 1 == len(co2)):
|
||||
break
|
||||
if (abs(co2[i][1] - co2[i + 1][1]) > 100):
|
||||
continue
|
||||
else:
|
||||
self.create_line(co2[i], co2[i + 1], fill=c, width=1)
|
||||
sleep(0.01)
|
||||
self.update()
|
||||
except Exception as E:
|
||||
messagebox.showerror("错误", message=f"函数有误!\n{E}")
|
||||
|
||||
|
||||
class ExpressionCanvas(Canvas):
|
||||
def __init__(self, master, *args, **kwargs):
|
||||
super().__init__(master, *args, **kwargs)
|
||||
self.node_width = 60
|
||||
self.node_height = 30
|
||||
self.x_spacing = 10
|
||||
self.y_spacing = 70
|
||||
self.level = {}
|
||||
|
||||
def add_node(self, text, x, y, parent=None):
|
||||
node_width = len(text) * 5 + 20
|
||||
if node_width < 40:
|
||||
node_width = 50
|
||||
# 创建节点
|
||||
coords = [x - node_width / 2, y, x + node_width / 2, y + self.node_height]
|
||||
if coords[1] in self.level:
|
||||
max_x = max(self.level[coords[1]])
|
||||
if coords[0] <= max_x:
|
||||
x = max_x + node_width / 2 + self.x_spacing
|
||||
node_id = self.create_rectangle(x - node_width / 2, y, x + node_width / 2, y + self.node_height,
|
||||
fill="white")
|
||||
sleep(0.2)
|
||||
self.update()
|
||||
elif text == "x":
|
||||
x = self.coords(parent)[0] + node_width / 2
|
||||
node_id = self.create_rectangle(x - node_width / 2, y, x + node_width / 2, y + self.node_height,
|
||||
fill="white")
|
||||
sleep(0.2)
|
||||
self.update()
|
||||
else:
|
||||
node_id = self.create_rectangle(x - node_width / 2, y, x + node_width / 2, y + self.node_height, fill="white")
|
||||
sleep(0.2)
|
||||
self.update()
|
||||
coords = [x - node_width / 2, y, x + node_width / 2, y + self.node_height]
|
||||
if coords[1] in self.level:
|
||||
self.level[coords[1]].append(coords[2])
|
||||
else:
|
||||
self.level[coords[1]] = [coords[2]]
|
||||
text_id = self.create_text(x + 5, y + self.node_height // 2, text=text, anchor="center")
|
||||
sleep(0.2)
|
||||
self.update()
|
||||
# 绘制父节点和当前节点的连线
|
||||
if parent:
|
||||
parent_coords = self.coords(parent)
|
||||
parent_x = parent_coords[0] + (parent_coords[2] - parent_coords[0]) // 2
|
||||
parent_y = parent_coords[1] + (parent_coords[3] - parent_coords[1]) // 2
|
||||
self.create_line(parent_x, parent_y + self.node_height // 2, x, y, fill="black")
|
||||
self.update()
|
||||
return node_id
|
||||
|
||||
def draw_expression(self, expression, x, y, parent=None, sibling_width=0):
|
||||
# 创建节点并绘制表达式文本
|
||||
node = self.add_node(str(expression), x, y, parent=parent)
|
||||
if expression.is_Atom:
|
||||
return node
|
||||
# 处理子表达式
|
||||
num_children = len(expression.args)
|
||||
total_width = num_children * self.node_width + (num_children - 1) * self.x_spacing
|
||||
start_x = x - total_width // 2
|
||||
for subexpr in expression.args:
|
||||
subnode = self.draw_expression(subexpr, start_x, y + self.y_spacing, parent=node, sibling_width=total_width)
|
||||
start_x += self.node_width + self.x_spacing
|
||||
return node
|
||||
|
||||
|
||||
class FunctionDisplay(Frame):
|
||||
def __init__(self, master, attr):
|
||||
super().__init__(master)
|
||||
self.attr = attr
|
||||
self.font_style = tkFont.Font(family="Lucida Grande", size=30)
|
||||
self.functions = FunctionUtil()
|
||||
self.master = master
|
||||
self.create_window()
|
||||
|
||||
def add_function(self):
|
||||
"""
|
||||
用户添加函数
|
||||
"""
|
||||
input_func = self.func_input.get()
|
||||
if input_func == "":
|
||||
messagebox.showwarning("注意", message="请输入函数!")
|
||||
return
|
||||
elif input_func.find("=") < 0:
|
||||
messagebox.showerror("注意", message="添加函数的格式为:\nSinPlusCos(x)=sin(x)+cos(x)")
|
||||
return
|
||||
left_var = input_func.split("=")[0]
|
||||
right_var = input_func.split("=")[1]
|
||||
try:
|
||||
function = self.functions.get_function_by_iter(right_var)
|
||||
sympy.sympify(function, evaluate=False)
|
||||
except Exception as E:
|
||||
messagebox.showerror("注意", message="函数解析错误,请仔细检查函数是否正确!")
|
||||
self.func_input.delete(0, END)
|
||||
return
|
||||
if self.functions.check_function_exit(left_var):
|
||||
result = messagebox.askokcancel(title='标题~', message=f'函数{left_var}已经存在,是否需要覆盖!')
|
||||
if result:
|
||||
self.functions.add_function(left_var, right_var)
|
||||
messagebox.showinfo(title="提示", message="覆盖成功!")
|
||||
self.func_input.delete(0, END)
|
||||
return
|
||||
self.functions.add_function(left_var, right_var)
|
||||
messagebox.showinfo(title="提示", message="添加成功!")
|
||||
self.func_input.delete(0, END)
|
||||
|
||||
def update_quadrant(self):
|
||||
"""
|
||||
更换象限
|
||||
"""
|
||||
self.axis_canvas.switch_quadrant(self.quadrant.get())
|
||||
self.print_function()
|
||||
|
||||
def print_function(self):
|
||||
"""
|
||||
输出
|
||||
"""
|
||||
input_func = self.func_input.get()
|
||||
if input_func == "":
|
||||
messagebox.showwarning("注意", message="请输入函数!")
|
||||
return
|
||||
step = eval(self.x_step.get()) if not self.x_step.get() == "" else 0.1
|
||||
count = self.x_count.get() if not self.x_count.get() == "" else None
|
||||
x_scale = self.x_scale.get() if not self.x_scale.get() == "" else 20
|
||||
y_scale = self.y_scale.get() if not self.y_scale.get() == "" else 40
|
||||
if input_func.find("=") >= 0:
|
||||
left_var = input_func.split("=")[0]
|
||||
right_var = input_func.split("=")[1]
|
||||
if self.functions.check_function_exit(right_var):
|
||||
func = self.functions.get_function_by_iter(right_var)
|
||||
self.axis_canvas.draw_graph(func, step, count,
|
||||
x_scale, y_scale)
|
||||
else:
|
||||
self.axis_canvas.draw_graph(input_func, step, count,
|
||||
x_scale, y_scale)
|
||||
else:
|
||||
if self.functions.check_function_exit(input_func):
|
||||
func = self.functions.get_function_by_iter(input_func)
|
||||
self.axis_canvas.draw_graph(func, step, count,
|
||||
x_scale, y_scale)
|
||||
else:
|
||||
self.axis_canvas.draw_graph(input_func, step, count,
|
||||
x_scale, y_scale)
|
||||
|
||||
def create_form(self):
|
||||
bottom_frame = Frame(self.master, width=self.attr["width"], height=self.attr["height"] * 0.3)
|
||||
self.func_input = Entry(bottom_frame, font=self.font_style, bg="#c0d9d9", width=40)
|
||||
self.func_input.grid(row=0, column=0, columnspan=4, padx=30)
|
||||
self.add_func_button = Button(bottom_frame, text="用户命名并新增基本函数", command=self.add_function, font=("Lucida Grande", 20))
|
||||
self.add_func_button.grid(row=0, column=4, padx=10)
|
||||
Label(bottom_frame, text="x步长", font=("Lucida Grande", 20)).grid(row=1, column=0, padx=30)
|
||||
self.x_step = Entry(bottom_frame, font=self.font_style, bg="#c0d9d9", width=10)
|
||||
self.x_step.grid(row=1, column=1, padx=10, pady=10)
|
||||
Label(bottom_frame, text="x个数", font=("Lucida Grande", 20)).grid(row=1, column=2, padx=30)
|
||||
self.x_count = Entry(bottom_frame, font=self.font_style, bg="#c0d9d9", width=10)
|
||||
self.x_count.grid(row=1, column=3, padx=10, pady=10)
|
||||
|
||||
Label(bottom_frame, text="坐标轴", font=("Lucida Grande", 20)).grid(row=2, column=0, padx=30)
|
||||
self.quadrant = IntVar()
|
||||
self.quadrant.set(4)
|
||||
Radiobutton(bottom_frame, text="四象限", font=("Lucida Grande", 20), indicatoron=False, command=self.update_quadrant, value=4, variable=self.quadrant).grid(row=2, column=1, padx=10, pady=10)
|
||||
Radiobutton(bottom_frame, text="一象限", font=("Lucida Grande", 20), indicatoron=False, command=self.update_quadrant, value=1, variable=self.quadrant).grid(row=2, column=2, padx=10, pady=10)
|
||||
|
||||
Label(bottom_frame, text="x放大倍数", font=("Lucida Grande", 20)).grid(row=3, column=0, padx=30)
|
||||
self.x_scale = Entry(bottom_frame, font=self.font_style, bg="#c0d9d9", width=10)
|
||||
self.x_scale.grid(row=3, column=1, padx=10, pady=10)
|
||||
Label(bottom_frame, text="y放大倍数", font=("Lucida Grande", 20)).grid(row=3, column=2, padx=30)
|
||||
self.y_scale = Entry(bottom_frame, font=self.font_style, bg="#c0d9d9", width=10)
|
||||
self.y_scale.grid(row=3, column=3, padx=10, pady=10)
|
||||
|
||||
Button(bottom_frame, text="输出", font=("Lucida Grande", 20),
|
||||
command=self.print_function, height=3, width=5).grid(row=1, rowspan=3, column=4, sticky="w")
|
||||
bottom_frame.place(x=0, y=self.attr["height"] * 0.65 + 20)
|
||||
|
||||
def show_user_function(self):
|
||||
children_window = Toplevel(root)
|
||||
children_window.title("用户自定义函数")
|
||||
children_window.geometry('350x260+450+200')
|
||||
packet_frame = Frame(children_window)
|
||||
packet_frame.grid(row=0, column=0, columnspan=3, padx=10, pady=5)
|
||||
function_treeview = Treeview(packet_frame, columns=("function_name", "function"), show="headings")
|
||||
function_treeview.heading("function_name", text="函数名称")
|
||||
function_treeview.column("function_name", width=100, anchor=CENTER)
|
||||
function_treeview.heading("function", text="函数体")
|
||||
function_treeview.column("function", width=200, anchor=CENTER)
|
||||
function_treeview.pack(side="left", fill="both")
|
||||
scrollbar = Scrollbar(packet_frame, orient="vertical", command=function_treeview.yview)
|
||||
scrollbar.pack(side="right", fill="y")
|
||||
function_treeview.configure(yscrollcommand=scrollbar.set)
|
||||
|
||||
def delete_item():
|
||||
selected_item = function_treeview.selection()
|
||||
for item in selected_item:
|
||||
item_data = function_treeview.item(item)
|
||||
function_treeview.delete(item)
|
||||
self.functions.data.pop(item_data["values"][0])
|
||||
self.functions.save()
|
||||
|
||||
context_menu = Menu(root, tearoff=False)
|
||||
context_menu.add_command(label="删除", command=delete_item)
|
||||
|
||||
def popup_menu(event):
|
||||
if function_treeview.identify_region(event.x, event.y) == "cell":
|
||||
function_treeview.selection_set(function_treeview.identify_row(event.y))
|
||||
context_menu.post(event.x_root, event.y_root)
|
||||
|
||||
function_treeview.bind("<Button-3>", popup_menu)
|
||||
for function_name, function in self.functions.data.items():
|
||||
function_treeview.insert("", "end", values=(function_name, function))
|
||||
|
||||
|
||||
def create_window(self):
|
||||
self.axis_canvas = Graph(self.master, width=self.attr["width"] * 0.65, height=self.attr["height"] * 0.65, bg="#cdcdcd")
|
||||
self.axis_canvas.place(x=0, y=0)
|
||||
self.text_canvas = ExpressionCanvas(self.master, width=self.attr["width"] * 0.35, height=self.attr["height"] * 0.65, bg="#cdcdcd")
|
||||
self.text_canvas.create_text(20 + 180, 35, text='可识别基本函数', fill='red', font=("Purisa", 25, "bold"))
|
||||
menubar = Menu(root)
|
||||
menubar.add_command(label='查看自定义函数', command=self.show_user_function)
|
||||
root.config(menu=menubar)
|
||||
text = "可支持下列函数的加减乘除组合运算\n" \
|
||||
"\ty=sin(x)\n" \
|
||||
"\tcos(x)\n" \
|
||||
"\ttan(x)\n" \
|
||||
"\tcot(x)\n" \
|
||||
"\tx^n\n" \
|
||||
"\tP1(x)=x^3+x^2+x+5\n" \
|
||||
"---将括号内的表达式命名为P1,P1可\n" \
|
||||
"出现于用户构造中"
|
||||
self.text_canvas.create_text(20 + 180, 35, text='可识别基本函数', fill='red', font=("Purisa", 25, "bold"))
|
||||
center_text(self.text_canvas, text, ("Lucida Grande", 17))
|
||||
self.text_canvas.place(x=int(self.attr["width"] * 0.65), y=0)
|
||||
self.create_form()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
root = Tk()
|
||||
screenwidth = root.winfo_screenwidth()
|
||||
screenheight = root.winfo_screenheight()
|
||||
root_attr = {
|
||||
"width": 1200,
|
||||
"height": 800,
|
||||
}
|
||||
alignstr = '%dx%d+%d+%d' % (root_attr['width'], root_attr['height'], (screenwidth - root_attr['width']) / 2,
|
||||
(screenheight - root_attr['height']) / 2)
|
||||
root.geometry(alignstr)
|
||||
root.resizable(width=False, height=False)
|
||||
app = FunctionDisplay(root, root_attr)
|
||||
root.mainloop()
|
@ -0,0 +1 @@
|
||||
{"f1": "x**2 + 2*x + 1", "f3": "x**2", "f2": "(x/100)**2+2*(x/100)+1", "sinpluscos(x)": "cos(x) + sin(x)"}
|
@ -0,0 +1,9 @@
|
||||
import sympy
|
||||
|
||||
from functionUtil import FunctionUtil
|
||||
|
||||
|
||||
# f = FunctionUtil()
|
||||
# print(f.get_function_by_iter("f1 + f3"))
|
||||
|
||||
print(sympy.sympify("(x/100)**2+2*(x/100)+1"))
|
@ -0,0 +1,25 @@
|
||||
def trapezoidal_rule(a, b, n):
|
||||
"""
|
||||
梯形法进行数值积分
|
||||
|
||||
参数:
|
||||
- f: 要积分的函数
|
||||
- a: 积分区间的起始点
|
||||
- b: 积分区间的结束点
|
||||
- n: 划分的子区间数
|
||||
|
||||
返回:
|
||||
- 积分近似值
|
||||
"""
|
||||
h = (b - a) / n # 子区间宽度
|
||||
x = [a + i * h for i in range(n+1)] # 子区间的节点
|
||||
y = [x_i ** 2 for x_i in x] # 子区间节点对应的函数值
|
||||
integral = (h / 2) * (y[0] + 2 * sum(y[1:n]) + y[n]) # 梯形法计算积分值
|
||||
return integral
|
||||
|
||||
|
||||
a = 0 # 积分区间的起始点
|
||||
b = 1 # 积分区间的结束点
|
||||
n = 100 # 划分的子区间数
|
||||
integral = trapezoidal_rule(a, b, n)
|
||||
print("数值积分的近似值:", integral)
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
"f1": "sin(x)+cos(x)+p3(x)",
|
||||
"f3": "x**2",
|
||||
"f2": "(x/100)**2+2*(x/100)+1",
|
||||
"SinPlusCos(x)": "sin(x) + cos(x)",
|
||||
"superFunc": "(x/100)**2 + 2*(x/100) + 1",
|
||||
"func": "3 * x + sin(x) * cos(x)"
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
import sympy
|
||||
|
||||
from functionUtil import FunctionUtil
|
||||
|
||||
|
||||
# f = FunctionUtil()
|
||||
# print(f.get_function_by_iter("f1 + f3"))
|
||||
|
||||
print(sympy.sympify("(x/100)**2+2*(x/100)+1"))
|
@ -0,0 +1,372 @@
|
||||
import math
|
||||
from time import sleep
|
||||
from ttkbootstrap import *
|
||||
import tkinter.font as tkFont
|
||||
from tkinter import messagebox
|
||||
from tkinter.ttk import Treeview
|
||||
|
||||
import numpy as np
|
||||
from matplotlib.figure import Figure
|
||||
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
||||
|
||||
import sympy
|
||||
from numpy import arange
|
||||
from sympy import symbols, lambdify
|
||||
|
||||
from functionUtil import FunctionUtil
|
||||
|
||||
|
||||
# 在画布中央绘制文本
|
||||
def center_text(canvas, text, font):
|
||||
canvas_width = int(canvas.cget("width"))
|
||||
x = canvas_width // 2
|
||||
y = 200
|
||||
canvas.create_text(x, y, text=text, font=font, anchor="center")
|
||||
|
||||
|
||||
class Graph(Canvas):
|
||||
"""
|
||||
绘制函数图形
|
||||
"""
|
||||
|
||||
def __init__(self, master=None, **kwargs):
|
||||
super().__init__(master, **kwargs)
|
||||
self.width = int(self.cget('width'))
|
||||
self.height = int(self.cget('height'))
|
||||
self.origin = (self.width / 2, self.height / 2)
|
||||
self.bili_x = 20
|
||||
self.bili_y = 20
|
||||
self.draw_axis()
|
||||
self.draw_scale()
|
||||
self.fig = FigureCanvasTkAgg()
|
||||
|
||||
|
||||
def draw_axis(self):
|
||||
"""
|
||||
绘制坐标轴
|
||||
"""
|
||||
self.delete("all")
|
||||
self.create_line(0, self.origin[1], self.width, self.origin[1], fill='black', arrow=LAST) # 一象限xy轴
|
||||
self.create_line(self.origin[0], 0, self.origin[0], self.height, fill='black', arrow=FIRST)
|
||||
|
||||
def draw_scale(self):
|
||||
"""
|
||||
绘制刻度值
|
||||
"""
|
||||
for i in range(-math.ceil((self.origin[0] / self.bili_x)) + 1,
|
||||
math.ceil((self.width - self.origin[0]) / self.bili_x)):
|
||||
j = i * self.bili_x
|
||||
if (i % 10 == 0):
|
||||
self.create_line(j + self.origin[0], self.origin[1], j + self.origin[0], self.origin[1] - 5,
|
||||
fill='black')
|
||||
self.create_text(j + self.origin[0], self.origin[1] + 10, text=i)
|
||||
for i in range(-math.ceil((self.height - self.origin[1]) / self.bili_y) + 1,
|
||||
math.ceil((self.origin[1] / self.bili_y))):
|
||||
j = -(i * self.bili_y)
|
||||
if (i == 0):
|
||||
continue
|
||||
elif (i % 2 == 0):
|
||||
self.create_line(self.origin[0], j + self.origin[1], self.origin[0] + 5, j + self.origin[1],
|
||||
fill='black')
|
||||
self.create_text(self.origin[0] - 10, j + self.origin[1], text=i)
|
||||
|
||||
def switch_quadrant(self, quadrant):
|
||||
"""
|
||||
切换象限
|
||||
"""
|
||||
if quadrant == 1:
|
||||
self.origin = (50, self.height - 50)
|
||||
else:
|
||||
self.origin = (self.width / 2, self.height / 2)
|
||||
self.draw_axis()
|
||||
self.draw_scale()
|
||||
|
||||
def draw_graph_3d(self, func, draw_precision=0.1, count=1000, bili_x=20, bili_y=40, ):
|
||||
self.fig.get_tk_widget().destroy()
|
||||
self.delete("all")
|
||||
# 创建 sympy 符号
|
||||
x, y = symbols('x y')
|
||||
self.bili_x, self.bili_y = int(bili_x), int(bili_y)
|
||||
# 将表达式转换为可计算的函数
|
||||
expr = lambdify((x, y), func, modules=['numpy'])
|
||||
xmin, xmax = -math.ceil((self.origin[0] / self.bili_x)) + 1, math.ceil(
|
||||
(self.width - self.origin[0]) / self.bili_x)
|
||||
# 创建数据网格
|
||||
X = np.linspace(xmin, xmax, int(draw_precision * 100))
|
||||
Y = np.linspace(xmin, xmax, int(draw_precision * 100))
|
||||
X, Y = np.meshgrid(X, Y)
|
||||
Z = expr(X, Y)
|
||||
# 创建一个 Figure 对象
|
||||
fig = Figure(figsize=(5.5, 6), dpi=100)
|
||||
# 在 Figure 上创建一个 3D 子图
|
||||
ax = fig.add_subplot(111, projection='3d')
|
||||
# 绘制三维图形
|
||||
ax.plot_surface(X, Y, Z, cmap='viridis')
|
||||
self.fig = FigureCanvasTkAgg(fig, master=self)
|
||||
self.fig.draw()
|
||||
self.fig.get_tk_widget().pack(fill=BOTH, ipadx=0, ipady=0, pady=0, padx=0)
|
||||
|
||||
def draw_graph(self, func, draw_precision=0.1, count=1000, bili_x=20, bili_y=40, c='blue'):
|
||||
'xmin,xmax 自变量的取值范围; c 图像颜色'
|
||||
'x0,y0 原点坐标 w,h 横纵轴半长 draw_precision 步进'
|
||||
self.fig.get_tk_widget().destroy()
|
||||
self.bili_x, self.bili_y = int(bili_x), int(bili_y)
|
||||
self.draw_axis()
|
||||
self.draw_scale()
|
||||
w1, w2 = self.bili_x, self.bili_y # w1,w2为自变量和函数值在横纵轴上的放大倍数
|
||||
xmin, xmax = -math.ceil((self.origin[0] / self.bili_x)) + 1, math.ceil(
|
||||
(self.width - self.origin[0]) / self.bili_x)
|
||||
co2 = []
|
||||
try:
|
||||
for x in arange(xmin, xmax, draw_precision): # draw_precision----画图精度
|
||||
y = sympy.sympify(func, convert_xor=True).subs("x", x).evalf()
|
||||
coord = self.origin[0] + w1 * x, self.origin[1] - w2 * y, self.origin[0] + w1 * x + 1, self.origin[
|
||||
1] - w2 * y + 1
|
||||
if abs(coord[1]) < self.height: # 超过w,h就截断
|
||||
co2.append((self.origin[0] + w1 * x, self.origin[1] - w2 * y))
|
||||
if count is None:
|
||||
length = len(co2)
|
||||
else:
|
||||
length = len(co2[:int(count)])
|
||||
for i in range(length):
|
||||
if (draw_precision >= 1):
|
||||
self.create_line(co2, fill=c, width=1)
|
||||
if (i + 1 == len(co2)):
|
||||
break
|
||||
if (abs(co2[i][1] - co2[i + 1][1]) > 100):
|
||||
continue
|
||||
else:
|
||||
self.create_line(co2[i], co2[i + 1], fill=c, width=1)
|
||||
sleep(0.01)
|
||||
self.update()
|
||||
except Exception as E:
|
||||
messagebox.showerror("错误", message=f"函数有误!\n{E}")
|
||||
|
||||
|
||||
class ExpressionCanvas(Canvas):
|
||||
def __init__(self, master, *args, **kwargs):
|
||||
super().__init__(master, *args, **kwargs)
|
||||
self.node_width = 60
|
||||
self.node_height = 30
|
||||
self.x_spacing = 10
|
||||
self.y_spacing = 70
|
||||
self.level = {}
|
||||
|
||||
def add_node(self, text, x, y, parent=None):
|
||||
node_width = len(text) * 5 + 20
|
||||
if node_width < 40:
|
||||
node_width = 50
|
||||
# 创建节点
|
||||
coords = [x - node_width / 2, y, x + node_width / 2, y + self.node_height]
|
||||
if coords[1] in self.level:
|
||||
max_x = max(self.level[coords[1]])
|
||||
if coords[0] <= max_x:
|
||||
x = max_x + node_width / 2 + self.x_spacing
|
||||
node_id = self.create_rectangle(x - node_width / 2, y, x + node_width / 2, y + self.node_height,
|
||||
fill="white")
|
||||
sleep(0.2)
|
||||
self.update()
|
||||
elif text == "x":
|
||||
x = self.coords(parent)[0] + node_width / 2
|
||||
node_id = self.create_rectangle(x - node_width / 2, y, x + node_width / 2, y + self.node_height,
|
||||
fill="white")
|
||||
sleep(0.2)
|
||||
self.update()
|
||||
else:
|
||||
node_id = self.create_rectangle(x - node_width / 2, y, x + node_width / 2, y + self.node_height,
|
||||
fill="white")
|
||||
sleep(0.2)
|
||||
self.update()
|
||||
coords = [x - node_width / 2, y, x + node_width / 2, y + self.node_height]
|
||||
if coords[1] in self.level:
|
||||
self.level[coords[1]].append(coords[2])
|
||||
else:
|
||||
self.level[coords[1]] = [coords[2]]
|
||||
text_id = self.create_text(x + 5, y + self.node_height // 2, text=text, anchor="center")
|
||||
sleep(0.2)
|
||||
self.update()
|
||||
# 绘制父节点和当前节点的连线
|
||||
if parent:
|
||||
try:
|
||||
parent_coords = self.coords(parent)
|
||||
parent_x = parent_coords[0] + (parent_coords[2] - parent_coords[0]) // 2
|
||||
parent_y = parent_coords[1] + (parent_coords[3] - parent_coords[1]) // 2
|
||||
self.create_line(parent_x, parent_y + self.node_height // 2, x, y, fill="black")
|
||||
self.update()
|
||||
except Exception as E:
|
||||
pass
|
||||
return node_id
|
||||
|
||||
def draw_expression(self, expression, x, y, parent=None, sibling_width=0):
|
||||
# 创建节点并绘制表达式文本
|
||||
node = self.add_node(str(expression), x, y, parent=parent)
|
||||
if expression.is_Atom:
|
||||
return node
|
||||
# 处理子表达式
|
||||
num_children = len(expression.args)
|
||||
total_width = num_children * self.node_width + (num_children - 1) * self.x_spacing
|
||||
start_x = x - total_width // 2
|
||||
for subexpr in expression.args:
|
||||
subnode = self.draw_expression(subexpr, start_x, y + self.y_spacing, parent=node, sibling_width=total_width)
|
||||
start_x += self.node_width + self.x_spacing
|
||||
return node
|
||||
|
||||
|
||||
class FunctionDisplay(Frame):
|
||||
def __init__(self, master, attr):
|
||||
super().__init__(master)
|
||||
self.attr = attr
|
||||
self.font_style = tkFont.Font(family="Lucida Grande", size=30)
|
||||
self.functions = FunctionUtil()
|
||||
self.master = master
|
||||
self.create_window()
|
||||
self.load_user_function()
|
||||
|
||||
def add_function(self):
|
||||
"""
|
||||
用户添加函数
|
||||
"""
|
||||
input_func = self.func_input.get()
|
||||
if input_func == "":
|
||||
messagebox.showwarning("注意", message="请输入函数!")
|
||||
return
|
||||
elif input_func.find("=") < 0:
|
||||
messagebox.showerror("注意", message="添加函数的格式为:\nSinPlusCos(x)=sin(x)+cos(x)")
|
||||
return
|
||||
left_var = input_func.split("=")[0]
|
||||
right_var = input_func.split("=")[1]
|
||||
try:
|
||||
function = self.functions.get_function_by_iter(right_var)
|
||||
sympy.sympify(function, evaluate=False)
|
||||
except Exception as E:
|
||||
messagebox.showerror("注意", message="函数解析错误,请仔细检查函数是否正确!")
|
||||
self.func_input.delete(0, END)
|
||||
return
|
||||
if self.functions.check_function_exit(left_var):
|
||||
result = messagebox.askokcancel(title='提示', message=f'函数{left_var}已经存在,是否需要覆盖!')
|
||||
if result:
|
||||
self.functions.add_function(left_var, right_var)
|
||||
messagebox.showinfo(title="提示", message="覆盖成功!")
|
||||
self.func_input.delete(0, END)
|
||||
self.load_user_function()
|
||||
return
|
||||
self.functions.add_function(left_var, right_var)
|
||||
messagebox.showinfo(title="提示", message="添加成功!")
|
||||
self.func_input.delete(0, END)
|
||||
self.load_user_function()
|
||||
|
||||
def update_quadrant(self):
|
||||
"""
|
||||
更换象限
|
||||
"""
|
||||
self.axis_canvas.switch_quadrant(self.quadrant.get())
|
||||
self.print_function()
|
||||
|
||||
def print_function(self):
|
||||
"""
|
||||
输出
|
||||
"""
|
||||
self.text_canvas.delete("all")
|
||||
self.text_canvas.level.clear()
|
||||
input_func = self.func_input.get()
|
||||
if input_func == "":
|
||||
messagebox.showwarning("注意", message="请输入函数!")
|
||||
return
|
||||
step = eval(self.x_step.get()) if not self.x_step.get() == "" else 0.1
|
||||
count = self.x_count.get() if not self.x_count.get() == "" else None
|
||||
x_scale = self.x_scale.get() if not self.x_scale.get() == "" else 20
|
||||
y_scale = self.y_scale.get() if not self.y_scale.get() == "" else 40
|
||||
if input_func.find("=") >= 0:
|
||||
input_func = input_func.split("=")[1]
|
||||
if self.functions.check_function_exit(input_func):
|
||||
func = self.functions.get_function_by_iter(input_func)
|
||||
self.axis_canvas.draw_graph_3d(func, step, count, x_scale, y_scale)
|
||||
self.text_canvas.draw_expression(sympy.sympify(func, evaluate=False, convert_xor=True),
|
||||
int(self.text_canvas.cget("width")) // 2, self.text_canvas.y_spacing)
|
||||
else:
|
||||
self.axis_canvas.draw_graph_3d(input_func, step, count, x_scale, y_scale)
|
||||
self.text_canvas.draw_expression(sympy.sympify(input_func, evaluate=False, convert_xor=True),
|
||||
int(self.text_canvas.cget("width")) // 2, self.text_canvas.y_spacing)
|
||||
|
||||
def load_user_function(self):
|
||||
self.user_function_canvas.delete(ALL)
|
||||
self.user_function_canvas.create_text(20 + 90, 35, text='可识别基本函数', fill='red', font=("Purisa", 20, "bold"))
|
||||
num = 2
|
||||
self.user_function_canvas.create_text(0, 70, anchor="nw", text="函数", font=("", 15))
|
||||
self.user_function_canvas.create_text(100, 70, anchor="nw", text="函数体", font=("", 15))
|
||||
for function, function_body in self.functions.data.items():
|
||||
self.user_function_canvas.create_text(0, 20 * num + 50, anchor="nw", text=function, font=("", 15))
|
||||
self.user_function_canvas.create_text(100, 20 * num + 50, anchor="nw", text=function_body, font=("", 15))
|
||||
num += 1
|
||||
self.user_function_canvas.update()
|
||||
self.user_function_canvas.configure(scrollregion=self.user_function_canvas.bbox("all"))
|
||||
|
||||
def create_form(self):
|
||||
bottom_frame = Frame(self.master, width=self.attr["width"], height=self.attr["height"] * 0.3)
|
||||
self.func_input = Entry(bottom_frame, font=self.font_style, width=40)
|
||||
self.func_input.grid(row=0, column=0, columnspan=4, padx=30)
|
||||
self.add_func_button = Button(bottom_frame, text="用户命名并新增基本函数", command=self.add_function)
|
||||
self.add_func_button.grid(row=0, column=4, padx=10)
|
||||
Label(bottom_frame, text="x步长", font=("Lucida Grande", 20)).grid(row=1, column=0, padx=30)
|
||||
self.x_step = Entry(bottom_frame, font=self.font_style, width=10)
|
||||
self.x_step.grid(row=1, column=1, padx=10, pady=10)
|
||||
Label(bottom_frame, text="x个数", font=("Lucida Grande", 20)).grid(row=1, column=2, padx=30)
|
||||
self.x_count = Entry(bottom_frame, font=self.font_style, width=10)
|
||||
self.x_count.grid(row=1, column=3, padx=10, pady=10)
|
||||
|
||||
Label(bottom_frame, text="坐标轴", font=("Lucida Grande", 20)).grid(row=2, column=0, padx=30)
|
||||
self.quadrant = IntVar()
|
||||
self.quadrant.set(4)
|
||||
Radiobutton(bottom_frame, text="四象限", command=self.update_quadrant,
|
||||
value=4, variable=self.quadrant).grid(row=2, column=1, padx=10, pady=10)
|
||||
Radiobutton(bottom_frame, text="一象限", command=self.update_quadrant,
|
||||
value=1, variable=self.quadrant).grid(row=2, column=2, padx=10, pady=10)
|
||||
Label(bottom_frame, text="x放大倍数", font=("Lucida Grande", 20)).grid(row=3, column=0, padx=30)
|
||||
self.x_scale = Entry(bottom_frame, font=self.font_style, width=10)
|
||||
self.x_scale.grid(row=3, column=1, padx=10, pady=10)
|
||||
Label(bottom_frame, text="y放大倍数", font=("Lucida Grande", 20)).grid(row=3, column=2, padx=30)
|
||||
self.y_scale = Entry(bottom_frame, font=self.font_style, width=10)
|
||||
self.y_scale.grid(row=3, column=3, padx=10, pady=10)
|
||||
Button(bottom_frame, text="输出", command=self.print_function, width=5)\
|
||||
.grid(row=1, rowspan=3, column=4, sticky="w")
|
||||
bottom_frame.place(x=0, y=self.attr["height"] * 0.65 + 20)
|
||||
|
||||
def create_window(self):
|
||||
self.axis_canvas = Graph(self.master, width=self.attr["width"] * 0.45, height=self.attr["height"] * 0.65,
|
||||
bg="#cdcdcd")
|
||||
self.axis_canvas.place(x=0, y=0)
|
||||
self.text_canvas = ExpressionCanvas(self.master, width=self.attr["width"] * 0.35,
|
||||
height=self.attr["height"] * 0.65, bg="#cdcdcd")
|
||||
self.text_canvas.place(x=int(self.attr["width"] * 0.45), y=0)
|
||||
user_function = Frame(self.master, width=self.attr["width"] * 0.2, height=self.attr["height"] * 0.65)
|
||||
user_function.place(x=int(self.attr["width"] * 0.8), y=0)
|
||||
self.user_function_canvas = Canvas(user_function, width=self.attr["width"] * 0.18,
|
||||
height=self.attr["height"] * 0.63, background="#cdcdcd")
|
||||
y_scrollbar = Scrollbar(user_function, orient="vertical", command=self.user_function_canvas.yview)
|
||||
x_scrollbar = Scrollbar(user_function, orient="horizontal", command=self.user_function_canvas.xview)
|
||||
x_scrollbar.pack(side=BOTTOM, fill=BOTH)
|
||||
y_scrollbar.pack(side=RIGHT, fill=BOTH)
|
||||
self.user_function_canvas.pack(side=TOP, fill=BOTH)
|
||||
self.user_function_canvas.config(yscrollcommand=y_scrollbar.set, xscrollcommand=x_scrollbar.set)
|
||||
for i in range(1, 200):
|
||||
self.user_function_canvas.create_text(80, i * 20 + 35, text="fdsaf{}".format(i))
|
||||
self.user_function_canvas.config(scrollregion=self.user_function_canvas.bbox("all"))
|
||||
self.create_form()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
root = Window()
|
||||
screenwidth = root.winfo_screenwidth()
|
||||
screenheight = root.winfo_screenheight()
|
||||
root_attr = {
|
||||
"width": 1200,
|
||||
"height": 800,
|
||||
}
|
||||
alignstr = '%dx%d+%d+%d' % (root_attr['width'], root_attr['height'], (screenwidth - root_attr['width']) / 2,
|
||||
(screenheight - root_attr['height']) / 2)
|
||||
root.geometry(alignstr)
|
||||
root.resizable(width=False, height=False)
|
||||
app = FunctionDisplay(root, root_attr)
|
||||
ttk.Style().configure("TButton", font="-size 18")
|
||||
ttk.Style().configure("TRadiobutton", font="-size 18")
|
||||
root.mainloop()
|
@ -0,0 +1 @@
|
||||
{"xy": "x*y", "f2": "sin(x) * cos(x)", "2sinx": "2*sin(x)", "2cosx": "2*cos(x)"}
|
@ -0,0 +1,45 @@
|
||||
import tkinter as tk
|
||||
|
||||
import numpy as np
|
||||
from matplotlib import pyplot as plt
|
||||
from matplotlib.figure import Figure
|
||||
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
||||
from mpl_toolkits.mplot3d import Axes3D
|
||||
import sympy
|
||||
from numpy import arange
|
||||
|
||||
|
||||
def plot_3d_graph():
|
||||
# 创建一个 Figure 对象
|
||||
fig = Figure(figsize=(8, 8), dpi=100, facecolor='#cdcdcd', edgecolor="#cdcdcd")
|
||||
x = arange(-19, 20, 0.1)
|
||||
y = arange(-19, 20, 0.1)
|
||||
z = []
|
||||
sympy.Symbol("x y")
|
||||
for i in arange(-19, 20, 0.1):
|
||||
z.append(sympy.sympify("x**2+y**2").subs("x", i).subs("y", i).evalf())
|
||||
# 在 Figure 上创建一个 3D 子图
|
||||
ax = fig.add_subplot(111, projection='3d')
|
||||
ax.plot3D(x, y, z, 'gray')
|
||||
|
||||
# 绘制三维图形
|
||||
# 在这里添加你自己的三维图形绘制逻辑
|
||||
# 例如 ax.plot_surface() 或 ax.scatter()
|
||||
|
||||
# 创建一个 tkinter 窗口
|
||||
root = tk.Tk()
|
||||
root.title("3D Graph")
|
||||
|
||||
# 在 tkinter 窗口上创建一个 canvas
|
||||
canvas = FigureCanvasTkAgg(fig, master=root)
|
||||
canvas.draw()
|
||||
canvas.get_tk_widget().pack()
|
||||
|
||||
# 运行 tkinter 主循环
|
||||
tk.mainloop()
|
||||
|
||||
|
||||
# 调用函数绘制三维图形
|
||||
plot_3d_graph()
|
||||
|
||||
|
Binary file not shown.
Loading…
Reference in new issue