You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
358 lines
13 KiB
358 lines
13 KiB
# -*- encoding: utf-8 -*-
|
|
"""
|
|
@Author: packy945
|
|
@FileName: TreeNode.py
|
|
@DateTime: 2023/5/15 14:27
|
|
@SoftWare: PyCharm
|
|
"""
|
|
|
|
import re
|
|
import tkinter as tk
|
|
from data import *
|
|
import tkinter.messagebox
|
|
|
|
|
|
sz = []
|
|
A = Aexp([0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0])
|
|
img_open = []
|
|
img_png = []
|
|
|
|
class Tree:
|
|
def __init__(self):
|
|
self.bc = 40
|
|
self.Node = []
|
|
self.rootID = None
|
|
self.deepth = []
|
|
self.place = []
|
|
self.mark = [0] * 110
|
|
self.ck = []
|
|
|
|
def check(self):
|
|
self.ck = [1] * len(self.Node)
|
|
for N in self.Node:
|
|
if N[3]:
|
|
self.ck[N[3]] = 0
|
|
if N[4]:
|
|
self.ck[N[4]] = 0
|
|
|
|
def remark(self):
|
|
self.mark = [0] * 110
|
|
|
|
|
|
|
|
def deep(self):
|
|
flag = 0
|
|
for i in range(len(self.Node)):
|
|
if self.Node[i][1] == 'Value':
|
|
self.deepth[i] = 0
|
|
|
|
for i in range(len(self.Node)):
|
|
if self.deepth[i] == -1:
|
|
flag = 1
|
|
if self.Node[i][3] != None and self.Node[i][4] != None:
|
|
# print(self.Node[i][3], self.Node[i][4])
|
|
self.deepth[i] = max(self.deepth[self.Node[i][3]], self.deepth[self.Node[i][4]]) + 1
|
|
# print(self.deepth)
|
|
if flag:
|
|
self.deep()
|
|
|
|
def calculate(self, node):
|
|
print(node)
|
|
if node[1] == 'Value':
|
|
return node[5] * 1.0
|
|
elif node[1] == 'Operator':
|
|
a = self.calculate(self.Node[node[3]])
|
|
b = self.calculate(self.Node[node[4]])
|
|
if node[2] == '^':
|
|
return pow(int(a), int(b))
|
|
else:
|
|
# print(a,node[2],b)
|
|
return eval(str(a) + node[2] + str(b))
|
|
|
|
def Aexp(self, node):
|
|
|
|
if node[1] == 'Value':
|
|
return str(node[5])
|
|
elif node[1] == 'Operator':
|
|
a = self.Aexp(self.Node[node[3]])
|
|
b = self.Aexp(self.Node[node[4]])
|
|
# print(a,node[2],b)
|
|
ans = '(' + str(a) + node[2] + str(b) + ')'
|
|
# print(ans)
|
|
return ans
|
|
|
|
|
|
tree = Tree()
|
|
|
|
|
|
cur = 0
|
|
class TreeNode:
|
|
def __init__(self, Type, value, left=None, right=None, color='方块'):
|
|
global cur
|
|
self.NodeID = cur # 节点编号
|
|
cur += 1
|
|
self.NodeType = Type # 节点类型
|
|
self.Ops = None # 运算符类型
|
|
self.left = left
|
|
self.right = right
|
|
self.LeftNodeID = None
|
|
self.RightNodeID = None
|
|
if self.left:
|
|
self.LeftNodeID = left.NodeID # 左节点编号
|
|
if self.right:
|
|
self.RightNodeID = right.NodeID # 右节点编号
|
|
self.FaceValue = None # 节点数值
|
|
self.FaceColor = color # 节点花色
|
|
if Type == 'Value':
|
|
self.FaceValue = value
|
|
else:
|
|
self.Ops = value
|
|
|
|
tree.Node.append([self.NodeID, self.NodeType, self.Ops, self.LeftNodeID, self.RightNodeID, self.FaceValue, self.FaceColor])
|
|
|
|
|
|
|
|
def build_ast(formula):
|
|
'''
|
|
建立语法树
|
|
:param formula: 表达式,表达式的花色
|
|
:return: 建立好的语法树
|
|
'''
|
|
# 去掉空格
|
|
|
|
global tree, cur
|
|
try:
|
|
expr, color = formula
|
|
except:
|
|
expr = formula
|
|
color = ['方块'] * 20
|
|
if len(expr) == 0:
|
|
tk.messagebox.showinfo('', '表达式不存在')
|
|
return
|
|
cur = 0
|
|
tree.__init__()
|
|
|
|
expr = expr.replace(' ', '')
|
|
|
|
# 将所有数字和符号分离出来
|
|
tokens = re.findall(r'\d+|[()+\-*/^.]', expr)
|
|
i = 0
|
|
for token in tokens:
|
|
if tokens[i] == '.':
|
|
|
|
tokens[i - 1] = tokens[i - 1] + tokens[i] + tokens[i + 1]
|
|
del tokens[i: i + 2]
|
|
i += 1
|
|
|
|
# 定义优先级
|
|
precedence = {'+': 1, '-': 1, '*': 2, '/': 2, '^': 3}
|
|
|
|
# 创建一个操作符栈和一个节点栈
|
|
op_stack = []
|
|
node_stack = []
|
|
|
|
color_cur = 0
|
|
# 遍历所有 token
|
|
for token in tokens:
|
|
if token.isdigit():
|
|
# print(cur, color[cur])
|
|
node_stack.append(TreeNode('Value', int(token), color=color[color_cur]))
|
|
color_cur += 1
|
|
elif token.count('.') == 1:
|
|
node_stack.append(TreeNode('Value', float(token), color=color[color_cur]))
|
|
color_cur += 1
|
|
elif token in '+-*/^':
|
|
while op_stack and op_stack[-1] != '(' and precedence[token] <= precedence[op_stack[-1]]:
|
|
op = op_stack.pop()
|
|
right = node_stack.pop()
|
|
left = node_stack.pop()
|
|
node_stack.append(TreeNode('Operator', op, left, right))
|
|
op_stack.append(token)
|
|
elif token == '(':
|
|
op_stack.append(token)
|
|
elif token == ')':
|
|
while op_stack and op_stack[-1] != '(':
|
|
op = op_stack.pop()
|
|
right = node_stack.pop()
|
|
left = node_stack.pop()
|
|
node_stack.append(TreeNode('Operator', op, left, right))
|
|
op_stack.pop()
|
|
|
|
# 处理剩下的操作符
|
|
while op_stack:
|
|
op = op_stack.pop()
|
|
right = node_stack.pop()
|
|
left = node_stack.pop()
|
|
node_stack.append(TreeNode('Operator', op, left, right))
|
|
|
|
# 返回抽象语法树的根节点
|
|
tree.rootID = node_stack[0].NodeID
|
|
# print(tree.rootID)
|
|
# print(tree.Node)
|
|
return node_stack[0]
|
|
|
|
|
|
def print_ast(node:TreeNode, prefix='', is_left=True):
|
|
if node:
|
|
if node.NodeType == 'Value':
|
|
s = str(node.FaceValue)
|
|
else:
|
|
s = str(node.Ops)
|
|
print(prefix + ('├── ' if is_left else '└── ') + s)
|
|
print_ast(node.left, prefix + ('│ ' if is_left else ' '), True)
|
|
print_ast(node.right, prefix + ('│ ' if is_left else ' '), False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
expr = "(34-13.121)*2*6"
|
|
# ast = build_ast(expr)
|
|
# print_ast(ast)
|
|
# print(tree.Node)
|
|
class Frame_Tree:
|
|
# def Frame_tree():
|
|
def __init__(self, window):
|
|
self.Tree_root = tk.Frame(window, relief="groove", width=610, height=400)
|
|
self.Tree_root.place(x=50, y=250) # 树的显示
|
|
|
|
self.Tree_cv = tk.Canvas(self.Tree_root, width=610, height=400, bg='#b7bcc0')
|
|
self.Tree_cv.place(x=0, y=0)
|
|
# Tree_cv.create_rectangle(10, 10, 600, 290, width=10, outline='#ff9933')
|
|
|
|
bot = tk.Button(self.Tree_root, text='添加运算节点', bg='#cca583', fg='black', )
|
|
bot.place(x=500, y=40, width=80, height=30)
|
|
bot = tk.Button(self.Tree_root, text='添加数值节点', bg='#cca583', fg='black', )
|
|
bot.place(x=500, y=75, width=80, height=30)
|
|
bot = tk.Button(self.Tree_root, text='改变运算输入', bg='#cca583', fg='black', )
|
|
bot.place(x=500, y=110, width=80, height=30)
|
|
bot = tk.Button(self.Tree_root, text='改变节点运算', bg='#cca583', fg='black', )
|
|
bot.place(x=500, y=145, width=80, height=30)
|
|
bot = tk.Button(self.Tree_root, text='数值节点交换', bg='#cca583', fg='black', )
|
|
bot.place(x=500, y=180, width=80, height=30)
|
|
bot = tk.Button(self.Tree_root, text='产生表达式', bg='#cca583', fg='black', command=lambda: self.Aexp())
|
|
bot.place(x=500, y=215, width=80, height=30)
|
|
# bot = tk.Button(self.Tree_root, text='计算', bg='#cca583', fg='black', command=lambda: tree.calculate(tree.Node[tree.rootID]))
|
|
# bot.place(x=500, y=250, width=80, height=30)
|
|
|
|
|
|
|
|
|
|
def clear(self):
|
|
self.Tree_cv.delete('all')
|
|
tree.Node = []
|
|
tree.rootID = None
|
|
|
|
def Aexp(self):
|
|
entry.delete(0, "end")
|
|
print(tree.Aexp(tree.Node[tree.rootID]))
|
|
entry.insert("end", tree.Aexp(tree.Node[tree.rootID]))
|
|
label2 = tk.Label(window, text=f'= {float(tree.calculate(tree.Node[tree.rootID])):.2f}',
|
|
font=('宋体', 15, 'bold'), width=30, height=10,
|
|
# 设置填充区距离、边框宽度和其样式(凹陷式)
|
|
borderwidth=5, relief="sunken")
|
|
label2.place(x=240, y=160, width=60, height=50)
|
|
|
|
def show_tree(self, ss):
|
|
# 清空画布
|
|
self.Tree_cv.delete('all')
|
|
build_ast(ss)
|
|
for i in range(len(tree.Node)):
|
|
tree.deepth.append(-1)
|
|
# print(tree.Node)
|
|
# print(tree.deepth)
|
|
# 格式化树节点的深度
|
|
tree.deep()
|
|
# print(tree.deepth)
|
|
|
|
# 初始化节点数与深度
|
|
dept, nums = -1, 0
|
|
for i in range(len(tree.deepth)):
|
|
dept = max(dept, tree.deepth[i])
|
|
tree.place.append((None, None))
|
|
if tree.deepth[i] == 0:
|
|
nums += 1
|
|
# print(dept, nums)
|
|
|
|
tree_length = 500
|
|
tree_height = 400
|
|
dx = tree_length // (nums + 1)
|
|
dy = tree_height // (dept + 2)
|
|
bc = 40
|
|
# 若为数字节点
|
|
cur = 0
|
|
for i in range(len(tree.deepth)):
|
|
if tree.deepth[i] == 0:
|
|
cur += 1
|
|
tree.place[i] = (cur * dx, tree_height - dy)
|
|
self.Tree_cv.create_rectangle(tree.place[i][0] - bc / 2, tree.place[i][1] - bc / 2,
|
|
tree.place[i][0] + bc / 2,
|
|
tree.place[i][1] + bc / 2, fill='white', width=0)
|
|
self.Tree_cv.create_text(tree.place[i][0], tree.place[i][1], text=str(tree.Node[i][5]),
|
|
font=('宋体', 15, 'bold'), anchor='center')
|
|
|
|
for d in range(dept):
|
|
cur_d = d + 1
|
|
for i in range(len(tree.deepth)):
|
|
if tree.deepth[i] == cur_d:
|
|
lnode = tree.place[tree.Node[i][3]]
|
|
rnode = tree.place[tree.Node[i][4]]
|
|
node = ((lnode[0] + rnode[0]) / 2, tree_height - (cur_d + 1) * dy)
|
|
self.Tree_cv.create_line(lnode[0], lnode[1] - bc / 2, node[0], node[1] + bc / 2, fill='#1ffbe9',
|
|
width=2)
|
|
self.Tree_cv.create_line(rnode[0], rnode[1] - bc / 2, node[0], node[1] + bc / 2, fill='#1ffbe9',
|
|
width=2)
|
|
tree.place[i] = node
|
|
self.Tree_cv.create_rectangle(tree.place[i][0] - bc / 2, tree.place[i][1] - bc / 2,
|
|
tree.place[i][0] + bc / 2, tree.place[i][1] + bc / 2,
|
|
fill='white', width=0)
|
|
self.Tree_cv.create_text(tree.place[i][0], tree.place[i][1], text=str(tree.Node[i][2]),
|
|
font=('宋体', 15, 'bold'), anchor='center')
|
|
window = tk.Tk()
|
|
var = tk.StringVar()
|
|
var2 = tk.StringVar()
|
|
width = 700
|
|
height = 700 # 窗口大小
|
|
screen_width = window.winfo_screenwidth() # winfo方法来获取当前电脑屏幕大小
|
|
screen_height = window.winfo_screenheight()
|
|
x = int((screen_width - width) / 2)
|
|
y = int((screen_height - height) / 2) - 40
|
|
size = '{}x{}+{}+{}'.format(width, height, x, y)
|
|
window.geometry(size)
|
|
|
|
|
|
# 基本组件显示
|
|
cv = tk.Canvas(window, width=380, height=136, bg='#FFFFCC')
|
|
cv.place(x=100, y=3)
|
|
F = Frame_Tree(window)
|
|
but = tk.Button(window, text='出题', bg='#ffffcc', fg='black', command=lambda: show_card())
|
|
but.place(x=500, y=20, width=100, height=50, )
|
|
but2 = tk.Button(window, text='换牌', bg='#ffffcc', fg='black', )
|
|
but2.place(x=500, y=80, width=100, height=50, )
|
|
|
|
entry = tk.Entry(window, show=None, font=('宋体', 15, 'bold'))
|
|
entry.place(x=50, y=160, width=180, height=50)
|
|
label2 = tk.Label(window, text='= 24', font=('宋体', 15, 'bold'), width=30, height=10,
|
|
# 设置填充区距离、边框宽度和其样式(凹陷式)
|
|
borderwidth=5, relief="sunken")
|
|
but4 = tk.Button(window, text='计算', bg='#ffffcc', fg='black', command=lambda: show_result(entry.get(), sz))
|
|
but4.place(x=315, y=165, width=50, height=40, )
|
|
label2.place(x=240, y=160, width=60, height=50)
|
|
|
|
but3 = tk.Button(window, text='自动求解', bg='#ffffcc', fg='black', command=lambda: show_answer())
|
|
but3.place(x=250, y=210, width=120, height=40, )
|
|
but3 = tk.Button(window, text='所有答案', bg='#ffffcc', fg='black', command=lambda: show_all())
|
|
but3.place(x=410, y=210, width=120, height=40, )
|
|
|
|
but5 = tk.Button(window, text='画树', bg='#ffffcc', fg='black', command=lambda: F.show_tree(expr))
|
|
but5.place(x=80, y=210, width=120, height=40, )
|
|
|
|
|
|
|
|
# cv1 = tk.Canvas(window, width=380, height=90, bg='#FFFFCC')
|
|
# cv1.place(x=100, y=200)
|
|
|
|
window.mainloop()
|