@@ -687,6 +687,8 @@ def removeNodesFrom(self, nodes):
687
687
Args:
688
688
startNode (Node): the node to start from.
689
689
"""
690
+ if isinstance (nodes , Node ):
691
+ nodes = [nodes ]
690
692
with self .groupedGraphModification ("Remove Nodes From Selected Nodes" ):
691
693
nodesToRemove , _ = self ._graph .dfsOnDiscover (startNodes = nodes , reverse = True , dependenciesOnly = True )
692
694
# filter out nodes that will be removed more than once
@@ -706,7 +708,7 @@ def duplicateNodes(self, nodes):
706
708
list[Node]: the list of duplicated nodes
707
709
"""
708
710
nodes = self .filterNodes (nodes )
709
- nPositions = []
711
+ nPositions = [( n . x , n . y ) for n in self . _graph . nodes ]
710
712
# enable updates between duplication and layout to get correct depths during layout
711
713
with self .groupedGraphModification ("Duplicate Selected Nodes" , disableUpdates = False ):
712
714
# disable graph updates during duplication
@@ -716,9 +718,8 @@ def duplicateNodes(self, nodes):
716
718
bbox = self ._layout .boundingBox (nodes )
717
719
718
720
for n in duplicates :
719
- idx = duplicates .index (n )
720
721
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 :
722
723
# make sure the node will not be moved on top of another node
723
724
while (n .x , yPos ) in nPositions :
724
725
yPos = yPos + self .layout .gridSpacing + self .layout .nodeHeight
@@ -739,12 +740,62 @@ def duplicateNodesFrom(self, nodes):
739
740
Returns:
740
741
list[Node]: the list of duplicated nodes
741
742
"""
743
+ if isinstance (nodes , Node ):
744
+ nodes = [nodes ]
742
745
with self .groupedGraphModification ("Duplicate Nodes From Selected Nodes" ):
743
746
nodesToDuplicate , _ = self ._graph .dfsOnDiscover (startNodes = nodes , reverse = True , dependenciesOnly = True )
744
747
# filter out nodes that will be duplicated more than once
745
748
uniqueNodesToDuplicate = list (dict .fromkeys (nodesToDuplicate ))
746
749
duplicates = self .duplicateNodes (uniqueNodesToDuplicate )
747
750
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
+
748
799
749
800
@Slot (QObject )
750
801
def clearData (self , nodes ):
@@ -765,7 +816,9 @@ def clearDataFrom(self, nodes):
765
816
766
817
@Slot (Attribute , Attribute )
767
818
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 ):
769
822
with self .groupedGraphModification ("Insert and Add Edge on {}" .format (dst .getFullNameToNode ())):
770
823
self .appendAttribute (dst )
771
824
self ._addEdge (src , dst .at (- 1 ))
@@ -787,14 +840,32 @@ def removeEdge(self, edge):
787
840
else :
788
841
self .push (commands .RemoveEdgeCommand (self ._graph , edge ))
789
842
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
+
790
854
@Slot (Attribute , "QVariant" )
791
855
def setAttribute (self , attribute , value ):
792
856
self .push (commands .SetAttributeCommand (self ._graph , attribute , value ))
793
857
794
858
@Slot (Attribute )
795
859
def resetAttribute (self , attribute ):
796
860
""" 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 ()))
798
869
799
870
@Slot (CompatibilityNode , result = Node )
800
871
def upgradeNode (self , node ):
0 commit comments