PyQt5 QGraphicsScene节点编号、位置更新及唯一ID存储咨询
解决方案
1. 优化节点坐标更新效率
原来通过遍历全局列表查找节点再更新坐标的方案时间复杂度为O(n),节点数量大时性能损耗明显,可通过以下方式优化为O(1)效率:
- 直接将节点ID、坐标作为
Node类的实例属性,不需要依赖全局列表存储,拖动节点时直接修改当前触发事件的Node实例自身属性即可,完全省去遍历查找过程。 - 废弃全局列表的存储方式,改用字典存储节点映射:键为唯一ID,值为Node实例,需要批量查询时直接通过ID取对象即可。
2. 唯一ID分配与快速检索实现
唯一ID生成
给Node类加类级别的自增ID计数器,每次实例化Node时自动分配唯一ID,不需要手动维护全局编号变量,不会出现ID重复问题。
ID存储
两种存储方式可以同时使用,适配不同检索场景:
- 作为Node的实例属性
self.node_id存储,直接通过实例访问。 - 利用QGraphicsItem内置的
setData()方法将ID存入Item的自定义数据域,后续从QGraphicsScene.items()返回的任意元素中都可以通过data()方法快速判断是否为Node节点、获取对应ID。
快速检索
在Scene类中维护两个映射字典:
self.node_map:键为node_id,值为Node实例,查询指定ID的节点时直接取值即可,无需遍历所有scene元素。self.group_map:键为连通组ID,值为该组所有节点ID的列表,批量删除连通组时直接遍历对应组的ID列表操作即可。
核心修改代码示例
修改后的Node类
class Node(QGraphicsEllipseItem): # 类级别的自增ID计数器 _next_id = 0 def __init__(self, rect=QRectF(-20, -20, 20, 20), parent=None): super().__init__(rect, parent) # 分配唯一ID self.node_id = Node._next_id Node._next_id += 1 # 将ID存入Item内置数据域,自定义键从Qt.UserRole开始 self.setData(Qt.UserRole, self.node_id) self.edges = [] # 原有初始化逻辑保持不变 self.setZValue(1) self.setBrush(Qt.darkGray) self.setFlag(QGraphicsItem.ItemIsMovable, True) self.setFlag(QGraphicsItem.ItemIsSelectable, True) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True) def mouseMoveEvent(self, event): origCursorPos = event.lastScenePos() actualCursorPos = event.scenePos() origPos = self.scenePos() new_x = actualCursorPos.x() - origCursorPos.x() + origPos.x() new_y = actualCursorPos.y() - origCursorPos.y() + origPos.y() self.setPos(QPointF(new_x, new_y)) # 直接读取当前节点的ID和坐标,无需遍历全局列表 print(f"节点ID {self.node_id} 新坐标: {new_x:.1f}, {new_y:.1f}")
修改后的Scene类
class SceneClass(QGraphicsScene): prepStartEndNode = 0 def __init__(self, parent=None): super().__init__(QRectF(0, 0, 2000, 2000), parent) self.node_start = None self.node_end = None # 节点ID到实例的映射 self.node_map = {} # 连通组ID到节点ID列表的映射 self.group_map = {} self._current_group_id = 0 # 新增节点时存入映射的逻辑示例 def mouseDoubleClickEvent(self, event): if event.button() == Qt.LeftButton and self.node_start is None: SceneClass.prepStartEndNode = 1 node = Node() # 新增节点存入映射 self.node_map[node.node_id] = node # 新建连通组 self.group_map[self._current_group_id] = [node.node_id] self.addItem(node) node.setPos(event.scenePos() + QPointF(10, 10)) self.node_start = node else: self.node_start = None SceneClass.prepStartEndNode = 0 # 当前连通组创建完成,组ID自增 self._current_group_id += 1 self.saveNodeToGlobalList()
检索与删除示例
# 检索指定ID的节点 target_node = self.node_map.get(3) if target_node: print(f"找到节点ID3,坐标:{target_node.pos().x()}, {target_node.pos().y()}") # 批量删除指定连通组 def delete_group(self, group_id): if group_id not in self.group_map: return for node_id in self.group_map[group_id]: node = self.node_map.pop(node_id) # 先删除关联边 for edge in node.edges: self.removeItem(edge) # 再删除节点 self.removeItem(node) del self.group_map[group_id] # 从scene.items()中遍历筛选所有节点 for item in self.items(): node_id = item.data(Qt.UserRole) if node_id is not None: print(f"遍历到节点ID:{node_id}")
内容的提问来源于stack exchange,提问作者Pr.Syn
相关产品推荐
相关产品推荐

