diff --git a/typetapper/hierarchy_graph_view.py b/typetapper/hierarchy_graph_view.py index d39cc5c..ff60d1f 100644 --- a/typetapper/hierarchy_graph_view.py +++ b/typetapper/hierarchy_graph_view.py @@ -8,7 +8,7 @@ from threading import Lock import logging from PySide6.QtCore import QRectF, QPointF, QTimeLine, QEasingCurve, QMarginsF, QTimer, Qt, QEvent -from PySide6.QtGui import QColor, QFont, QBrush, QPainterPath, QPen, QAction, QShortcut +from PySide6.QtGui import QColor, QFont, QBrush, QPainterPath, QPen, QAction, QShortcut, QMouseEvent from PySide6.QtWidgets import QGraphicsItem, QGraphicsRectItem, QGraphicsSimpleTextItem, QHBoxLayout, QGraphicsScene, \ QGraphicsEllipseItem, QGraphicsSceneMouseEvent, QMenu, QDialog, QVBoxLayout, QLineEdit, QInputDialog, QMessageBox, \ QGraphicsLineItem, QGraphicsTextItem @@ -106,6 +106,7 @@ class HierarchicalGraphWidget(QZoomableDraggableGraphicsView): self._layout_animation = QTimer() self._layout_animation_controller = QTimer() self._mouse_is_down = False + self._rapid_expand = False self._selection_event_occurring = False self._layout_event_occurring = False self.faux_frontier = faux_frontier or set() @@ -213,15 +214,17 @@ class HierarchicalGraphWidget(QZoomableDraggableGraphicsView): def begin_expand(self, model: RelativeAtomOrGroup): self.expansion_models = [adj for adj in set(chain(self.ng.succ[model], self.ng.pred[model])) if adj in self.faux_frontier] if not self.expansion_models: - l.error("How'd you do that") return + for other_model in self.expansion_models: + self._do_expand(other_model) + self._layout(0) qnode = self.qnodes[self.ug_reverse[model]] self.scene().clearSelection() qnode.setSelected(True) for other_qnode in self.qnodes: other_qnode.setAcceptHoverEvents(False) - other_qnode.setAcceptedMouseButtons(Qt.MouseButton.NoButton | Qt.MouseButton.NoButton) + other_qnode.setAcceptedMouseButtons(Qt.MouseButtons.NoButton) if qnode is other_qnode: other_qnode.setOpacity(0.8) else: @@ -241,7 +244,7 @@ class HierarchicalGraphWidget(QZoomableDraggableGraphicsView): qnode, other_qnode, other_model in self.ng.succ[model], - other_model in self.ng.pred[model] + other_model in self.ng.pred[model], ) for other_qnode, other_model in zip(self.expansion_qnodes, self.expansion_models)] tmp_ag = pygraphviz.AGraph() @@ -259,7 +262,7 @@ class HierarchicalGraphWidget(QZoomableDraggableGraphicsView): self.scene().addItem(other_qnode) other_qnode.center = parse_pos(tmp_ag.get_node(idx).attr['pos']) - origin + qnode.center other_qnode.enter() - other_qnode.set_expandable(False) + other_qnode.set_expandable(self.can_expand(other_qnode.model)) for qedge in self.expansion_qedges: self.scene().addItem(qedge) @@ -275,7 +278,7 @@ class HierarchicalGraphWidget(QZoomableDraggableGraphicsView): def end_expand(self, accepted_qnode: Optional['PropChartHG']=None): for qnode in self.qnodes: qnode.setAcceptHoverEvents(True) - qnode.setAcceptedMouseButtons(Qt.MouseButton.AllButtons | Qt.MouseButton.AllButtons) + qnode.setAcceptedMouseButtons(Qt.MouseButtons.AllButtons) qnode.setOpacity(1) for edge in self.qedges: edge.setOpacity(1) @@ -300,22 +303,8 @@ class HierarchicalGraphWidget(QZoomableDraggableGraphicsView): self.expansion_qnodes.clear() self.expansion_models.clear() - if accepted_qnode: + if accepted_qnode is not None and accepted_qnode.model in self.faux_frontier: self.faux_frontier.remove(accepted_qnode.model) - existing_groups = [(g, self.label(g)) for g in self.current_group.children if isinstance(g, RelativeAtomGroup)] - existing_groups = { - self.instance.kb.functions[lbl].addr: g - for (g, lbl) in existing_groups - if lbl in self.instance.kb.functions - } - newgroups = self.hg.expand_by_key(accepted_qnode.model, self.current_group.am_obj, self.hg.kp.atom_to_function, dict(existing_groups)) - for k, g in newgroups.items(): - if k in existing_groups: - continue - if k in self.hg.kp.kb.functions and self.hg.kp.kb.functions[k].alignment: - self.ignore.add(g) - else: - self.faux_frontier.add(g) self._layout(0) def set_drop_target(self, item: Optional['PropChartHG']): @@ -441,6 +430,22 @@ class HierarchicalGraphWidget(QZoomableDraggableGraphicsView): # private interfaces + def _do_expand(self, model: RelativeAtomOrGroup): + existing_groups = [(g, self.label(g)) for g in self.current_group.children if isinstance(g, RelativeAtomGroup)] + existing_groups = { + self.instance.kb.functions[lbl].addr: g + for (g, lbl) in existing_groups + if lbl in self.instance.kb.functions + } + newgroups = self.hg.expand_by_key(model, self.current_group.am_obj, self.hg.kp.atom_to_function, dict(existing_groups)) + for k, g in newgroups.items(): + if k in existing_groups: + continue + if k in self.hg.kp.kb.functions and self.hg.kp.kb.functions[k].alignment: + self.ignore.add(g) + else: + self.faux_frontier.add(g) + def _rekey_externs(self, old_qnode: 'PropChartHG', new_qnode: 'PropChartHG'): old_extern_model = ExternNode(old_qnode.model) new_extern_model = ExternNode(new_qnode.model) @@ -648,12 +653,27 @@ class HierarchicalGraphWidget(QZoomableDraggableGraphicsView): def viewportEvent(self, event): if event.type() == QEvent.Type.ContextMenu: return False + elif isinstance(event, QMouseEvent) and event.type() == QMouseEvent.Type.MouseButtonPress: + if event.button() == Qt.MouseButton.LeftButton: + self._mouse_is_down = True + + if (event.modifiers() & Qt.ShiftModifier) == Qt.NoModifier: + self.setDragMode(QZoomableDraggableGraphicsView.ScrollHandDrag) + else: + self.setDragMode(QZoomableDraggableGraphicsView.RubberBandDrag) + self.viewport().setCursor(Qt.CursorShape.CrossCursor) return super().viewportEvent(event) def keyPressEvent(self, event): if event.key() == Qt.Key_Z and not self.is_expanding: self._layout_animation.start() event.accept() + elif event.key() == Qt.Key_X: + if event.modifiers() & Qt.KeyboardModifier.ControlModifier: + self._rapid_expand ^= True + else: + self._rapid_expand = True + event.accept() else: super().keyPressEvent(event) @@ -661,6 +681,12 @@ class HierarchicalGraphWidget(QZoomableDraggableGraphicsView): if event.key() == Qt.Key_Z: self._layout_animation.stop() event.accept() + elif event.key() == Qt.Key_X: + if event.modifiers() & Qt.KeyboardModifier.ControlModifier: + self._rapid_expand ^= True + else: + self._rapid_expand = False + event.accept() super().keyReleaseEvent(event) def mouseDoubleClickEvent(self, event): @@ -672,16 +698,12 @@ class HierarchicalGraphWidget(QZoomableDraggableGraphicsView): self.current_group.am_event() def mousePressEvent(self, event): - self._mouse_is_down = True - if (event.modifiers() & Qt.ShiftModifier) == Qt.NoModifier: - self.setDragMode(QZoomableDraggableGraphicsView.ScrollHandDrag) - else: - self.setDragMode(QZoomableDraggableGraphicsView.RubberBandDrag) - self.viewport().setCursor(Qt.CursorShape.CrossCursor) super(QZoomableDraggableGraphicsView, self).mousePressEvent(event) # lol. lmao even def mouseReleaseEvent(self, event): - self._mouse_is_down = False + if event.button() == Qt.MouseButton.LeftButton: + self._mouse_is_down = False + if self.drop_target is not None: self.confirm_drop() super().mouseReleaseEvent(event) @@ -723,6 +745,10 @@ class HierarchicalGraphWidget(QZoomableDraggableGraphicsView): self._selection_event_occurring = False def _on_selection_changed_internal(self): + if not self.items(): + # don't do anything if we are closing the pane (there will be a desync between items() and qnodes/qedges) + return + if self.is_expanding: items = self.scene().selectedItems() if not items: @@ -735,10 +761,8 @@ class HierarchicalGraphWidget(QZoomableDraggableGraphicsView): else: l.error("How'd you select more than one item during expansion") elif not self._selection_event_occurring: - if self.items(): - # don't do this if we are closing the pane (there will be a desync between items() and qnodes) - self.selected_nodes.am_obj = {model for model, qnode in zip(self.ug_lookup, self.qnodes) if qnode.isSelected()} - self.selected_nodes.am_event(src='qt') + self.selected_nodes.am_obj = {model for model, qnode in zip(self.ug_lookup, self.qnodes) if qnode.isSelected()} + self.selected_nodes.am_event(src='qt') for qedge in self.qedges: qedge.update() @@ -1013,6 +1037,9 @@ class PropChartHG(HGNode, PropChart): self.object_btn: Optional[PlusButton] = None super().__init__(parent, **kwargs) + self.setAcceptHoverEvents(True) + self.setAcceptedMouseButtons(Qt.MouseButtons.AllButtons) + @property def label(self): return self.hgw.label(self.model) @@ -1059,7 +1086,6 @@ class PropChartHG(HGNode, PropChart): else: obj.setY(obj.y() + lbl_height) - self.setTransformOriginPoint(self.center - self.pos()) def set_expandable(self, expandable: bool): @@ -1067,6 +1093,7 @@ class PropChartHG(HGNode, PropChart): def begin_expand(self): if self.timeline.state() == QTimeLine.NotRunning: + self.hgw.end_expand(self) self.hgw.begin_expand(self.model) return True else: @@ -1120,8 +1147,13 @@ class PropChartHG(HGNode, PropChart): break else: self.hgw.set_drop_target(None) + super().mouseMoveEvent(event) + def hoverEnterEvent(self, event): + if self.hgw._rapid_expand: + self.begin_expand() + def emphasize(self, duration=100): self._start_animation(self._animate_emphasize, duration)