diff --git a/data.py b/data.py index 88c20c9..d32d0b8 100644 --- a/data.py +++ b/data.py @@ -2,322 +2,348 @@ import math import random -OPS = ['+', '-', '*', '/','^','sin','cos','tan'] +OPS = ['+', '-', '*', '/', '^', 'sin', 'cos', 'tan'] + + def get_Aexps(nums=None): - ops = OPS[:4] - if nums is None: - nums = [] - for i in range(4): - num = input("请输入第%d个数字:" % (i + 1)) - if num.isdigit(): - if 1 <= int(num) <= 9: - nums.append(num) - group_nums = get_nums_group(nums) - results = [] - for nums in group_nums: - for op1 in ops: - for op2 in ops: - for op3 in ops: - Aexp = f"(({nums[0]}{op1}{nums[1]}){op2}{nums[2]}){op3}{nums[3]}" # 枚举表达式 - try: - result = eval(Aexp) - if result == 24 and Aexp not in results: - results.append(Aexp) - except ZeroDivisionError: - continue # 跳过0除错误 - if len(results) != 0: - return results - else: - return "无法得出24点" + ops = OPS[:4] + if nums is None: + nums = [] + for i in range(4): + num = input("请输入第%d个数字:" % (i + 1)) + if num.isdigit(): + if 1 <= int(num) <= 9: + nums.append(num) + group_nums = get_nums_group(nums) + results = [] + for nums in group_nums: + for op1 in ops: + for op2 in ops: + for op3 in ops: + Aexp = f"(({nums[0]}{op1}{nums[1]}){op2}{nums[2]}){op3}{nums[3]}" # 枚举表达式 + try: + result = eval(Aexp) + if result == 24 and Aexp not in results: + results.append(Aexp) + except ZeroDivisionError: + continue # 跳过0除错误 + if len(results) != 0: + return results + else: + return "无法得出24点" + def gen_exp(nums): # 生成表达式 - ops = OPS[:4] - result = 0 - group_nums = get_nums_group(nums) - for nums in group_nums: - for op1 in ops: - for op2 in ops: - for op3 in ops: - Aexp = f"(({nums[0]}{op1}{nums[1]}){op2}{nums[2]}){op3}{nums[3]}" - try: - result = eval(Aexp) - if result == 24: - return Aexp - except ZeroDivisionError: - continue # 跳过0除错误 - if result != 24: - return "无法得出24点" + ops = OPS[:4] + result = 0 + group_nums = get_nums_group(nums) + for nums in group_nums: + for op1 in ops: + for op2 in ops: + for op3 in ops: + Aexp = f"(({nums[0]}{op1}{nums[1]}){op2}{nums[2]}){op3}{nums[3]}" + try: + result = eval(Aexp) + if result == 24: + return Aexp + except ZeroDivisionError: + continue # 跳过0除错误 + if result != 24: + return "无法得出24点" + + def get_nums_group(nums): # 获取4个数的排列组合 - group = [] - for i in range(4): - for j in range(4): - for k in range(4): - for l in range(4): - if i != j and i != k and i != l and j != k and j != l and k != l: - group.append([nums[i], nums[j], nums[k], nums[l]]) - return group + group = [] + for i in range(4): + for j in range(4): + for k in range(4): + for l in range(4): + if i != j and i != k and i != l and j != k and j != l and k != l: + group.append([nums[i], nums[j], nums[k], nums[l]]) + return group class Node: - def __init__(self, treeNode_value): - self.NodeID = treeNode_value[0] - self.NodeType = treeNode_value[1] - self.Ops = treeNode_value[2] - self.LeftNodeID = treeNode_value[3] - self.RightNodeID = treeNode_value[4] - self.FaceValue = treeNode_value[5] - self.FaceColor = treeNode_value[6] - - def __str__(self): - return f"NodeID: {self.NodeID}\t NodeType: {self.NodeType}\t Ops: {self.Ops}\t LeftNodeID: {self.LeftNodeID}\t RightNodeID: {self.RightNodeID}\t FaceValue: {self.FaceValue}\t FaceColor: {self.FaceColor}" + def __init__(self, treeNode_value): + self.NodeID = treeNode_value[0] + self.NodeType = treeNode_value[1] + self.Ops = treeNode_value[2] + self.LeftNodeID = treeNode_value[3] + self.RightNodeID = treeNode_value[4] + self.FaceValue = treeNode_value[5] + self.FaceColor = treeNode_value[6] + def __str__(self): + return ( + f"NodeID: {self.NodeID}\t NodeType: {self.NodeType}\t Ops: {self.Ops}\t LeftNodeID: {self.LeftNodeID}\t " + f"RightNodeID: {self.RightNodeID}\t FaceValue: {self.FaceValue}\t FaceColor: {self.FaceColor}") def split_exp(expression): # 将表达式拆分为数字和运算符 - stack = [] - current_number = '' - for char in expression: - if char.isdigit() or char == '.': - current_number += char - elif char.isalpha(): # 处理sin,cos等函数 - current_number += char - elif char == '(': - if current_number: - stack.append(current_number) - current_number = '' - stack.append(char) - elif char == ')' and current_number: - stack.append(current_number) - stack.append(char) - current_number = '' - elif char in OPS: - if current_number: - stack.append(current_number) - current_number = '' - stack.append(char) - if current_number: - stack.append(current_number) - return stack + stack = [] + current_number = '' + for char in expression: + if char.isdigit() or char == '.': + current_number += char + elif char.isalpha(): # 处理sin,cos等函数 + current_number += char + elif char == '(': + if current_number: + stack.append(current_number) + current_number = '' + stack.append(char) + elif char == ')' and current_number: + stack.append(current_number) + stack.append(char) + current_number = '' + elif char in OPS: + if current_number: + stack.append(current_number) + current_number = '' + stack.append(char) + if current_number: + stack.append(current_number) + return stack def get_postfix_expression_(expression): # 将中缀表达式转化为后缀表达式 - expression = split_exp(expression) # 多位数拆分 - precedence = {'+': 1, '-': 1, '*': 2, '/': 2} - postfix = [] - stack = [] - for char in expression: - if char.isdigit(): - postfix.append(char) - elif char == '(': - stack.append(char) - elif char == ')': - while stack and stack[-1] != '(': - postfix.append(stack.pop()) - stack.pop() # 弹出 '(' - elif char in precedence: - while stack and stack[-1] != '(' and precedence[stack[-1]] >= precedence[char]: - '''这行代码的作用是在中缀表达式转换为后缀表达式的过程中,检查当前运算符与栈顶运算符的优先级。具体来说,它的功能是: - 确保栈不为空(避免了索引错误)。 - 确保栈顶元素不是左括号 '(',因为左括号的优先级最低,不需要进行比较。 - 确保栈顶元素的优先级大于等于当前运算符的优先级,因为栈中的运算符都应该在当前运算符之前执行(栈顶元素的优先级大于等于当前运算符表示栈顶元素的操作要优先于当前操作执行)。''' - postfix.append(stack.pop()) - stack.append(char) - - while stack: - postfix.append(stack.pop()) - return postfix - - -def get_postfix_expression(expression): - # 多位数拆分 - expression = split_exp(expression) - precedence = {'+': 1, '-': 1, '*': 2, '/': 2, '^': 3, "sin": 4, "cos": 4, "tan": 4} - postfix = [] - stack = [] - - def is_number(char): - try: - float(char);return True - except ValueError: return False - - for char in expression: - if is_number(char): - postfix.append(char) - elif char == '(': - stack.append(char) - elif char == ')': - while stack and stack[-1] != '(': - postfix.append(stack.pop()) - stack.pop() # 弹出 '(' - elif char in precedence: - while stack and stack[-1] != '(' and precedence.get(stack[-1], 0) >= precedence.get(char, 0): - postfix.append(stack.pop()) - stack.append(char) - - while stack: - postfix.append(stack.pop()) - - return postfix + expression = split_exp(expression) # 多位数拆分 + precedence = {'+': 1, '-': 1, '*': 2, '/': 2} + postfix = [] + stack = [] + for char in expression: + if char.isdigit(): + postfix.append(char) + elif char == '(': + stack.append(char) + elif char == ')': + while stack and stack[-1] != '(': + postfix.append(stack.pop()) + stack.pop() # 弹出 '(' + elif char in precedence: + while stack and stack[-1] != '(' and precedence[stack[-1]] >= precedence[char]: + '''这行代码的作用是在中缀表达式转换为后缀表达式的过程中,检查当前运算符与栈顶运算符的优先级。具体来说,它的功能是: + 确保栈不为空(避免了索引错误)。 + 确保栈顶元素不是左括号 '(',因为左括号的优先级最低,不需要进行比较。 + 确保栈顶元素的优先级大于等于当前运算符的优先级,因为栈中的运算符都应该在当前运算符之前执行(栈顶元素的优先级大于等于当前运算符表示栈顶元素的操作要优先于当前操作执行)。''' + postfix.append(stack.pop()) + stack.append(char) + + while stack: + postfix.append(stack.pop()) + return postfix + + +def get_postfix_expression(expression): # 将中缀表达式转化为后缀表达式 + # 多位数拆分 + expression = split_exp(expression) + precedence = {'+': 1, '-': 1, '*': 2, '/': 2, '^': 3, "sin": 4, "cos": 4, "tan": 4} + postfix = [] + stack = [] + + def is_number(char): + try: + float(char) + return True + except ValueError: + return False + + for char in expression: + if is_number(char): + postfix.append(char) + elif char == '(': + stack.append(char) + elif char == ')': + while stack and stack[-1] != '(': + postfix.append(stack.pop()) + stack.pop() # 弹出 '(' + elif char in precedence: + while stack and stack[-1] != '(' and precedence.get(stack[-1], 0) >= precedence.get(char, 0): + postfix.append(stack.pop()) + stack.append(char) + while stack: + postfix.append(stack.pop()) + + return postfix def calculate(root, TreeNode): # 计算节点树表达式 - if root.NodeType == 'Value': - return float(root.FaceValue) if root.FaceValue.replace('.', '').isdigit() else None # 将操作数转换为浮点数 - elif root.NodeType == 'Operator': - if root.Ops in ('sin', 'cos', 'tan'): # 检查是否是三角函数 - if root.LeftNodeID is not None: - operand_value = calculate(find_node_by_id(root.LeftNodeID, TreeNode), TreeNode) - else: - operand_value = calculate(find_node_by_id(root.RightNodeID, TreeNode), TreeNode) - if operand_value is None: - return None - if root.Ops == 'sin': - return math.sin(operand_value) - elif root.Ops == 'cos': - return math.cos(operand_value) - elif root.Ops == 'tan': - return math.tan(operand_value) - else: - left_value = calculate(find_node_by_id(root.LeftNodeID, TreeNode), TreeNode) - right_value = calculate(find_node_by_id(root.RightNodeID, TreeNode), TreeNode) - if left_value is None or right_value is None: - return None - if root.Ops == '+': - return left_value + right_value - elif root.Ops == '-': - return left_value - right_value - elif root.Ops == '*': - return left_value * right_value - elif root.Ops == '^': - return left_value ** right_value - elif root.Ops == '/': - if right_value == 0: - return None # 除数为0,返回None表示计算错误 - return left_value / right_value - return None # 节点类型不合法,返回None表示计算错误 + if root.NodeType == 'Value': + return float(root.FaceValue) if root.FaceValue.replace('.', '').isdigit() else None # 将操作数转换为浮点数 + elif root.NodeType == 'Operator': + if root.Ops in ('sin', 'cos', 'tan'): # 检查是否是三角函数 + if root.LeftNodeID is not None: + operand_value = calculate(find_node_by_id(root.LeftNodeID, TreeNode), TreeNode) + else: + operand_value = calculate(find_node_by_id(root.RightNodeID, TreeNode), TreeNode) + if operand_value is None: + return None + if root.Ops == 'sin': + return math.sin(operand_value) + elif root.Ops == 'cos': + return math.cos(operand_value) + elif root.Ops == 'tan': + return math.tan(operand_value) + else: + left_value = calculate(find_node_by_id(root.LeftNodeID, TreeNode), TreeNode) + right_value = calculate(find_node_by_id(root.RightNodeID, TreeNode), TreeNode) + if left_value is None or right_value is None: + return None + if root.Ops == '+': + return left_value + right_value + elif root.Ops == '-': + return left_value - right_value + elif root.Ops == '*': + return left_value * right_value + elif root.Ops == '^': + return left_value ** right_value + elif root.Ops == '/': + if right_value == 0: + return None # 除数为0,返回None表示计算错误 + return left_value / right_value # 除法结果为浮点数 + return None # 节点类型不合法,返回None表示计算错误 def find_node_by_id(node_id, TreeNode): - for node in TreeNode: - if node.NodeID == node_id: - return node - return None -def evaluate_postfix(expression): # 计算后缀表达式 - stack = [] - for char in expression: - if char.isdigit(): - stack.append(int(char)) - elif char in OPS: - operand2 = stack.pop() - operand1 = stack.pop() - if char == '+': - result = operand1 + operand2 - elif char == '-': - result = operand1 - operand2 - elif char == '*': - result = operand1 * operand2 - elif char == '^': - result = operand1 ** operand2 - elif char == '/': - result = operand1 / operand2 # 这里假设除法结果为浮点数 - stack.append(result) - return stack.pop() + for node in TreeNode: + if node.NodeID == node_id: + return node + return None + + +def evaluate_postfix(expression): # 计算后缀表达式 + stack = [] + for char in expression: + if char.isdigit(): + stack.append(int(char)) + elif char in OPS: + operand2 = stack.pop() + operand1 = stack.pop() + if char == '+': + result = operand1 + operand2 + elif char == '-': + result = operand1 - operand2 + elif char == '*': + result = operand1 * operand2 + elif char == '^': + result = operand1 ** operand2 + elif char == '/': + result = operand1 / operand2 # 这里假设除法结果为浮点数 + stack.append(result) + return stack.pop() + def exp_to_treeNode_dict(exp_ls): # 返回嵌套节点字典 - stack = [] - for char in exp_ls: - two = {} - if char.isdigit(): - stack.append(char) - elif char in OPS: - right = stack.pop() - left = stack.pop() - root = char - two[root] = [left,right] - stack.append(two) - return stack.pop() + stack = [] + for char in exp_ls: + two = {} + if char.isdigit(): + stack.append(char) + elif char in OPS: + right = stack.pop() + left = stack.pop() + root = char + two[root] = [left, right] + stack.append(two) + return stack.pop() + + +# 写一个函数从TreeNode中还原出表达式 +def get_expression(TreeNode): + stack = [] + for node in TreeNode: + if node.NodeType == 'Value': + stack.append(node.FaceValue) + elif node.NodeType == 'Operator': + if node.Ops in ('sin', 'cos', 'tan'): # 三角函数 + right = stack.pop() # 三角函数只有右操作数 + stack.append(f"{node.Ops}({right})") + else: + right = stack.pop() + left = stack.pop() + stack.append(f"({left}{node.Ops}{right})") + return stack.pop() def ExpressionAnalyse(exp_ls): # 返回语法树 - stack = [] # 符合和数值栈 - TreeNode = [] - face_color = ['Club', 'Diamond', 'Heart', 'Spade'] - id = 0 - for char in exp_ls: - fc = random.sample(face_color, 1) - if char.replace('.', '').isdigit(): # 检查字符是否是数字(包括小数) - stack.append(char) - elif char in OPS: - if char in ('sin', 'cos', 'tan'): # 检查运算符是否是三角函数 - operand = stack.pop() - if isinstance(operand, str) and operand.replace('.', '').isdigit(): - TreeNode.append(Node([id, 'Value', None, None, None, operand, fc])) - op_id = id - id += 1 - else: - op_id = operand - - root = char - TreeNode.append(Node([id, 'Operator', root, None, op_id, None, None])) - root_id = id - id += 1 - stack.append(root_id) - else: - right = stack.pop() - if isinstance(right, str) and right.replace('.', '').isdigit(): - TreeNode.append(Node([id, 'Value', None, None, None, right, fc])) - r_id = id - id += 1 - else: - r_id = right - - left = stack.pop() - if isinstance(left, str) and left.replace('.', '').isdigit(): - TreeNode.append(Node([id, 'Value', None, None, None, left, fc])) - l_id = id - id += 1 - else: - l_id = left - - root = char - TreeNode.append(Node([id, 'Operator', root, l_id, r_id, None, None])) - root_id = id - id += 1 - stack.append(root_id) - - return TreeNode + stack = [] # 符合和数值栈 + TreeNode = [] + face_color = ['Club', 'Diamond', 'Heart', 'Spade'] + id = 0 + for char in exp_ls: + fc = random.sample(face_color, 1) + if char.replace('.', '').isdigit(): # 检查字符是否是数字(包括小数) + stack.append(char) + elif char in OPS: + if char in ('sin', 'cos', 'tan'): # 检查运算符是否是三角函数 + operand = stack.pop() + if isinstance(operand, str) and operand.replace('.', '').isdigit(): + TreeNode.append(Node([id, 'Value', None, None, None, operand, fc])) + op_id = id + id += 1 + else: + op_id = operand + + root = char + TreeNode.append(Node([id, 'Operator', root, None, op_id, None, None])) + root_id = id + id += 1 + stack.append(root_id) + else: + right = stack.pop() + if isinstance(right, str) and right.replace('.', '').isdigit(): + TreeNode.append(Node([id, 'Value', None, None, None, right, fc])) + r_id = id + id += 1 + else: + r_id = right + + left = stack.pop() + if isinstance(left, str) and left.replace('.', '').isdigit(): + TreeNode.append(Node([id, 'Value', None, None, None, left, fc])) + l_id = id + id += 1 + else: + l_id = left + + root = char + TreeNode.append(Node([id, 'Operator', root, l_id, r_id, None, None])) + root_id = id + id += 1 + stack.append(root_id) + + return TreeNode if __name__ == '__main__': - # Aexps = get_Aexps() - # Aexp = '((2+92)*3)+35' - # treeNode = get_TreeNode(Aexp) - # for node in treeNode: - # print(node) - - # print(get_ast("2+9")) - # - # print(get_ast('(2+9)*3')) - # - # print("表达式","((6*3)+8)-6") - # print(get_ast('((6*(3-2))+8)-6')) - - # print(infix_to_postfix('((9+8*3)+8)-6')) - # exp = '6.1+2^3*3' - exp = 'cos(0)+sin(0)+5' - exp_ls = get_postfix_expression(exp) - print(exp_ls) - # print(exp_to_treeNode_dict(exp_ls)) - - # exp_ls = ['3.3', '2', '^', '3', '3', '*', '-'] - TreeNode= ExpressionAnalyse(exp_ls) - - result = calculate(TreeNode[-1], TreeNode) - print(result) - # - # # print(TreeNode) - for node in TreeNode: - print(node) - - - # print_tree(Aexp) + # Aexps = get_Aexps() + # Aexp = '((2+92)*3)+35' + # treeNode = get_TreeNode(Aexp) + # for node in treeNode: + # print(node) + + # print(get_ast("2+9")) + # + # print(get_ast('(2+9)*3')) + # + # print("表达式","((6*3)+8)-6") + # print(get_ast('((6*(3-2))+8)-6')) + + # print(infix_to_postfix('((9+8*3)+8)-6')) + # exp = '6.1+2^3*3' + exp = 'cos(0)+sin(0)+5' + exp_ls = get_postfix_expression(exp) + print(exp_ls) + # print(exp_to_treeNode_dict(exp_ls)) + + # exp_ls = ['3.3', '2', '^', '3', '3', '*', '-'] + TreeNode = ExpressionAnalyse(exp_ls) + + result = calculate(TreeNode[-1], TreeNode) + print(result) + # + # # print(TreeNode) + for node in TreeNode: + print(node) + +# print_tree(Aexp) diff --git a/graph_24.py b/graph_24.py index acc7208..7f694c5 100644 --- a/graph_24.py +++ b/graph_24.py @@ -4,66 +4,66 @@ from PySide6.QtWidgets import QGraphicsItem class RoundedRectangleItem(QGraphicsItem, QObject): - selected = Signal(int) - unselected = Signal(int) - def __init__(self, x, y, text, id): - QGraphicsItem.__init__(self) - QObject.__init__(self) - self.rect = QRectF(x, y, 70, 70) - self.radius = 10 - self.text = text - self.id = id - self.isSelected = False # 是否被选中 + selected = Signal(int) + unselected = Signal(int) - def boundingRect(self): - return self.rect + def __init__(self, x, y, text, id): + QGraphicsItem.__init__(self) + QObject.__init__(self) + self.rect = QRectF(x, y, 70, 70) + self.radius = 10 + self.text = text + self.id = id + self.isSelected = False # 是否被选中 - def shape(self): - path = QPainterPath() - path.addRoundedRect(self.rect, self.radius, self.radius) - return path + def boundingRect(self): + return self.rect - def paint(self, painter, option, widget): - painter.setRenderHint(QPainter.Antialiasing) # 设置抗锯齿 - pen = QPen(Qt.black) - painter.setPen(pen) + def shape(self): + path = QPainterPath() + path.addRoundedRect(self.rect, self.radius, self.radius) + return path - if self.isSelected: - # 如果被选中,设置不同的填充颜色和边框样式 - painter.setBrush(QColor(255, 0, 0, 255)) # 设置选中时的填充颜色 - pen.setWidth(2) # 设置边框宽度 - pen.setStyle(Qt.DashLine) # 设置虚线边框样式 - painter.setPen(pen) - else: - painter.setBrush(QColor(0, 255, 0, 255)) # 设置默认的填充颜色 + def paint(self, painter, option, widget): + painter.setRenderHint(QPainter.Antialiasing) # 设置抗锯齿 + pen = QPen(Qt.black) + painter.setPen(pen) - # 绘制圆角矩形 - painter.drawRoundedRect(self.rect, self.radius, self.radius) + if self.isSelected: + # 如果被选中,设置不同的填充颜色和边框样式 + painter.setBrush(QColor(255, 0, 0, 255)) # 设置选中时的填充颜色 + pen.setWidth(2) # 设置边框宽度 + pen.setStyle(Qt.DashLine) # 设置虚线边框样式 + painter.setPen(pen) + else: + painter.setBrush(QColor(0, 255, 0, 255)) # 设置默认的填充颜色 - # 计算文本的位置 - font = painter.font() - font.setPointSize(36) - painter.setFont(font) - metrics = QFontMetricsF(font) - text_bounding_rect = metrics.boundingRect(str(self.text)) - text_width = text_bounding_rect.width() - text_height = text_bounding_rect.height() - text_x = self.rect.center().x() - text_width / 2 - text_y = self.rect.center().y() + text_height / 4 + # 绘制圆角矩形 + painter.drawRoundedRect(self.rect, self.radius, self.radius) - # 绘制文本 - painter.drawText(text_x, text_y, str(self.text)) + # 计算文本的位置 + font = painter.font() + font.setPointSize(36) + painter.setFont(font) + metrics = QFontMetricsF(font) + text_bounding_rect = metrics.boundingRect(str(self.text)) + text_width = text_bounding_rect.width() + text_height = text_bounding_rect.height() + text_x = self.rect.center().x() - text_width / 2 + text_y = self.rect.center().y() + text_height / 4 - def mousePressEvent(self, event): - # 鼠标点击时切换选中状态 - self.isSelected = not self.isSelected - self.update() + # 绘制文本 + painter.drawText(text_x, text_y, str(self.text)) - # 发射选中信号 - if self.isSelected: - self.selected.emit(self.id) - else: - self.unselected.emit(self.id) + def mousePressEvent(self, event): + # 鼠标点击时切换选中状态 + self.isSelected = not self.isSelected + self.update() + # 发射选中信号 + if self.isSelected: + self.selected.emit(self.id) + else: + self.unselected.emit(self.id) - super().mousePressEvent(event) + super().mousePressEvent(event) diff --git a/main.py b/main.py index 659dc3f..1f2486c 100644 --- a/main.py +++ b/main.py @@ -1,451 +1,461 @@ - import random -from PySide6.QtGui import QPainter, QPen, QFont, QPixmap + +from PySide6.QtGui import QFont from PySide6.QtWidgets import QApplication, QMainWindow, QGraphicsLineItem, QMessageBox, QGraphicsScene -from main_ui import Ui_MainWindow import data from graph_24 import * +from main_ui import Ui_MainWindow class MainWindow(QMainWindow, Ui_MainWindow): - def __init__(self): - super().__init__() - self.drawn_tree_id = set() # 已经绘制了树的节点 - self.seq = 1 # 在有树的情况下,添加节点的序号,以确定新加的节点位置 - self.trees_root = [] # 保存所有树的根节点 - self.selected_id = [] # 选中的节点ID - self.adds_id = set() # 已经用过的ID - self.value_nodes = [] # 数值节点 - # self.operator_nodes = [] - self.selected_ops = None # 选择的运算符 - self.selected_value = None # 当前选择的卡片值 - self.r_count = 0 # 记录所有答案点击次数 - self.offset = 0 # 语法树遍历时随深度平移长度,避免节点重合 - self.TreeNode = [] - self.Tree_4_nums = None - self.setupUi(self) - self.init_ui() - self.init_slot() - self.pushButton_q.clicked.connect(self.show_card) - self.pushButton_ex.clicked.connect(self.show_card) - self.pushButton_autosv.clicked.connect(self.AutoTest_9_10) - self.pushButton_dtree.clicked.connect(self.draw_tree) - self.pushButton_ast.clicked.connect(self.calculate_button) - self.pushButton_allan.clicked.connect(self.AutoTest) - self.clear_button.clicked.connect(self.secen_clear) - self.pushButton_add.clicked.connect(lambda :self.select_ops('+')) - self.pushButton_sub.clicked.connect(lambda :self.select_ops('-')) - self.pushButton_mul.clicked.connect(lambda :self.select_ops('*')) - self.pushButton_div.clicked.connect(lambda :self.select_ops('/')) - self.add_val_node.clicked.connect(self.add_value_node) - self.add_ops_node.clicked.connect(self.add_operator_node) - self.ex_node_value.clicked.connect(self.exchange_value_node) - self.ex_node_ops.clicked.connect(self.exchange_ops_node) - self.ex_node_self.clicked.connect(self.exchange_two_nodes) - - - def secen_clear(self): - self.scene.clear() - self.selected_id = [] - self.value_nodes = [] - self.TreeNode = [] - self.adds_id = set() - self.trees_root = [] - self.drawn_tree_id = set() - self.selected_value = None # 当前选择的卡片值. - self.selected_ops = None # 选择的运算符 - # 清空运算符显示 - self.lb_ops_selected.setText("") - - - def select_ops(self, ops): - self.lb_ops_selected.setText(ops) - self.selected_ops = ops - - def add_value_node(self): - # self.scene.clear() - if self.selected_value is not None: # 选择了卡片值 - self.value_nodes.append(self.selected_value) # 添加到节点数值列表 - for i in range(len(self.adds_id)+1): - if i not in self.adds_id: - value = self.selected_value - nodet = RoundedRectangleItem(150*self.seq, 0, str(value), i) - self.seq += 1 # 序号加1 - nodet.selected.connect(lambda id: self.selected_node(id)) # 连接信号 - nodet.unselected.connect(lambda id: self.unselected_node(id)) - node = data.Node([i, 'Value', None, None, None, value, None]) - self.adds_id.add(i) # 添加全局id - self.TreeNode.append(node) - self.scene.addItem(nodet) - else: - QMessageBox.warning(self, '警告', '请先选择一张牌') - - def judge_node_conn(self): # 判断节点是否已经连接 - for id in self.selected_id: - for node in self.TreeNode: - if node.LeftNodeID == id or node.RightNodeID == id: - return True - - def add_operator_node(self): - if self.selected_ops is not None: - if len(self.selected_id) == 2: - # 判断已选节点是否已经被连接 - if self.judge_node_conn(): - QMessageBox.warning(self, '警告', '节点已经连接') - return - for i in range(len(self.adds_id)+1): # 确保id唯一 - if i not in self.adds_id: - node = data.Node([i, 'Operator', self.selected_ops, None, None, None, None]) - self.TreeNode.append(node) - self.adds_id.add(i) # 全局ID - node.LeftNodeID = int(self.selected_id[1]) - node.RightNodeID = int(self.selected_id[0]) - self.flash_node(self.selected_id[0]) - self.flash_node(self.selected_id[1]) - - self.selected_id = [] # 清空选中的节点 - self.trees_root.append(node) # 保存根节点 - # 从根节点列表中删去作为子节点连接的节点 - node_to_remove = [] - for root in self.trees_root: - if root.NodeID == node.LeftNodeID or root.NodeID == node.RightNodeID: - node_to_remove.append(root) - for root in node_to_remove: - self.trees_root.remove(root) - - print("已用ID", self.adds_id) - self.scene.clear() - for i, root in enumerate(self.trees_root): - if self.has_both_operators(self.TreeNode): - self.offset = 35 - self.ShowTree(root, self.TreeNode,i*300, 0) - self.offset = 0 - - self.seq = 1 # 重置序号 - for noed in self.TreeNode: - print("运算符节点", noed) - else: - QMessageBox.warning(self, '警告', '请先选择两个节点') - else: - QMessageBox.warning(self, '警告', '请先选择一个运算符') - - def exchange_value_node(self): - # 修改节点值 - if len(self.selected_id) == 1: - if self.selected_value: - if self.TreeNode: - for node in self.TreeNode: - if node.NodeID == self.selected_id[0]: - node.FaceValue = self.selected_value - for nodet in self.scene.items(): - if isinstance(nodet, RoundedRectangleItem): - if nodet.id == self.selected_id[0]: - nodet.text = str(self.selected_value) - nodet.update() - else: - QMessageBox.warning(self, '警告', '请先选择一张牌') - else: - QMessageBox.warning(self, '警告', '请先选择一个节点') - - def exchange_ops_node(self): - # 修改节点运算符 - if len(self.selected_id) == 1: - if self.selected_ops: - if self.TreeNode: - for node in self.TreeNode: - if node.NodeID == self.selected_id[0]: - node.Ops = self.selected_ops - for nodet in self.scene.items(): - if isinstance(nodet, RoundedRectangleItem): - if nodet.id == self.selected_id[0]: - nodet.text = str(self.selected_ops) - nodet.update() - else: - QMessageBox.warning(self, '警告', '请先选择一个运算符') - else: - QMessageBox.warning(self, '警告', '请先选择一个节点') - - def exchange_two_nodes(self): - # 交换选中两个节点FaceValue的值 - if len(self.selected_id) == 2: - if self.TreeNode: - node_1 = None - node_2 = None - for node in self.TreeNode: - if node.NodeID == self.selected_id[0]: - if node.NodeType == 'Value': - node_1 = node - else: - QMessageBox.warning(self, '警告', '请选择两个数值节点') - return - elif node.NodeID == self.selected_id[1]: - if node.NodeType == 'Value': - node_2 = node - else: - QMessageBox.warning(self, '警告', '请选择两个数值节点') - return - if node_1 and node_2: - node_1.FaceValue, node_2.FaceValue = node_2.FaceValue, node_1.FaceValue - for item in self.scene.items(): - if isinstance(item, RoundedRectangleItem): - if item.id == self.selected_id[0]: - item.text = str(node_1.FaceValue) - item.update() - elif item.id == self.selected_id[1]: - item.text = str(node_2.FaceValue) - item.update() - - - else: - QMessageBox.warning(self, '警告', '请选择两个节点') - - - def selected_node(self, id): - if len(self.selected_id) < 2: - self.selected_id.append(id) - else: - QMessageBox.warning(self, '警告', '不能选择超过3个节点') - for nodet in self.scene.items(): # 遍历场景中的所有图元 - if isinstance(nodet, RoundedRectangleItem): # 如果是圆角矩形 - if nodet.id == id: # 如果是当前选中的节点 - nodet.isSelected = False # 取消选中 - nodet.update() # 更新图元 - - - def unselected_node(self, id): - self.selected_id.remove(id) - print(self.selected_id) - - def flash_node(self, id): # 还原节点为未选中状态 - for nodet in self.scene.items(): # 遍历场景中的所有图元 - if isinstance(nodet, RoundedRectangleItem): # 如果是圆角矩形 - if nodet.id == id: # 如果是当前选中的节点 - nodet.isSelected = False # 取消选中 - nodet.update() # 更新图元 - def init_ui(self): - self.scene = QGraphicsScene() - self.graphicsView.setScene(self.scene) - - - def init_slot(self): - self.pushButton.clicked.connect(self.on_calc) - - def load_image(self, path, Tree_4_nums, index): - def current_selection(value, path): - self.selected_value = value - self.p1_selected.setStyleSheet(f"border-image: url({path});") - self.pushButton_ast.setEnabled(False) - self.pushButton_allan.setEnabled(False) - self.pushButton_dtree.setEnabled(False) - self.pushButton_autosv.setEnabled(False) - print(value) - - if index == 0: - self.p1.setStyleSheet(f"border-image: url({path});") - self.p1.clicked.connect(lambda :current_selection(Tree_4_nums[index], path)) - elif index == 1: - self.p2.setStyleSheet(f"border-image: url({path});") - self.p2.clicked.connect(lambda :current_selection(Tree_4_nums[index], path)) - elif index == 2: - self.p3.setStyleSheet(f"border-image: url({path});") - self.p3.clicked.connect(lambda :current_selection(Tree_4_nums[index], path)) - elif index == 3: - self.p4.setStyleSheet(f"border-image: url({path});") - self.p4.clicked.connect(lambda :current_selection(Tree_4_nums[index], path)) - - - def show_card(self): - self.pushButton_ast.setEnabled(True) - self.pushButton_allan.setEnabled(True) - self.pushButton_dtree.setEnabled(True) - self.pushButton_autosv.setEnabled(True) - self.p1_selected.setStyleSheet("border-image: none;") - # 原始数组 - self.lineEdit.clear() - self.label_result.setText(" =? ") - self.label_TF.setText("") - original_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] - face_card = ['Club', 'Diamond', 'Heart', 'Spade'] - # 从原始数组中随机选择4个元素组成新列表 - self.Tree_4_nums = random.sample(original_list, 4) - # 从花色中随机选择一个 - for index, num in enumerate(self.Tree_4_nums): - card_face = random.choice(face_card) - if num == 11: - card_path = f"card/{card_face}/{card_face}j.png" - elif num == 12: - card_path = f"card/{card_face}/{card_face}q.png" - elif num == 13: - card_path = f"card/{card_face}/{card_face}k.png" - else: - card_path = f"card/{card_face}/{card_face}{num}.png" - self.load_image(card_path, self.Tree_4_nums, index) - # return self.Tree_4_nums - - def AutoTest(self): - if self.Tree_4_nums is None: - QMessageBox.warning(self, '警告', '请先出题') - return - else: - results = data.get_Aexps(self.Tree_4_nums) - if results == "无法得出24点": - self.lineEdit.clear() - self.lineEdit.setText("无法得出24点") - return - else: - if self.r_count < len(results): - self.lineEdit.clear() - self.lineEdit.setText(f"{results[self.r_count]}") - self.on_calc() # 自动计算 - self.draw_tree() # 绘制语法树 - self.r_count += 1 - else: - QMessageBox.information(self, '提示', '已经是最后一个表达式了') - self.r_count = 0 - - def AutoTest_9_10(self): - if self.Tree_4_nums is None: - QMessageBox.warning(self, '警告', '请先出题') - return - else: - aexp = data.gen_exp(self.Tree_4_nums) - if aexp == "无法得出24点": - self.lineEdit.clear() - self.lineEdit.setText("无法得出24点") - else: - self.lineEdit.clear() - self.lineEdit.setText(f"{aexp}") - self.on_calc() # 自动计算 - self.draw_tree() # 绘制语法树 - - def calculate_button(self): # - self.scene.clear() - aexp = self.lineEdit.text() - try: - exp_ls = data.get_postfix_expression(aexp) # 获取后缀表达式列表 - self.TreeNode = data.ExpressionAnalyse(exp_ls) # 将后缀表达式转化为节点树 - result = data.calculate(self.TreeNode[-1], self.TreeNode) - self.label_result.setText(f"={result}") - except: - QMessageBox.warning(self, '警告', '表达式错误!') - return - if self.TreeNode: - root = self.TreeNode[-1] - result = data.calculate(root, self.TreeNode) - self.label_result.setText(f"={result}") - self.result_ToF(int(result)) - if self.has_both_operators(self.TreeNode): - self.offset = 35 - self.ShowTree(root, self.TreeNode) - self.offset = 0 - - def has_both_operators(self, TreeNode): # 判断是否同时有两个运算符 - ops = data.OPS - def find_op_by_id(id): # 根据ID查找运算符 - for node in TreeNode: - if node.NodeID == id and node.NodeType == "Operator": - return node.Ops - for node in TreeNode: # 遍历节点树 - if node.NodeType == "Operator": - opl = find_op_by_id(node.LeftNodeID) - opr = find_op_by_id(node.RightNodeID) - if opl in ops and opr in ops: - return True - - - - def draw_tree(self): - self.scene.clear() - aexp = self.lineEdit.text() - if aexp == "": - QMessageBox.warning(self, '警告', '请输入表达式') - return - try: - exp_ls = data.get_postfix_expression(aexp) # 获取后缀表达式列表 - self.TreeNode = data.ExpressionAnalyse(exp_ls) # 将后缀表达式转化为节点树 - root = self.TreeNode[-1] - if self.has_both_operators(self.TreeNode): - self.offset = 35 - self.ShowTree(root, self.TreeNode, depth=0) - self.offset = 0 - except Exception as e: - QMessageBox.warning(self, '警告', '表达式错误') - - - - def ShowTree(self, root, TreeNode, startx=0, starty=0, depth=0): - # 绘制根节点 - if root.NodeType == 'Value': - nds_root = RoundedRectangleItem(startx, starty, root.FaceValue, root.NodeID) - nds_root.selected.connect(lambda id: self.selected_node(id)) - nds_root.unselected.connect(lambda id: self.unselected_node(id)) - self.drawn_tree_id.add(root.NodeID) - else: - nds_root = RoundedRectangleItem(startx, starty, root.Ops, root.NodeID) - nds_root.selected.connect(lambda id: self.selected_node(id)) - nds_root.unselected.connect(lambda id: self.unselected_node(id)) - self.drawn_tree_id.add(root.NodeID) - nds_root.setZValue(1) - self.scene.addItem(nds_root) - - # 计算节点间的水平偏移量 - horizontal_offset = 100 - depth*self.offset - - # 计算左子树和右子树的位置 - left_x, left_y = startx - horizontal_offset, starty + 100 - right_x, right_y = startx + horizontal_offset, starty + 100 - - # 绘制左子树 - if root.LeftNodeID is not None: - left_child = next(node for node in TreeNode if node.NodeID == root.LeftNodeID) - self.ShowTree(left_child, TreeNode, left_x, left_y, depth + 1) - - # 绘制左子树与根节点之间的连线 - line_left = QGraphicsLineItem(startx + 35, starty + 35, left_x + 35, left_y + 35) - line_left.setZValue(-1) - line_left.setPen(QPen(Qt.red, 3)) - self.scene.addItem(line_left) - - # 绘制右子树 - if root.RightNodeID is not None: - right_child = next(node for node in TreeNode if node.NodeID == root.RightNodeID) - self.ShowTree(right_child, TreeNode, right_x, right_y, depth + 1) - - # 绘制右子树与根节点之间的连线 - line_right = QGraphicsLineItem(startx + 35, starty + 35, right_x + 35, right_y + 35) - line_right.setZValue(-1) - line_right.setPen(QPen(Qt.red, 3)) - self.scene.addItem(line_right) - - - def on_calc(self): - aexp = self.lineEdit.text() - if aexp == '': - QMessageBox.warning(self, '警告', '请输入表达式') - return - try: - result = eval(aexp) - self.label_result.setText(f"={int(result)}") - # 设置字体样式 - self.result_ToF(result) - except Exception as e: - QMessageBox.warning(self, '警告', '表达式错误') - return - - - def result_ToF(self,result): - font = QFont('Microsoft YaHei', 16) - font.setBold(True) # 将字体设置为粗体 - self.label_TF.setText('√' if result == 24 else '×') - # 将字体样式应用到 QLabel 上 - self.label_TF.setFont(font) - # 将文本颜色设置为红色 - self.label_TF.setStyleSheet("color: red;background-color:rgb(219, 205, 166)") + def __init__(self): + super().__init__() + self.drawn_tree_id = set() # 已经绘制了树的节点 + self.seq = 1 # 在有树的情况下,添加节点的序号,以确定新加的节点位置 + self.trees_root = [] # 保存所有树的根节点 + self.selected_id = [] # 选中的节点ID + self.adds_id = set() # 已经用过的ID + self.value_nodes = [] # 数值节点 + # self.operator_nodes = [] + self.selected_ops = None # 选择的运算符 + self.selected_value = None # 当前选择的卡片值 + self.r_count = 0 # 记录所有答案点击次数 + self.offset = 0 # 语法树遍历时随深度平移长度,避免节点重合 + self.TreeNode = [] + self.Tree_4_nums = None + self.setupUi(self) + self.init_ui() + self.init_slot() + self.pushButton_q.clicked.connect(self.show_card) # 显示卡片 + self.pushButton_ex.clicked.connect(self.show_card) # 显示卡片 + self.pushButton_autosv.clicked.connect(self.AutoTest_9_10) # 自动求解 + self.pushButton_dtree.clicked.connect(self.draw_tree) # 绘制语法树 + self.pushButton_ast.clicked.connect(self.calculate_button) # 生成语法树 + self.pushButton_allan.clicked.connect(self.AutoTest) # 自动求解 + self.clear_button.clicked.connect(self.secen_clear) # 清空画布 + self.pushButton_add.clicked.connect(lambda: self.select_ops('+')) + self.pushButton_sub.clicked.connect(lambda: self.select_ops('-')) + self.pushButton_mul.clicked.connect(lambda: self.select_ops('*')) + self.pushButton_div.clicked.connect(lambda: self.select_ops('/')) + self.add_val_node.clicked.connect(self.add_value_node) # 添加数值节点 + self.add_ops_node.clicked.connect(self.add_operator_node) # 添加运算符节点 + self.ex_node_value.clicked.connect(self.exchange_value_node) # 修改数值节点 + self.ex_node_ops.clicked.connect(self.exchange_ops_node) # 修改运算符节点 + self.ex_node_self.clicked.connect(self.exchange_two_nodes) # 交换两个节点 + self.pushButton_exp.clicked.connect(self.agent_exp) # 生成表达式 + + def secen_clear(self): + self.scene.clear() + self.selected_id = [] # 选中的节点ID + self.value_nodes = [] # 数值节点 + self.TreeNode = [] # 所有节点 + self.adds_id = set() # 已经用过的ID + self.trees_root = [] # 保存所有树的根节点 + self.drawn_tree_id = set() # 已经绘制了树的节点 + self.selected_value = None # 当前选择的卡片值. + self.selected_ops = None # 选择的运算符 + # 清空运算符显示 + self.lb_ops_selected.setText("") # 运算符显示 + + def select_ops(self, ops): + self.lb_ops_selected.setText(ops) + self.selected_ops = ops + + def add_value_node(self): + # self.scene.clear() + if self.selected_value is not None: # 选择了卡片值 + self.value_nodes.append(self.selected_value) # 添加到节点数值列表 + for i in range(len(self.adds_id) + 1): + if i not in self.adds_id: + value = self.selected_value + nodet = RoundedRectangleItem(150 * self.seq, 0, str(value), i) + self.seq += 1 # 序号加1 + nodet.selected.connect(lambda id: self.selected_node(id)) # 连接信号 + nodet.unselected.connect(lambda id: self.unselected_node(id)) + node = data.Node([i, 'Value', None, None, None, value, None]) + self.adds_id.add(i) # 添加全局id + self.TreeNode.append(node) + self.scene.addItem(nodet) + else: + QMessageBox.warning(self, '警告', '请先选择一张牌') + + def judge_node_conn(self): # 判断节点是否已经连接 + for id in self.selected_id: + for node in self.TreeNode: + if node.LeftNodeID == id or node.RightNodeID == id: + return True + + def add_operator_node(self): # 添加运算符节点 + if self.selected_ops is not None: + if len(self.selected_id) == 2: + # 判断已选节点是否已经被连接 + if self.judge_node_conn(): + QMessageBox.warning(self, '警告', '节点已经连接') + return + for i in range(len(self.adds_id) + 1): # 确保id唯一 + if i not in self.adds_id: + node = data.Node([i, 'Operator', self.selected_ops, None, None, None, None]) + self.TreeNode.append(node) + self.adds_id.add(i) # 全局ID + node.LeftNodeID = int(self.selected_id[1]) + node.RightNodeID = int(self.selected_id[0]) + self.flash_node(self.selected_id[0]) + self.flash_node(self.selected_id[1]) + + self.selected_id = [] # 清空选中的节点 + self.trees_root.append(node) # 保存根节点 + # 从根节点列表中删去作为子节点连接的节点 + node_to_remove = [] + for root in self.trees_root: + if root.NodeID == node.LeftNodeID or root.NodeID == node.RightNodeID: + node_to_remove.append(root) + for root in node_to_remove: + self.trees_root.remove(root) + + print("已用ID", self.adds_id) + self.scene.clear() + for i, root in enumerate(self.trees_root): + if self.has_both_operators(self.TreeNode): + self.offset = 35 + self.ShowTree(root, self.TreeNode, i * 300, 0) + self.offset = 0 + + self.seq = 1 # 重置序号 + for noed in self.TreeNode: + print("运算符节点", noed) + exps = data.get_expression(self.TreeNode) + print("表达式", exps) + else: + QMessageBox.warning(self, '警告', '请先选择两个节点') + else: + QMessageBox.warning(self, '警告', '请先选择一个运算符') + + def exchange_value_node(self): + # 修改节点值 + if len(self.selected_id) == 1: + if self.selected_value: + if self.TreeNode: + for node in self.TreeNode: + if node.NodeID == self.selected_id[0]: + node.FaceValue = self.selected_value + for nodet in self.scene.items(): + if isinstance(nodet, RoundedRectangleItem): + if nodet.id == self.selected_id[0]: + nodet.text = str(self.selected_value) + nodet.update() + else: + QMessageBox.warning(self, '警告', '请先选择一张牌') + else: + QMessageBox.warning(self, '警告', '请先选择一个节点') + + def exchange_ops_node(self): + # 修改节点运算符 + if len(self.selected_id) == 1: + if self.selected_ops: + if self.TreeNode: + for node in self.TreeNode: + if node.NodeID == self.selected_id[0]: + node.Ops = self.selected_ops + for nodet in self.scene.items(): + if isinstance(nodet, RoundedRectangleItem): + if nodet.id == self.selected_id[0]: + nodet.text = str(self.selected_ops) + nodet.update() + else: + QMessageBox.warning(self, '警告', '请先选择一个运算符') + else: + QMessageBox.warning(self, '警告', '请先选择一个节点') + + def exchange_two_nodes(self): + # 交换选中两个节点FaceValue的值 + if len(self.selected_id) == 2: + if self.TreeNode: + node_1 = None + node_2 = None + for node in self.TreeNode: + if node.NodeID == self.selected_id[0]: + if node.NodeType == 'Value': + node_1 = node + else: + QMessageBox.warning(self, '警告', '请选择两个数值节点') + return + elif node.NodeID == self.selected_id[1]: + if node.NodeType == 'Value': + node_2 = node + else: + QMessageBox.warning(self, '警告', '请选择两个数值节点') + return + if node_1 and node_2: + node_1.FaceValue, node_2.FaceValue = node_2.FaceValue, node_1.FaceValue + for item in self.scene.items(): + if isinstance(item, RoundedRectangleItem): + if item.id == self.selected_id[0]: + item.text = str(node_1.FaceValue) + item.update() + elif item.id == self.selected_id[1]: + item.text = str(node_2.FaceValue) + item.update() + + + else: + QMessageBox.warning(self, '警告', '请选择两个节点') + + def selected_node(self, id): + if len(self.selected_id) < 2: + self.selected_id.append(id) + else: + QMessageBox.warning(self, '警告', '不能选择超过3个节点') + for nodet in self.scene.items(): # 遍历场景中的所有图元 + if isinstance(nodet, RoundedRectangleItem): # 如果是圆角矩形 + if nodet.id == id: # 如果是当前选中的节点 + nodet.isSelected = False # 取消选中 + nodet.update() # 更新图元 + + def unselected_node(self, id): + self.selected_id.remove(id) + print(self.selected_id) + + def flash_node(self, id): # 还原节点为未选中状态 + for nodet in self.scene.items(): # 遍历场景中的所有图元 + if isinstance(nodet, RoundedRectangleItem): # 如果是圆角矩形 + if nodet.id == id: # 如果是当前选中的节点 + nodet.isSelected = False # 取消选中 + nodet.update() # 更新图元 + + def init_ui(self): + self.scene = QGraphicsScene() + self.graphicsView.setScene(self.scene) + + def init_slot(self): + self.pushButton.clicked.connect(self.on_calc) + + def load_image(self, path, Tree_4_nums, index): + def current_selection(value, path): + self.selected_value = value + self.p1_selected.setStyleSheet(f"border-image: url({path});") + self.pushButton_ast.setEnabled(False) + self.pushButton_allan.setEnabled(False) + self.pushButton_dtree.setEnabled(False) + self.pushButton_autosv.setEnabled(False) + # print(value) + + if index == 0: + self.p1.setStyleSheet(f"border-image: url({path});") + self.p1.clicked.connect(lambda: current_selection(Tree_4_nums[index], path)) + elif index == 1: + self.p2.setStyleSheet(f"border-image: url({path});") + self.p2.clicked.connect(lambda: current_selection(Tree_4_nums[index], path)) + elif index == 2: + self.p3.setStyleSheet(f"border-image: url({path});") + self.p3.clicked.connect(lambda: current_selection(Tree_4_nums[index], path)) + elif index == 3: + self.p4.setStyleSheet(f"border-image: url({path});") + self.p4.clicked.connect(lambda: current_selection(Tree_4_nums[index], path)) + + def show_card(self): + self.pushButton_ast.setEnabled(True) + self.pushButton_allan.setEnabled(True) + self.pushButton_dtree.setEnabled(True) + self.pushButton_autosv.setEnabled(True) + self.p1_selected.setStyleSheet("border-image: none;") + # 原始数组 + self.lineEdit.clear() + self.label_result.setText(" =? ") + self.label_TF.setText("") + original_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] + face_card = ['Club', 'Diamond', 'Heart', 'Spade'] + # 从原始数组中随机选择4个元素组成新列表 + self.Tree_4_nums = random.sample(original_list, 4) + # 从花色中随机选择一个 + for index, num in enumerate(self.Tree_4_nums): + card_face = random.choice(face_card) + if num == 11: + card_path = f"card/{card_face}/{card_face}j.png" + elif num == 12: + card_path = f"card/{card_face}/{card_face}q.png" + elif num == 13: + card_path = f"card/{card_face}/{card_face}k.png" + else: + card_path = f"card/{card_face}/{card_face}{num}.png" + self.load_image(card_path, self.Tree_4_nums, index) + # return self.Tree_4_nums + + def AutoTest(self): + if self.Tree_4_nums is None: + QMessageBox.warning(self, '警告', '请先出题') + return + else: + results = data.get_Aexps(self.Tree_4_nums) + if results == "无法得出24点": + self.lineEdit.clear() + self.lineEdit.setText("无法得出24点") + return + else: + if self.r_count < len(results): + self.lineEdit.clear() + self.lineEdit.setText(f"{results[self.r_count]}") + self.on_calc() # 自动计算 + self.draw_tree() # 绘制语法树 + self.r_count += 1 + else: + QMessageBox.information(self, '提示', '已经是最后一个表达式了') + self.r_count = 0 + + def AutoTest_9_10(self): + if self.Tree_4_nums is None: + QMessageBox.warning(self, '警告', '请先出题') + return + else: + aexp = data.gen_exp(self.Tree_4_nums) + if aexp == "无法得出24点": + self.lineEdit.clear() + self.lineEdit.setText("无法得出24点") + else: + self.lineEdit.clear() + self.lineEdit.setText(f"{aexp}") + self.on_calc() # 自动计算 + self.draw_tree() # 绘制语法树 + + def calculate_button(self): # 计算生成语法树 + self.scene.clear() + aexp = self.lineEdit.text() + try: + exp_ls = data.get_postfix_expression(aexp) # 获取后缀表达式列表 + self.TreeNode = data.ExpressionAnalyse(exp_ls) # 将后缀表达式转化为节点树 + result = data.calculate(self.TreeNode[-1], self.TreeNode) + self.label_result.setText(f"={round(result, 2)}") + except: + QMessageBox.warning(self, '警告', '表达式错误!') + return + if self.TreeNode: # 如果节点树不为空 + root = self.TreeNode[-1] + result = data.calculate(root, self.TreeNode) # 计算表达式的值 + self.label_result.setText(f"={round(result, 2)}") + self.result_ToF(int(result)) # 判断结果是否为24 + if self.has_both_operators(self.TreeNode): + self.offset = 35 + self.ShowTree(root, self.TreeNode) + self.offset = 0 + + def has_both_operators(self, TreeNode): # 判断是否同时有两个运算符 + ops = data.OPS + + def find_op_by_id(id): # 根据ID查找运算符 + for node in TreeNode: + if node.NodeID == id and node.NodeType == "Operator": + return node.Ops + + for node in TreeNode: # 遍历节点树 + if node.NodeType == "Operator": + opl = find_op_by_id(node.LeftNodeID) + opr = find_op_by_id(node.RightNodeID) + if opl in ops and opr in ops: + return True + + def draw_tree(self): + self.scene.clear() + aexp = self.lineEdit.text() + if aexp == "": + QMessageBox.warning(self, '警告', '请输入表达式') + return + try: + exp_ls = data.get_postfix_expression(aexp) # 获取后缀表达式列表 + self.TreeNode = data.ExpressionAnalyse(exp_ls) # 将后缀表达式转化为节点树 + root = self.TreeNode[-1] + if self.has_both_operators(self.TreeNode): + self.offset = 35 + self.ShowTree(root, self.TreeNode, depth=0) + self.offset = 0 + except Exception as e: + QMessageBox.warning(self, '警告', '表达式错误') + + def ShowTree(self, root, TreeNode, startx=0, starty=0, depth=0): + self.selected_id = [] # 清空选中的节点 + self.drawn_tree_id = set() # 清空已绘制的节点ID + # 绘制根节点 + if root.NodeType == 'Value': + nds_root = RoundedRectangleItem(startx, starty, root.FaceValue, root.NodeID) + nds_root.selected.connect(lambda id: self.selected_node(id)) + nds_root.unselected.connect(lambda id: self.unselected_node(id)) + self.drawn_tree_id.add(root.NodeID) + self.adds_id.add(root.NodeID) + else: + nds_root = RoundedRectangleItem(startx, starty, root.Ops, root.NodeID) + nds_root.selected.connect(lambda id: self.selected_node(id)) + nds_root.unselected.connect(lambda id: self.unselected_node(id)) + self.drawn_tree_id.add(root.NodeID) + self.adds_id.add(root.NodeID) + nds_root.setZValue(1) + self.scene.addItem(nds_root) + + # 计算节点间的水平偏移量 + horizontal_offset = 100 - depth * self.offset + + # 计算左子树和右子树的位置 + left_x, left_y = startx - horizontal_offset, starty + 100 + right_x, right_y = startx + horizontal_offset, starty + 100 + + # 绘制左子树 + if root.LeftNodeID is not None: + left_child = next(node for node in TreeNode if node.NodeID == root.LeftNodeID) + self.ShowTree(left_child, TreeNode, left_x, left_y, depth + 1) + + # 绘制左子树与根节点之间的连线 + line_left = QGraphicsLineItem(startx + 35, starty + 35, left_x + 35, left_y + 35) + line_left.setZValue(-1) + line_left.setPen(QPen(Qt.red, 3)) + self.scene.addItem(line_left) + + # 绘制右子树 + if root.RightNodeID is not None: + right_child = next(node for node in TreeNode if node.NodeID == root.RightNodeID) + self.ShowTree(right_child, TreeNode, right_x, right_y, depth + 1) + + # 绘制右子树与根节点之间的连线 + line_right = QGraphicsLineItem(startx + 35, starty + 35, right_x + 35, right_y + 35) + line_right.setZValue(-1) + line_right.setPen(QPen(Qt.red, 3)) + self.scene.addItem(line_right) + + def on_calc(self): + aexp = self.lineEdit.text() + if aexp == '': + QMessageBox.warning(self, '警告', '请输入表达式') + return + try: + result = eval(aexp) + self.label_result.setText(f"={int(result)}") + # 设置字体样式 + self.result_ToF(result) + except Exception as e: + QMessageBox.warning(self, '警告', '表达式错误') + return + + def result_ToF(self, result): + font = QFont('Microsoft YaHei', 16) + font.setBold(True) # 将字体设置为粗体 + self.label_TF.setText('√' if result == 24 else '×') + # 将字体样式应用到 QLabel 上 + self.label_TF.setFont(font) + # 将文本颜色设置为红色 + self.label_TF.setStyleSheet("color: red;background-color:rgb(219, 205, 166)") + + def agent_exp(self): + if self.TreeNode is not None: + exp_str = data.get_expression(self.TreeNode) + if exp_str is not None: + self.label_result.clear() # 清空显示结果 + self.lineEdit.clear() # 清空显示表达式 + self.lineEdit.setText(exp_str) # 显示表达式 + else: + QMessageBox.warning(self, '警告', '无法生成表达式') + else: + QMessageBox.warning(self, '警告', '当前无语法树') if __name__ == '__main__': - app = QApplication() - window = MainWindow() - window.show() - app.exec() + app = QApplication() + window = MainWindow() + window.show() + app.exec()