@@ -687,6 +687,8 @@ def removeNodesFrom(self, nodes):
687687 Args:
688688 startNode (Node): the node to start from.
689689 """
690+ if isinstance (nodes , Node ):
691+ nodes = [nodes ]
690692 with self .groupedGraphModification ("Remove Nodes From Selected Nodes" ):
691693 nodesToRemove , _ = self ._graph .dfsOnDiscover (startNodes = nodes , reverse = True , dependenciesOnly = True )
692694 # filter out nodes that will be removed more than once
@@ -706,7 +708,7 @@ def duplicateNodes(self, nodes):
706708 list[Node]: the list of duplicated nodes
707709 """
708710 nodes = self .filterNodes (nodes )
709- nPositions = []
711+ nPositions = [( n . x , n . y ) for n in self . _graph . nodes ]
710712 # enable updates between duplication and layout to get correct depths during layout
711713 with self .groupedGraphModification ("Duplicate Selected Nodes" , disableUpdates = False ):
712714 # disable graph updates during duplication
@@ -716,9 +718,8 @@ def duplicateNodes(self, nodes):
716718 bbox = self ._layout .boundingBox (nodes )
717719
718720 for n in duplicates :
719- idx = duplicates .index (n )
720721 yPos = n .y + self .layout .gridSpacing + bbox [3 ]
721- if idx > 0 and (n .x , yPos ) in nPositions :
722+ if (n .x , yPos ) in nPositions :
722723 # make sure the node will not be moved on top of another node
723724 while (n .x , yPos ) in nPositions :
724725 yPos = yPos + self .layout .gridSpacing + self .layout .nodeHeight
@@ -739,12 +740,62 @@ def duplicateNodesFrom(self, nodes):
739740 Returns:
740741 list[Node]: the list of duplicated nodes
741742 """
743+ if isinstance (nodes , Node ):
744+ nodes = [nodes ]
742745 with self .groupedGraphModification ("Duplicate Nodes From Selected Nodes" ):
743746 nodesToDuplicate , _ = self ._graph .dfsOnDiscover (startNodes = nodes , reverse = True , dependenciesOnly = True )
744747 # filter out nodes that will be duplicated more than once
745748 uniqueNodesToDuplicate = list (dict .fromkeys (nodesToDuplicate ))
746749 duplicates = self .duplicateNodes (uniqueNodesToDuplicate )
747750 return duplicates
751+
752+ @Slot (Edge , result = bool )
753+ def canExpandForLoop (self , currentEdge ):
754+ """ Check if the list attribute can be expanded by looking at all the edges connected to it. """
755+ listAttribute = currentEdge .src .root
756+ if not listAttribute :
757+ return False
758+ srcIndex = listAttribute .index (currentEdge .src )
759+ allSrc = [e .src for e in self ._graph .edges .values ()]
760+ for i in range (len (listAttribute )):
761+ if i == srcIndex :
762+ continue
763+ if listAttribute .at (i ) in allSrc :
764+ return False
765+ return True
766+
767+ @Slot (Edge , result = Edge )
768+ def expandForLoop (self , currentEdge ):
769+ """ Expand 'node' by creating all its output nodes. """
770+ with self .groupedGraphModification ("Expand For Loop Node" ):
771+ listAttribute = currentEdge .src .root
772+ dst = currentEdge .dst
773+
774+ for i in range (1 , len (listAttribute )):
775+ duplicates = self .duplicateNodesFrom (dst .node )
776+ newNode = duplicates [0 ]
777+ previousEdge = self .graph .edge (newNode .attribute (dst .name ))
778+ self .replaceEdge (previousEdge , listAttribute .at (i ), previousEdge .dst )
779+
780+ # Last, replace the edge with the first element of the list
781+ return self .replaceEdge (currentEdge , listAttribute .at (0 ), dst )
782+
783+ @Slot (Edge )
784+ def collapseForLoop (self , currentEdge ):
785+ """ Collapse 'node' by removing all its output nodes. """
786+ with self .groupedGraphModification ("Collapse For Loop Node" ):
787+ listAttribute = currentEdge .src .root
788+ srcIndex = listAttribute .index (currentEdge .src )
789+ allSrc = [e .src for e in self ._graph .edges .values ()]
790+ for i in reversed (range (len (listAttribute ))):
791+ if i == srcIndex :
792+ continue
793+ occurence = allSrc .index (listAttribute .at (i )) if listAttribute .at (i ) in allSrc else - 1
794+ if occurence != - 1 :
795+ self .removeNodesFrom (self .graph .edges .at (occurence ).dst .node )
796+ # update the edges from allSrc
797+ allSrc = [e .src for e in self ._graph .edges .values ()]
798+
748799
749800 @Slot (QObject )
750801 def clearData (self , nodes ):
@@ -765,7 +816,9 @@ def clearDataFrom(self, nodes):
765816
766817 @Slot (Attribute , Attribute )
767818 def addEdge (self , src , dst ):
768- if isinstance (dst , ListAttribute ) and not isinstance (src , ListAttribute ):
819+ if isinstance (src , ListAttribute ) and not isinstance (dst , ListAttribute ):
820+ self ._addEdge (src .at (0 ), dst )
821+ elif isinstance (dst , ListAttribute ) and not isinstance (src , ListAttribute ):
769822 with self .groupedGraphModification ("Insert and Add Edge on {}" .format (dst .getFullNameToNode ())):
770823 self .appendAttribute (dst )
771824 self ._addEdge (src , dst .at (- 1 ))
@@ -787,14 +840,32 @@ def removeEdge(self, edge):
787840 else :
788841 self .push (commands .RemoveEdgeCommand (self ._graph , edge ))
789842
843+ @Slot (Edge , Attribute , Attribute , result = Edge )
844+ def replaceEdge (self , edge , newSrc , newDst ):
845+ with self .groupedGraphModification ("Replace Edge '{}'->'{}' with '{}'->'{}'" .format (edge .src .getFullNameToNode (), edge .dst .getFullNameToNode (), newSrc .getFullNameToNode (), newDst .getFullNameToNode ())):
846+ self .removeEdge (edge )
847+ self .addEdge (newSrc , newDst )
848+ return self ._graph .edge (newDst )
849+
850+ @Slot (Attribute , result = Edge )
851+ def getEdge (self , dst ):
852+ return self ._graph .edge (dst )
853+
790854 @Slot (Attribute , "QVariant" )
791855 def setAttribute (self , attribute , value ):
792856 self .push (commands .SetAttributeCommand (self ._graph , attribute , value ))
793857
794858 @Slot (Attribute )
795859 def resetAttribute (self , attribute ):
796860 """ Reset 'attribute' to its default value """
797- self .push (commands .SetAttributeCommand (self ._graph , attribute , attribute .defaultValue ()))
861+ with self .groupedGraphModification ("Reset Attribute '{}'" .format (attribute .name )):
862+ # if the attribute is a ListAttribute, remove all edges
863+ if isinstance (attribute , ListAttribute ):
864+ for edge in self ._graph .edges :
865+ # if the edge is connected to one of the ListAttribute's elements, remove it
866+ if edge .src in attribute .value :
867+ self .removeEdge (edge )
868+ self .push (commands .SetAttributeCommand (self ._graph , attribute , attribute .defaultValue ()))
798869
799870 @Slot (CompatibilityNode , result = Node )
800871 def upgradeNode (self , node ):
0 commit comments