diff --git a/typetapper/hierarchy_graph_view.py b/typetapper/hierarchy_graph_view.py index f1aa513..e9b9668 100644 --- a/typetapper/hierarchy_graph_view.py +++ b/typetapper/hierarchy_graph_view.py @@ -78,6 +78,8 @@ class HierarchicalGraphWidget(QZoomableDraggableGraphicsView): self.current_group: Union[ObjectContainer, RelativeAtomGroup] = ObjectContainer(hg.root_group if current_group is None else current_group) self.selected_nodes: Union[ObjectContainer, Set[RelativeAtomOrGroupOrExtern]] = ObjectContainer(set()) self.instance = instance + self._old_group: RelativeAtomGroup = self.current_group.am_obj + self._layouts: Dict[RelativeAtomGroup, Dict[RelativeAtomOrGroupOrExtern, QPointF]] = defaultdict(dict) self.qnodes: List[HGNode] = [] self.qedges: List[HGArrow] = [] @@ -208,7 +210,7 @@ class HierarchicalGraphWidget(QZoomableDraggableGraphicsView): qedge.layout() qedge.enter() - self._update_scene_rect() + self._update_scene_rect(False) @property def is_expanding(self): @@ -397,25 +399,20 @@ class HierarchicalGraphWidget(QZoomableDraggableGraphicsView): if ibr.isEmpty(): ibr = QRectF(0, -1000, 1000, 1000) for i, node in enumerate(self.ug_lookup): - if isinstance(node, ExternNode): - qnode = old_nodes.pop(node, None) - if qnode is None: + qnode = old_nodes.pop(node, None) + if qnode is None: + if isinstance(node, ExternNode): qnode = HGExtern(None, model=node, hgw=self) - entering.add(qnode) - qnode.center = rand_point(ibr, i) - self.qnodes.append(qnode) - else: - qnode = old_nodes.pop(node, None) - if qnode is None: + else: qnode = PropChartHG( None, byte_height=byte_height, model=node, hgw=self, ) - entering.add(qnode) - qnode.center = rand_point(ibr, i) - self.qnodes.append(qnode) + entering.add(qnode) + qnode.center = self._layouts[self.current_group.am_obj].get(node, rand_point(ibr, i)) + self.qnodes.append(qnode) for model, qnode in zip(self.ug_lookup, self.qnodes): qnode.set_expandable(self.can_expand(model)) @@ -459,9 +456,9 @@ class HierarchicalGraphWidget(QZoomableDraggableGraphicsView): qnode.enter() self.ag = networkx.drawing.nx_agraph.to_agraph(self.ug) - self._iterate_layout(maxiter) + self._iterate_layout(maxiter, pin_known=True) - def _iterate_layout(self, maxiter=1): + def _iterate_layout(self, maxiter=1, pin_known=False): if not maxiter: return if self._layout_lock.acquire(blocking=False): @@ -472,7 +469,7 @@ class HierarchicalGraphWidget(QZoomableDraggableGraphicsView): qnode = self.qnodes[idx] qnode.set_agraph_node_properties(node) node.attr['pos'] = render_pos(qnode.center) - node.attr['pin'] = str(qnode.pinned).lower() + node.attr['pin'] = str(qnode.pinned or (pin_known and qnode.model in self._layouts[self.current_group.am_obj])).lower() for edge in self.ag.edges_iter(): edge.attr['len'] = self.layout_social_distancing self.ag.graph_attr['overlap'] = self.layout_overlap @@ -493,27 +490,39 @@ class HierarchicalGraphWidget(QZoomableDraggableGraphicsView): for edge in self.qedges: edge.layout() - self._update_scene_rect() + self._update_scene_rect(False) finally: self._layout_event_occurring = False self._layout_lock.release() else: l.warning("Layout lock conflict. Is the application lagging?") - def _update_scene_rect(self): + def _update_scene_rect(self, scene_change): rect = None for item in self.items(): + if isinstance(item, AnimatableItem) and item.exiting: + continue br = item.boundingRect() br.translate(item.x(), item.y()) rect = rect.united(br) if rect is not None else br - if rect is None: - rect = self.mapToScene(self.viewport().rect()).boundingRect() + + if not scene_change: + if rect is None: + rect = self.mapToScene(self.viewport().rect()).boundingRect() + else: + rect = rect.marginsAdded(QMarginsF(SCENE_MARGIN, SCENE_MARGIN, SCENE_MARGIN, SCENE_MARGIN)) + rect = rect.united(self.mapToScene(self.viewport().rect()).boundingRect()) + center = self.mapToScene(self.viewport().rect().center()) + self.setSceneRect(rect) + self.centerOn(center) else: - rect = rect.marginsAdded(QMarginsF(SCENE_MARGIN, SCENE_MARGIN, SCENE_MARGIN, SCENE_MARGIN)) - rect = rect.united(self.mapToScene(self.viewport().rect()).boundingRect()) - center = self.mapToScene(self.viewport().rect().center()) - self.setSceneRect(rect) - self.centerOn(center) + if rect is None: + rect = QRectF() + else: + rect = rect.marginsAdded(QMarginsF(SCENE_MARGIN, SCENE_MARGIN, SCENE_MARGIN, SCENE_MARGIN)) + self.setSceneRect(rect) + self.resetTransform() + self.centerOn(self.sceneRect().center()) # qt overrides @@ -550,9 +559,10 @@ class HierarchicalGraphWidget(QZoomableDraggableGraphicsView): # objectcontainer event handlers def _on_change_group(self, **kwargs): + self._layouts[self._old_group] = {qnode.model: qnode.center for qnode in self.qnodes} + self._old_group = self.current_group.am_obj self._layout(600) - self.resetTransform() - self.centerOn(self.sceneRect().center()) + self._update_scene_rect(True) def _on_selection_changed(self, src=None, **kwargs): if src == 'qt': @@ -586,6 +596,7 @@ class HierarchicalGraphWidget(QZoomableDraggableGraphicsView): class AnimatableItem(QGraphicsItem): def __init__(self, *args, **kwargs): self.timeline: Optional[QTimeLine] = None + self.exiting = False super().__init__(*args, **kwargs) def _start_animation(self, animation, duration, finished=None, ease=QEasingCurve.Linear): @@ -614,6 +625,7 @@ class HGNode(AnimatableItem): # public interfaces def exit(self, duration=250): + self.exiting = True for edge in self.edges.values(): edge.orient(self, away=True) edge.exit(duration) @@ -967,6 +979,7 @@ class HGArrow(AnimatableItem): self.percentage = value def exit(self, duration=250): + self.exiting = True self._start_animation(self._animate_exit, duration, self._finish_exit) def _animate_exit(self, value):