# -*- 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()