diff --git a/20230509-函数绘制.docx b/20230509-函数绘制.docx new file mode 100644 index 0000000..d5ecb47 Binary files /dev/null and b/20230509-函数绘制.docx differ diff --git a/README.md b/README.md index bbb0afc..8ee0538 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,171 @@ -# NumericalCalculation +# 项目编程基本原则 + +## 文档及代码编辑的注意事项 + +### 进度汇报 +**查文档和编程任务是否符合。** + +首先自查一遍文档,每个编程任务是否符合文档上编程任务的要求---请在进度表中标示【自查:符合 或 不符合】,是按编程任务,不是按X1,X2... + +**查命名问题,函数框架,函数名称,和变量名规范** + +(2)按我们前两天讨论的,主函数框架、函数关系调用图、函数名称与变量名称规范化等方面回复【已优化完成,未优化完成】 + + +### 主函数框架示例: + +```python +# 需要导入的库 +import tkinter as tk # UI界面 +from tkinter import scrolledtext # 下拉框 +from tkinter import END # 下拉框自动下拉到最后一行 +import random # 随机数 +import time # 应用sleep函数睡眠 +import threading # 多线程任务 +from pymouse import PyMouse # 模拟鼠标自动点击 +import win32gui # 获取扫雷窗口信息 + +""" +所有的系统库或第三方库,一次性引出,避免因为确少库而导致运行不了。 +如果是自写的模块,可以在函数前引出 +""" + +#方式1--在文件中定义全局变量,加载全局变量定义文件(如果全局变量实在太多) +from setting import * # (setting为自定义程序存放的是全局变量)导入全局变量 +# setting中定义了哪些全局变量及其定义 +#方式2--直接定义全局变量(一般多) +GLOBAL ... ... # 常量用大写 + +########### +'此处说明是放函数定义的地方' +########### + + +# 主控函数, 将函数定义拷贝到main函数上面,将函数调用放在main函数 +if __name__ == '__main__': + pass + + ########### + '此处说明是函数调用的地方' + ########### + +""" +要求主函数框架单独运行不报错 +""" +``` + +```python +# 文档贴函数提示说明 如下图所示 +########################################################################### + +# 以func1()[编程章节]==>func2()[编程章节]的方式说明调用当前函数要用的前述函数。 +set_random_state()[5.1]==>create_boards()[5.4]==>show_window()[5.4] +``` +![img_2.png](img_2.png) + +### 根据不同情况不同的贴程序方法 + +```python +# 情况一 +##### 第一次出现init初始化方法需要写全 ##### +##### 在类里面增添方法,需要将类名写出,再写方法 ##### +X2: +class MineSweeper: # 建立扫雷类 + def __init__(self): # 第一次出现init初始化方法需要写全, + def f2(): + pass + +# 情况二 +##### 如果函数实在太多,需要标出函数调用关系图,同时将贴合编程要求的程序给出 ##### +X3: # f1(), f2(), f3(), f4(), ..., f10() 本段仅给出f4(), 完整的在[位置](可以在文档末尾建立附录)。 +Class Class_some + def f4(): 体现核心功能的函数给出。 + pass + +# 情况三 +##### 如果需要在后续编程章节中扩充原有的功能,可以扩写函数,##### +##### 而且不要出现已经出现过的代码,已经出现过的代码用...代指 ##### +X4: 【友好阅读性原则;最小重复代码原则;递增式代码表达原则;界面元素处理与业务逻辑完全分开原则】 +class MineSweeper: # 扩展扫雷类--在X2基础上进一步定义 + # _init_(self), f2(), 一并处理 + def _init_(self): #在原函数基础上增加/调整 + ... + self.par = "example" + def f3(): + ... + def f4(): + ... + +每个函数不超一页,原则上就是半页。 + +``` + +## 项目编程的普遍要求 + +### GUI界面和数据结构分开 + +借用前后端分离的架构模式。 + +GUI界面负责处理用户界面的展示和交互,数据结构部分负责处理业务逻辑和数据处理。 + +将GUI界面负和数据结构分为两个独立的部分,各自负责不同的功能和任务。GUI界面通过接口函数与更新和加载数据结构。 + +### 代码命名规则: + +在Python中,有一些命名规则和约定被广泛遵循,以提高代码的可读性和一致性。 + +变量和函数名:使用小写字母,单词之间使用下划线(snake_case)进行分隔。例如:my_variable, calculate_result。 + +常量:使用全大写字母,单词之间使用下划线进行分隔。例如:MAX_VALUE, PI。 + +类名:使用驼峰命名法(CamelCase),即首字母大写,不使用下划线。例如:MyClass, Calculator。 + +模块名:使用小写字母,单词之间使用下划线进行分隔。例如:my_module, utils。 + +- 首先是做好程序的优化,即做好程序函数的封装。 每个函数的函数名尽可能符合文档的编程任务及其要求--要让读者看到函数名就能想到编程任务,这样会更易于理解。 + +- 函数调用关系图的理解是正确的,就是要有一张全局关系图。 + +- 正确理解X1、X2、X3、X4和X5。通常,X1仅是围绕数据结构赋值,和界面或者界面元素坐标无关。X2是依据数据结构中的数据做输出,通常是仅输出。X3通常可以定义函数,或者定义类及其中的函数。X4是在X2基础上做界面交互元素,同时将界面交互元素相关的事件/消息绑定X3或X5的函数。X5也是一些函数。这样界面逻辑和业务逻辑是完全分离的。 + +- 注意:自前向后,编程是越来越复杂,但不要用后面的编程直接替代前面的编程任务中的程序,前面是简单的后面是复杂的。例如“Select Sno from Student”,这是前面的编程,Select :attr1 from :table1",尽管将attr1赋值成Sno,将table1赋值成Student,也能实现前面的语句,但不可用后面的替换前面的。 + + +### 函数调用关系图: + +函数调用图(Function Call Graph)是用于描述程序中函数之间调用关系的图形表示。它展示了函数之间的依赖关系和调用流程,帮助我们理解程序的执行流程和函数之间的交互 + +在函数调用图中,函数被表示为节点,函数之间的调用关系被表示为边。每个函数调用都会生成一个新的节点和一条连接调用者和被调用者的边。函数调用图可以是有向图或无向图,具体取决于函数调用的方向性。 + +调用图可以用软件生成。 + +![img.png](img.png) + +### 项目文件应包含(后续规范) +项目应该包含以下基本文件: + +1. README.md:项目的说明文档,包含项目的介绍、使用方法、安装指南、贡献指南等重要信息。README.md通常是其他开发者了解和使用项目的入口。 + +2. LICENSE:项目的开源许可证,明确了项目的使用条件和权利限制。选择适合项目的开源许可证对于保护项目的权益和推动开源合作非常重要。 + +3. .gitignore:Git版本控制系统的忽略文件配置,用于指定哪些文件或目录应该被忽略,不纳入版本控制。通常包括一些编译生成的文件、临时文件、敏感信息等。 + +4. requirements.txt:项目的依赖项清单,列出了项目所需的外部库、框架和工具的版本信息。这样其他开发者可以方便地安装相同的依赖项,确保项目的可重复性和一致性。 + +5. setup.py 或者 setup.cfg:用于打包和发布项目的配置文件。可以定义项目的元数据、依赖关系、安装过程等,以便其他人能够方便地安装和使用项目。 + +6. docs 目录:包含项目的文档,例如用户手册、API文档、开发者指南等。良好的文档对于其他开发者和用户理解和使用项目非常重要。 + +7. tests 目录:包含项目的测试代码和测试数据,用于验证项目的正确性和稳定性。包括单元测试、集成测试等,帮助开发者确保项目的质量和可靠性。 + +8. src 或者 lib 目录:包含项目的源代码文件。根据项目的规模和结构,可以进一步组织成子目录或包,方便代码的组织和维护。 + +除了上述基本文件,根据项目的特点和需求,还可以包含其他文件,如配置文件、示例代码、演示视频等。重要的是根据项目的具体情况进行文件的组织和描述,确保项目的可理解性、可维护性和可扩展性。 + +```bash +# 将输出重定向到文件: +pip freeze > requirements.txt +# 老师一键安装项目所有依赖,一般像Pycharm会自动识别这个文件,按提示安装即可 +pip install -r requirements.txt +``` diff --git a/function.py b/function.py new file mode 100644 index 0000000..e233bbe --- /dev/null +++ b/function.py @@ -0,0 +1,404 @@ +import random +import math +import numpy as np +import tkinter as tk +from math import * +from tkinter import * +from tkinter import messagebox +import matplotlib.pyplot as plt +import numpy.ma +import pyautogui as ag +from numpy import arange + + +def leftclick(event): # 测试鼠标点击 + x, y = event.x, event.y + print('坐标{}, {}'.format(x, y)) + + +def window_open(width, height): + X, Y = ag.size() # 获取分辨率 + winSize = str(width) + "x" + str(height) + # print("winsize",winSize) + wiarangeos = winSize + "+" + str((X - width) // 2) + wiarangeos += "+" + str((Y - height) // 2) + # print("wiarangeos",wiarangeos) + # window.geometry(wiarangeos) #指定窗口大小 + # window.resizable(False, False) #窗口不可调 + title = u'桌面分辨率:' + str(X) + "x" + str(Y) + title += ' ' * 5 + u'窗体大小:' + winSize + window.title(title) + # window.update() + + +def x1_1(): # 随机给出1000个点坐标,赋值给xyMap, + shape = [1000, 2] + xyMap = np.random.randint(-200, 200, size=shape) + return xyMap + +print(x1_1()) + + +# ab = x1_1() +# print(ab,len(ab),type(ab)) + +def x1_2(problemIndex): # 程序给出任一函数 y=f(x)计算x=1到1000的y值,赋值给xyMap + # shape = [1000,2] #x值为1到1000,y为其对应函数值 + xyMap = np.random.rand(1000, 2) + if problemIndex == 1: + for x in range(1, 1001): + xyMap[x - 1][0] = x # xyMap序号为0-999 + xyMap[x - 1][1] = np.sin(x) + return xyMap + elif problemIndex == 2: + for x in range(1, 1001): + xyMap[x - 1][0] = x + xyMap[x - 1][1] = np.cos(x) + return xyMap + elif problemIndex == 3: + for x in range(1, 1001): + xyMap[x - 1][0] = x + xyMap[x - 1][1] = np.tan(x) + return xyMap + elif problemIndex == 4: + for x in range(1, 1001): + xyMap[x - 1][0] = x + xyMap[x - 1][1] = x * 2 + 2 + return xyMap + + +print(x1_2(4)[:50]) +print(x1_2(1)) + +def x1_3(problemIndex): # 程序给出任一函数y=f(x)计算x=-500到+500的y值赋值给xyMap + c = 0 # 此处x赋值为-500到-1到,1到500到1000个 + shape = [1000, 2] # y赋值为其对应的函数 + xyMap = np.random.rand(1000, 2) + if problemIndex == 1: + for x in range(-500, 500): + if (x == 0): + continue + xyMap[c][0] = x + xyMap[c][1] = math.sin(x) + c += 1 + return xyMap + elif problemIndex == 2: + for x in range(-500, 500): + if (x == 0): + continue + xyMap[c][0] = x + xyMap[c][1] = math.cos(x) + c += 1 + return xyMap + elif problemIndex == 3: + for x in range(-500, 500): + if (x == 0): + continue + xyMap[c][0] = x + xyMap[c][1] = math.tan(x) + c += 1 + return xyMap + elif problemIndex == 4: + for x in range(-500, 501): + if (x == 0): + continue + xyMap[c][0] = x + xyMap[c][1] = x * 2 + 2 + c += 1 + return xyMap + + +# print(x1_3(1)) + +def x1_4(ProblemIndex): + step = 3 # 步长 + y = [] + i, c = 0, 0 + number = 1000 # 个数 + while (True): + if (c == number): + break + else: + y.append(i) + i += step + c += 1 + y = np.array(y) + xyMap = np.random.rand(1000, 2) + + if (ProblemIndex == 1): + for i in range(len(y)): + xyMap[i][0] = y[i] + xyMap[i][1] = 2 * y[i] + 1 + return xyMap + elif (ProblemIndex == 2): + for i in range(len(y)): + xyMap[i][0] = y[i] + xyMap[i][1] = y[i] / 3 + return xyMap + + +def x_6(xymap): # 按xymap绘制曲线 + xy = xymap.tolist() + canvas.create_line(xy, fill="purple", width=1) + + +def x_5(x, y): + # 1----x,y轴 + canvas.create_line(0, y / 2, x, y / 2, fill='white', arrow=LAST) # 绘制x轴 + canvas.create_line(x / 2, 0, x / 2, y, fill='white', arrow=FIRST) # 绘制y轴 + + +# 绘制坐标轴的刻度 +def kedu(): + # #两个for分别为xy刻度 + for i in range(-math.ceil((loca_y / bili_x)) + 1, math.ceil((canvas_x - loca_y) / bili_x)): + j = i * bili_x + # i*bili_x即实际的坐标轴对应的放大bili倍后的值,但是这个值并不是真正的像素点 + # 以中心点(loca_y, loca_x)为原点的,要在这里加上对应的原点像素值才能得到真正的坐标轴对应的像素点 + if (i % 10 == 0): + canvas.create_line(j + loca_y, loca_x, j + loca_y, loca_x - 5, fill='black') + canvas.create_text(j + loca_y, loca_x + 10, text=i) + # y轴是相反的,所以正半轴为:cordinate_x,负半轴为canvas_y - cordinate_x + # 此处的算法应该改为range*( -(canvas_y - loca_x) / bili_y) + 1, (cordinate_x / bili_y)) ) + # xmin,xmax =math.ceil((loca_y / bili_x)) + 1, math.ceil((canvas_x-loca_y) / bili_x) + # print(xmin,xmax) + + for i in range(-math.ceil((canvas_y - loca_x) / bili_y) + 1, math.ceil((loca_x / bili_y))): + j = -(i * bili_y) + # i*bili_y即实际的坐标轴对应的放大bili倍后的值,但是这个值并不是真正的像素点 + # 以中心点(loca_y, loca_x)为原点的,因此在这里加上对应的原点像素值才能得到真正的坐标轴对应的像素点 + if (i == 0): + continue + elif (i % 2 == 0): + canvas.create_line(loca_y, j + loca_x, loca_y + 5, j + loca_x, fill='black') + canvas.create_text(loca_y - 10, j + loca_x, text=i) + # print(loca_x,loca_y) + + +def si(): + global x0, y0, loca_x, loca_y + x0, y0 = 375, 275 # 四象限原点坐标 + loca_x, loca_y = y0, x0 + canvas.create_rectangle(0, 0, x_zhou, y_zhou, fill='#CDCDCD', width=5, outline='#CDCDCD') + + canvas.create_oval(170, y_zhou + 145, 200, y_zhou + 175, fill='black', width=1, outline='white') + canvas.create_oval(350, y_zhou + 145, 380, y_zhou + 175, fill='white', width=1, outline='black') + # kedu() + canvas.create_line(0, y0, x_zhou, y0, fill='white', arrow=LAST) # 一象限xy轴 + canvas.create_line(x0, 0, x0, y_zhou, fill='white', arrow=FIRST) + + +def yi(): + global x0, y0, loca_x, loca_y + x0, y0 = 20, 500 # 一象限原点 + loca_x, loca_y = y0, x0 + canvas.create_rectangle(0, 0, x_zhou, y_zhou, fill='#CDCDCD', width=5, outline='#CDCDCD') + + canvas.create_oval(170, y_zhou + 145, 200, y_zhou + 175, fill='white', width=1, outline='black') + canvas.create_oval(350, y_zhou + 145, 380, y_zhou + 175, fill='black', width=1, outline='white') + # kedu() + canvas.create_line(0, y0, x_zhou, y0, fill='white', arrow=LAST) # 一象限xy轴 + canvas.create_line(x0, 0, x0, y_zhou, fill='white', arrow=FIRST) + + +def x_show(x, y): # GUI界面 坐标系的x:x轴长,y:y轴长 + xmin, xmax = math.ceil((loca_y / bili_x)) + 1, math.ceil((canvas_x - loca_y) / bili_x) + # print("xmain",xmin,xmax) + # 1----x,y轴 + # canvas.create_line(0,y0,x,y0,fill='white',arrow=LAST) # 一象限xy轴 + # canvas.create_line(x0,0,x0,y,fill='white',arrow=FIRST) + + # 2----背景涂白 + canvas.create_rectangle(x, 0, 1200, y, fill='white', width=5, outline='white') # 右上区白色背景 + canvas.create_rectangle(0, y, 1200, 800, fill='white', width=3, outline='white') # 下半区白色背景 + + # 3----输入框 + e1 = tk.Entry(window, font=('Arial', 18), width=40, bg='#C0D9D9', bd=1) # 用户自命名函数输入 + e1.place(x=70, y=y + 15, height=50) + canvas.create_text(95, y + 100, text="x步长", fill='black', font=("Purisa", 15, "bold")) + e2 = tk.Entry(window, font=('Arial', 15), width=15, bg='#C0D9D9', bd=1) # x步长输入 + e2.place(x=130, y=y + 75, height=50) + canvas.create_text(380, y + 100, text="x个数", fill='black', font=("Purisa", 15, "bold")) + e3 = tk.Entry(window, font=('Arial', 15), width=15, bg='#C0D9D9', bd=1) # x个数输入 + e3.place(x=415, y=y + 75, height=50) + + e4 = tk.Entry(window, font=('Arial', 18), width=10, bg='#C0D9D9', bd=1) # x放大倍数 + e4.place(x=200, y=750, height=35) + canvas.create_text(95, y + 100, text="x步长", fill='black', font=("Purisa", 15, "bold")) + e5 = tk.Entry(window, font=('Arial', 15), width=10, bg='#C0D9D9', bd=1) # y放大倍数 + e5.place(x=580, y=750, height=35) + + # 4----右侧label文本 + canvas.create_text(x + 180, 35, text='可识别基本函数', fill='red', font=("Purisa", 25, "bold")) + text = "可支持下列函数的加减乘除组合运算\n" \ + "y=sin(x)\n" \ + "y=cos(x)\n" \ + "y=tan(x)\n" \ + "y=cot(x)\n" \ + "y=x^n\n" \ + "y=P1(y=x^3+x^2+x+5)\n" \ + "---将括号内的表达式命名为P1,P1可\n" \ + "出现于用户构造中" + label = tk.Label(window, text=text, font=('Arial', 15, 'bold italic'), bg='white', + # 设置标签内容区? + width=28, height=16, + # 设置填充区距离、边框宽度和其样式(凹陷式) + padx=5, pady=5, borderwidth=5, relief="sunken", justify="center") + label.place(x=x + 5, y=60) + + # 5----左下按钮 + canvas.create_text(100, y + 160, text="坐标轴", fill='black', font=("Purisa", 18, "bold")) + canvas.create_text(120, y + 210, text="x放大倍数", fill='black', font=("Purisa", 18, "bold")) + canvas.create_text(500, y + 210, text="y放大倍数", fill='black', font=("Purisa", 18, "bold")) + canvas.create_oval(170, y + 145, 200, y + 175, fill='white', width=1, outline='black') # 左小圆 + canvas.create_oval(350, y + 145, 380, y + 175, fill='white', width=1, outline='black') # 右小圆 + + def function_user(): + if (e1.get() == "y=sin(x)"): + Graph(0, sin, x0, y0, -xmin, xmax, 750 / 2, 300 / 2, c='#32CD32') + elif (e1.get() == "y=cos(x)"): + Graph(0, fx2, x0, y0, -xmin, xmax, 750 / 2, 300 / 2, c='#238E23') + elif (e1.get() == "y=tan(x)"): + Graph(0, fx3, x0, y0, -xmin, xmax, 750 / 2, 300 / 2, c='#FF6EC7') + elif (e1.get() == "y=cot(x)"): + Graph(0, fx4, x0, y0, -xmin, xmax, 750 / 2, 300 / 2, c='#CFB53B') + elif (e1.get() == "y=sin(x)+cos(x)"): + Graph(0, fx5, x0, y0, -xmin, xmax, 750 / 2, 300 / 2, c='#99CC32') + elif (e1.get() == "y=sin(x)*cos(x)"): + Graph(0, fx6, x0, y0, -xmin, xmax, 750 / 2, 300 / 2, c='#007FFF') + elif (e1.get() == "y=sin(x)-cos(x)+tan(x)"): + Graph(0, fx7, x0, y0, -xmin, xmax, 750 / 2, 300 / 2, c='#007FFF') + elif (e1.get() == "y=x"): + Graph(0, fx8, x0, y0, -xmin, xmax, 750 / 2, 300 / 2, c='#8E2323') + elif (e1.get() == "y=x**2"): + Graph(0, fx9, x0, y0, -xmin, xmax, 750 / 2, 300 / 2, c='#8E2323') + + def function_print(): + global bili_x, bili_y + bili_x = float(e4.get()) + bili_y = float(e5.get()) + buchang = float(e2.get()) + kedu() + if (e1.get() == "y=sin(x)"): + Graph(buchang, sin, x0, y0, -xmin, xmax, 750 / 2, 300 / 2, c='#32CD32') + elif (e1.get() == "y=cos(x)"): + Graph(buchang, fx2, x0, y0, -xmin, xmax, 750 / 2, 300 / 2, c='#238E23') + elif (e1.get() == "y=tan(x)"): + Graph(buchang, fx3, x0, y0, -xmin, xmax, 750 / 2, 300 / 2, c='#FF6EC7') + elif (e1.get() == "y=cot(x)"): + Graph(buchang, fx4, x0, y0, -xmin, xmax, 750 / 2, 300 / 2, c='#CFB53B') + elif (e1.get() == "y=sin(x)+cos(x)"): + Graph(buchang, fx5, x0, y0, -xmin, xmax, 750 / 2, 300 / 2, c='#99CC32') + elif (e1.get() == "y=sin(x)*cos(x)"): + Graph(buchang, fx6, x0, y0, -xmin, xmax, 750 / 2, 300 / 2, c='#007FFF') + elif (e1.get() == "y=sin(x)-cos(x)+tan(x)"): + Graph(buchang, fx7, x0, y0, -xmin, xmax, 750 / 2, 300 / 2, c='#007FFF') + elif (e1.get() == "y=x"): + Graph(buchang, fx8, x0, y0, -xmin, xmax, 750 / 2, 300 / 2, c='#8E2323') + elif (e1.get() == "y=x**2"): + Graph(buchang, fx9, x0, y0, -xmin, xmax, 750 / 2, 300 / 2, c='#8E2323') + + # 按钮一:命名函数 + button = tk.Button(window, text='用户命名并新增基本函数', bg='#38B0DE', command=function_user, + font=("Purisa", 13, "bold"), foreground='black') + button.place(x=x + 8, y=y + 10, width=440, height=60) + # 按钮二:输出 + button = tk.Button(window, text='输出', font=("Purisa", 13, "bold"), command=function_print, foreground='black') + button.place(x=x + 10, y=y + 90, width=70, height=60) + # 按钮三:切换四象限 + button = tk.Button(window, command=si, text='四象限', bd=0, font=("Asria", 15, "bold")) + button.place(x=230, y=y + 140, width=70, height=45) + # 按钮四:切换一象限 + button = tk.Button(window, command=yi, text='一象限', bd=0, font=("Asria", 15, "bold")) + button.place(x=400, y=y + 140, width=70, height=45) + + +def Graph(draw_precision, func, x0, y0, xmin, xmax, w, h, c='blue'): # 画基本函数图像 + 'xmin,xmax 自变量的取值范围; c 图像颜色' + 'x0,y0 原点坐标 w,h 横纵轴半长 draw_precision 步进' + w1, w2 = bili_x, bili_y # w1,w2为自变量和函数值在横纵轴上的放大倍数 + # w,h = 750/2,550/2 + w, h = x_zhou, y_zhou + xmin, xmax = -math.ceil((loca_y / bili_x)) + 1, math.ceil((canvas_x - loca_y) / bili_x) + # xmax = math.ceil((canvas_x-loca_y) / bili_x) + co2 = [] + count = 0 + for x in arange(xmin, xmax, draw_precision): # draw_precision----画图精度 + y = func(x) + # print(x,y) + # co2.append((x0+w1*x,y0-w2*y)) + coord = x0 + w1 * x, y0 - w2 * y, x0 + w1 * x + 1, y0 - w2 * y + 1 + + # coord = x0+w1*x,y0-w2*y + # coord2 = x0+w1*x+1,y0-w2*y+1 + # if abs(x*w1)= 1): + canvas.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: + canvas.create_line(co2[i], co2[i + 1], fill=c, width=1) + print("min,max:", xmin, xmax) + # print(co2,type(co2),len(co2)) #x个数 + # canvas.create_line(co2,fill=c,width=1) + # canvas.create_line(co2[:20],fill=c,width=1) + + canvas.update() + + +# y=tan(x) +# y=sin(x) +fx1 = lambda x: sin(x) +print(fx1) +fx2 = lambda x: cos(x) +fx3 = lambda x: tan(x) +fx4 = lambda x: (1 / tan(x)) # cot +fx5 = lambda x: sin(x) + cos(x) +fx6 = lambda x: sin(x) * cos(x) +fx7 = lambda x: sin(x) - cos(x) + tan(x) +fx8 = lambda x: x +fx9 = lambda x: (x ** 2) + +width, height = 1200, 800 # 画布与窗口大小相同 +x0, y0 = 375, 275 # 四象限原点坐标 +# x0,y0 = 20,500 #一象限原点坐标 +# xmin,xmax = 10,10 #自变量最大值 +x_zhou, y_zhou = 750, 550 + +# canvas_x ,canvas_y = 800,480 #x,y轴长 +canvas_x, canvas_y = x_zhou, y_zhou +origin = (x0, y0) # 原点的x, y像素值 +loca_x = origin[1] # 坐标轴 x (在y轴上的)的位置 +loca_y = origin[0] # 坐标轴 y (在x轴上的)的位置 +bili_x, bili_y = 10, 10 # x20尺度,y40尺度 +# draw_precision = 0.01 #画图精度为Δx + + +if __name__ == '__main__': + window = tk.Tk() + canvas = tk.Canvas(window, + bg='#CDCDCD', + height=800, + width=1200) + canvas.pack() + screenwidth = window.winfo_screenwidth() # 居中显示 + screenheight = window.winfo_screenheight() + size_geo = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2) + window.geometry(size_geo) + window.resizable(False, False) + window_open(width, height) + + x_show(x_zhou, y_zhou) # GUI界面 + + window.bind("", leftclick) # 输出鼠标点击坐标 + window.mainloop() diff --git a/functionUtil.py b/functionUtil.py new file mode 100644 index 0000000..09e52e3 --- /dev/null +++ b/functionUtil.py @@ -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 diff --git a/issues.md b/issues.md new file mode 100644 index 0000000..e9e2646 --- /dev/null +++ b/issues.md @@ -0,0 +1,21 @@ +### 【编程2.8】编制程序:解析用户输入的函数形式,构造语法树显示函数的计算过程。 + +【目的及编程说明】读者可通过编程2.8实现将函数组合解析为语法树的过程,解析用户输入的函数形式,构造语法树显示函数的计算过程。具体实现要求为:数值节点为x,运算符节点为加减乘除以及可识别计算的基本函数(界面右侧列表给出的)。系统会维护一个可参与表达式构造的函数形式及其可执行函数的列表。通过表达式解析,可以识别出具体的函数形式,并构建表达式语法树,展示用户输入函数表达式的计算过程。 + +### **【编程2.10】编制程序:数值积分** + +【目的及编程说明】读者通过编程2.10可理解数值积分,可用梯形法实现(还可以用矩形)。具体实现要求为:(1)定义函数表达式,积分区间,步长;(2)通过梯形法实现小步长梯形面积的累加 + +### **【编程2.11】编制程序:数值微分** + +【目的及编程说明】读者通过编程2.11可理解数值微分/求导的实现过程,使用中心差分计算导数(还可以用前向差分)。具体实现要求为:(1)定义求导函数表达式,步长;(2)定义求导点;(3)使用中心差分计算公式计算。 + +### **【编程2.12】编制程序:牛顿迭代法** + +【目的及编程说明】读者通过编程2.11可理解牛顿迭代法的含义。牛顿迭代法是找到函数值为0的解。具体实现要求为:(1)定义函数表达式,起始迭代点,迭代次数,迭代精度;(2)使用牛顿法计算起始迭代点的函数值,判断此函数值与迭代点的差值是否小于迭代精度,若小于则停止返回,若大于,按照步长往下移动迭代点,直到迭代次数完毕;(3)返回解值。 + + + +# 待改进 +- [X] 函数校验 +- [X] 用户自定义函数的显示 \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000..da19dc3 --- /dev/null +++ b/test.py @@ -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) \ No newline at end of file diff --git a/x3/FunctionDisplay_X3.py b/x3/FunctionDisplay_X3.py new file mode 100644 index 0000000..084f5cd --- /dev/null +++ b/x3/FunctionDisplay_X3.py @@ -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("", 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() diff --git a/x3/function.json b/x3/function.json new file mode 100644 index 0000000..8c369d5 --- /dev/null +++ b/x3/function.json @@ -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)"} \ No newline at end of file diff --git a/x3/test.py b/x3/test.py new file mode 100644 index 0000000..b81dcd4 --- /dev/null +++ b/x3/test.py @@ -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")) \ No newline at end of file diff --git a/x3/x3_210.py b/x3/x3_210.py new file mode 100644 index 0000000..71d453c --- /dev/null +++ b/x3/x3_210.py @@ -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) \ No newline at end of file diff --git a/x3/x3_211.py b/x3/x3_211.py new file mode 100644 index 0000000..706fb57 --- /dev/null +++ b/x3/x3_211.py @@ -0,0 +1,24 @@ +def central_difference(f, x, h): + """ + 中心差分法进行数值微分(计算导数) + + 参数: + - f: 要求导数的函数 + - x: 求导点的横坐标 + - h: 步长 + + 返回: + - 导数的近似值 + """ + df = (f(x + h) - f(x - h)) / (2 * h) # 中心差分法计算导数 + + return df + +def f(x): + return x**2 + +x = 2 # 求导点的横坐标 +h = 0.01 # 步长 + +df = central_difference(f, x, h) +print("导数的近似值:", df) \ No newline at end of file diff --git a/x3/x3_212.py b/x3/x3_212.py new file mode 100644 index 0000000..8ed370b --- /dev/null +++ b/x3/x3_212.py @@ -0,0 +1,37 @@ +def newton_method(f, f_prime, x0, tol=1e-6, max_iter=100): + """ + 牛顿迭代法计算函数值为0的解 + + 参数: + - f: 要求解的函数 + - f_prime: 函数的导数 + - x0: 初始猜测值 + - tol: 容差(迭代停止的条件,可选,默认为1e-6) + - max_iter: 最大迭代次数(可选,默认为100) + + 返回: + - 解的近似值 + """ + x = x0 # 初始猜测值 + for _ in range(max_iter): + fx = f(x) # 函数值 + if abs(fx) < tol: + # 已达到容差要求,返回解的近似值 + return x + fpx = f_prime(x) # 函数的导数值 + if abs(fpx) < tol: + # 导数值过小,无法继续迭代 + break + x -= fx / fpx # 牛顿迭代公式 + # 迭代未收敛或超过最大迭代次数,返回None表示未找到解 + return None + +def f(x): + return x**2 - 4 + +def f_prime(x): + return 2 * x + +x0 = 1 # 初始猜测值 +solution = newton_method(f, f_prime, x0) +print("解的近似值:", solution) \ No newline at end of file diff --git a/x4/FunctionDisplay_X4.py b/x4/FunctionDisplay_X4.py new file mode 100644 index 0000000..384835c --- /dev/null +++ b/x4/FunctionDisplay_X4.py @@ -0,0 +1,395 @@ +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, 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: + 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 clear_text_canvas(self, event): + def clear_text(): + self.text_canvas.delete("all") + self.text_canvas.create_text(20 + 180, 20, text='可识别基本函数', fill='red', font=("Purisa", 25, "bold")) + center_text(self.text_canvas, self.text, ("Lucida Grande", 15)) + self.menu = Menu(self, tearoff=False) + self.menu.add_command(label="重置", command=clear_text) + self.menu.post(event.x_root, event.y_root) + + 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): + """ + 输出 + """ + 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: + 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) + 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(right_var, step, count, + x_scale, y_scale) + self.text_canvas.draw_expression(sympy.sympify(right_var, evaluate=False, convert_xor=True), int(self.text_canvas.cget("width")) // 2, self.text_canvas.y_spacing) + 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) + 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(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 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("", 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") + menubar = Menu(root) + menubar.add_command(label='查看自定义函数', command=self.show_user_function) + root.config(menu=menubar) + # self.text = "可支持下列函数的加减乘除组合运算\n" \ + # "常用运算符:+,-,*,/,**,//,%\n" \ + # "常用函数:\n" \ + # " sqrt(x):求平方根\n" \ + # "数学常数:\n" \ + # " 虚数单位:I\n" \ + # " 自然对数的底:E\n" \ + # " 无穷大:oo\n" \ + # " 圆周率:pi\n" \ + # "三角函数:sin(x),cos(x),tan(x)\n" \ + # " sec(x),csc(x),cot(x),sinc(x)\n" \ + # " 及其反函数:sinh(),cosh()等\n" \ + # "复杂函数:\n" \ + # " 伽马函数:gamma(x)\n" \ + # " 贝塔函数:beta()\n" \ + # " 误差函数:erf(x)\n" \ + # "指数运算:\n" \ + # " 指数运算:exp()\n" \ + # " 自然对数:log()\n" \ + # " 以十为底的对数:log(var, 10)\n" \ + # " 自然对数:ln()或log()\n" + self.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, 20, text='可识别基本函数', fill='red', font=("Purisa", 25, "bold")) + center_text(self.text_canvas, self.text, ("Lucida Grande", 15)) + self.text_canvas.bind("", func=self.clear_text_canvas) + 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() diff --git a/x4/function.json b/x4/function.json new file mode 100644 index 0000000..a20339e --- /dev/null +++ b/x4/function.json @@ -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)" +} \ No newline at end of file diff --git a/x4/test.py b/x4/test.py new file mode 100644 index 0000000..b81dcd4 --- /dev/null +++ b/x4/test.py @@ -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")) \ No newline at end of file diff --git a/x5_218/FunctionDisplay_X5.py b/x5_218/FunctionDisplay_X5.py new file mode 100644 index 0000000..9e20e94 --- /dev/null +++ b/x5_218/FunctionDisplay_X5.py @@ -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() diff --git a/x5_218/function.json b/x5_218/function.json new file mode 100644 index 0000000..85437d8 --- /dev/null +++ b/x5_218/function.json @@ -0,0 +1 @@ +{"xy": "x*y", "f2": "sin(x) * cos(x)", "2sinx": "2*sin(x)", "2cosx": "2*cos(x)"} \ No newline at end of file diff --git a/x5_218/test.py b/x5_218/test.py new file mode 100644 index 0000000..3155f6f --- /dev/null +++ b/x5_218/test.py @@ -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() + + diff --git a/函数显示文档.docx b/函数显示文档.docx new file mode 100644 index 0000000..ed52bde Binary files /dev/null and b/函数显示文档.docx differ diff --git a/函数绘制.docx b/函数绘制.docx new file mode 100644 index 0000000..2fed1d0 Binary files /dev/null and b/函数绘制.docx differ