|
|
|
|
import random
|
|
|
|
|
|
|
|
|
|
from PySide6.QtGui import QFont
|
|
|
|
|
from PySide6.QtWidgets import QApplication, QMainWindow, QGraphicsLineItem, QMessageBox, QGraphicsScene
|
|
|
|
|
|
|
|
|
|
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.question_card) # 出题
|
|
|
|
|
self.pushButton_ex.clicked.connect(self.change_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) # 生成表达式
|
|
|
|
|
self.pushButton_check_te.clicked.connect(self.check_tree_and_expression) # 验证语法树和表达式是否匹配
|
|
|
|
|
|
|
|
|
|
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("") # 运算符显示
|
|
|
|
|
self.p1_selected.setStyleSheet("border-image: none;") # 清空已选牌面
|
|
|
|
|
|
|
|
|
|
self.pushButton_ast.setEnabled(True)
|
|
|
|
|
self.pushButton_allan.setEnabled(True)
|
|
|
|
|
self.pushButton_dtree.setEnabled(True)
|
|
|
|
|
self.pushButton_autosv.setEnabled(True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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, '警告', '请先选择一个运算符')
|
|
|
|
|
|
|
|
|
|
# 根据节点ID,找到对应的节点
|
|
|
|
|
def find_node(self, id):
|
|
|
|
|
for node in self.TreeNode:
|
|
|
|
|
if node.NodeID == id:
|
|
|
|
|
return node
|
|
|
|
|
def exchange_value_node(self):
|
|
|
|
|
# 修改节点值
|
|
|
|
|
if len(self.selected_id) == 1:
|
|
|
|
|
node = self.find_node(self.selected_id[0])
|
|
|
|
|
if node.NodeType == 'Operator':
|
|
|
|
|
QMessageBox.warning(self, '警告', '请选择数值节点')
|
|
|
|
|
return
|
|
|
|
|
if self.selected_value:
|
|
|
|
|
if self.TreeNode:
|
|
|
|
|
for node in self.TreeNode:
|
|
|
|
|
if node.NodeID == self.selected_id[0]:
|
|
|
|
|
node.FaceValue = str(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:
|
|
|
|
|
node = self.find_node(self.selected_id[0])
|
|
|
|
|
if node.NodeType == 'Value':
|
|
|
|
|
QMessageBox.warning(self, '警告', '请选择运算符节点')
|
|
|
|
|
return
|
|
|
|
|
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 = str(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):
|
|
|
|
|
# 原始数组
|
|
|
|
|
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 question_card(self): # 显示题目卡片
|
|
|
|
|
self.secen_clear() # 清空画布
|
|
|
|
|
self.lineEdit.clear()
|
|
|
|
|
self.label_result.setText(" =? ")
|
|
|
|
|
self.label_TF.setText("")
|
|
|
|
|
self.show_card() # 显示卡片
|
|
|
|
|
|
|
|
|
|
# 换牌
|
|
|
|
|
|
|
|
|
|
def change_card(self):
|
|
|
|
|
self.show_card() # 显示卡片
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 check_tree_and_expression(self): # 检查节点树和表达式是否一致
|
|
|
|
|
if self.TreeNode is not None:
|
|
|
|
|
aexp = self.lineEdit.text()
|
|
|
|
|
if aexp == '':
|
|
|
|
|
QMessageBox.warning(self, '警告', '请输入表达式')
|
|
|
|
|
return
|
|
|
|
|
try:
|
|
|
|
|
exp_ls = data.get_postfix_expression(aexp) # 获取后缀表达式列表
|
|
|
|
|
TreeNode2 = data.ExpressionAnalyse(exp_ls) # 将后缀表达式转化为节点树
|
|
|
|
|
if self.check_tree(self.TreeNode, TreeNode2):
|
|
|
|
|
QMessageBox.information(self, '提示', '节点树与表达式一致')
|
|
|
|
|
else:
|
|
|
|
|
QMessageBox.warning(self, '警告', '节点树与表达式不一致')
|
|
|
|
|
except Exception as e:
|
|
|
|
|
QMessageBox.warning(self, '警告', '表达式错误')
|
|
|
|
|
return
|
|
|
|
|
else:
|
|
|
|
|
QMessageBox.warning(self, '警告', '当前无语法树')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 检查两个节点树是否一致的函数
|
|
|
|
|
|
|
|
|
|
def check_tree(self, TreeNode1, TreeNode2):
|
|
|
|
|
if len(TreeNode1) != len(TreeNode2):
|
|
|
|
|
return False
|
|
|
|
|
for i in range(len(TreeNode1)):
|
|
|
|
|
if TreeNode1[i].NodeID != TreeNode2[i].NodeID:
|
|
|
|
|
return False
|
|
|
|
|
if TreeNode1[i].NodeType != TreeNode2[i].NodeType:
|
|
|
|
|
return False
|
|
|
|
|
if TreeNode1[i].Ops != TreeNode2[i].Ops:
|
|
|
|
|
return False
|
|
|
|
|
if TreeNode1[i].FaceValue != TreeNode2[i].FaceValue:
|
|
|
|
|
return False
|
|
|
|
|
if TreeNode1[i].LeftNodeID != TreeNode2[i].LeftNodeID:
|
|
|
|
|
return False
|
|
|
|
|
if TreeNode1[i].RightNodeID != TreeNode2[i].RightNodeID:
|
|
|
|
|
return False
|
|
|
|
|
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 len(self.TreeNode) > 0:
|
|
|
|
|
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()
|