""" 图形化编辑器控件 用于可视化编辑映射图 """ from PyQt5.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QGraphicsView, QGraphicsScene, QGraphicsEllipseItem, QGraphicsLineItem, QGraphicsTextItem, QPushButton, QToolBar, QAction ) from PyQt5.QtCore import Qt, QPointF, QRectF, pyqtSignal from PyQt5.QtGui import QPen, QBrush, QColor, QPainter from models.mapping_graph import MappingGraph, GraphNode, GraphEdge from config import Config from utils.logger import get_logger logger = get_logger(__name__) class GraphNodeItem(QGraphicsEllipseItem): """图节点图形项""" def __init__(self, node: GraphNode, x: float, y: float): super().__init__(0, 0, Config.NODE_WIDTH, Config.NODE_HEIGHT) self.node = node self.setPos(x, y) # 设置样式 self.setBrush(QBrush(QColor(33, 150, 243))) self.setPen(QPen(QColor(25, 118, 210), 2)) # 可移动 self.setFlag(QGraphicsEllipseItem.ItemIsMovable) self.setFlag(QGraphicsEllipseItem.ItemIsSelectable) self.setFlag(QGraphicsEllipseItem.ItemSendsGeometryChanges) # 添加文本标签 self.text_item = QGraphicsTextItem(self) self.text_item.setPlainText(node.field.name) self.text_item.setDefaultTextColor(Qt.white) # 居中文本 text_rect = self.text_item.boundingRect() text_x = (Config.NODE_WIDTH - text_rect.width()) / 2 text_y = (Config.NODE_HEIGHT - text_rect.height()) / 2 self.text_item.setPos(text_x, text_y) def itemChange(self, change, value): """节点位置改变时的处理""" if change == QGraphicsEllipseItem.ItemPositionChange: # 更新连接的边 pass return super().itemChange(change, value) class GraphEdgeItem(QGraphicsLineItem): """图边图形项""" def __init__(self, edge: GraphEdge, source_item: GraphNodeItem, target_item: GraphNodeItem): super().__init__() self.edge = edge self.source_item = source_item self.target_item = target_item # 设置样式 self.setPen(QPen(QColor(100, 100, 100), Config.EDGE_WIDTH)) # 更新位置 self.update_position() def update_position(self): """更新边的位置""" source_center = self.source_item.sceneBoundingRect().center() target_center = self.target_item.sceneBoundingRect().center() self.setLine( source_center.x(), source_center.y(), target_center.x(), target_center.y() ) class GraphViewWidget(QGraphicsView): """图形视图控件""" node_selected = pyqtSignal(GraphNode) edge_selected = pyqtSignal(GraphEdge) def __init__(self): super().__init__() # 创建场景 self.scene = QGraphicsScene() self.setScene(self.scene) # 设置视图属性 self.setRenderHint(QPainter.Antialiasing) self.setDragMode(QGraphicsView.ScrollHandDrag) self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) # 存储图形项 self.node_items = {} # field_id -> GraphNodeItem self.edge_items = {} # edge_id -> GraphEdgeItem logger.info("Graph view widget initialized") def load_graph(self, graph: MappingGraph): """加载映射图""" self.scene.clear() self.node_items.clear() self.edge_items.clear() # 添加节点 for field_id, node in graph.nodes.items(): self.add_node(node) # 添加边 for edge in graph.edges: self.add_edge(edge) logger.info(f"Loaded graph: {len(graph.nodes)} nodes, {len(graph.edges)} edges") def add_node(self, node: GraphNode): """添加节点""" node_item = GraphNodeItem(node, node.x, node.y) self.scene.addItem(node_item) self.node_items[node.field.id] = node_item def add_edge(self, edge: GraphEdge): """添加边""" source_item = self.node_items.get(edge.source_field_id) target_item = self.node_items.get(edge.target_field_id) if source_item and target_item: edge_item = GraphEdgeItem(edge, source_item, target_item) self.scene.addItem(edge_item) if edge.id: self.edge_items[edge.id] = edge_item def auto_layout(self): """自动布局(简单的圆形布局)""" import math nodes = list(self.node_items.values()) if not nodes: return center_x = 400 center_y = 300 radius = 200 angle_step = 2 * math.pi / len(nodes) for i, node_item in enumerate(nodes): angle = i * angle_step x = center_x + radius * math.cos(angle) y = center_y + radius * math.sin(angle) node_item.setPos(x, y) # 更新所有边 for edge_item in self.edge_items.values(): edge_item.update_position() logger.info("Auto layout applied") def wheelEvent(self, event): """鼠标滚轮缩放""" factor = 1.2 if event.angleDelta().y() > 0 else 0.8 self.scale(factor, factor)