1313from .factory import NodeFactory
1414from .menu import NodeGraphMenu , NodesMenu
1515from .model import NodeGraphModel
16- from .node import NodeObject , BaseNode
16+ from .node import NodeObject , BaseNode , SubGraph
1717from .port import Port
1818from ..constants import (DRAG_DROP_ID ,
1919 PIPE_LAYOUT_CURVED ,
@@ -128,11 +128,12 @@ def __init__(self, parent=None):
128128 self ._viewer = NodeViewer ()
129129 self ._node_factory = NodeFactory ()
130130 self ._undo_stack = QtWidgets .QUndoStack (self )
131+ self ._current_node_space = None
131132
132133 tab = QtWidgets .QShortcut (QtGui .QKeySequence (QtCore .Qt .Key_Tab ), self ._viewer )
133134 tab .activated .connect (self ._toggle_tab_search )
134135 self ._viewer .need_show_tab_search .connect (self ._toggle_tab_search )
135-
136+
136137 self ._wire_signals ()
137138 self .widget .setAcceptDrops (True )
138139
@@ -223,6 +224,8 @@ def _on_node_double_clicked(self, node_id):
223224 """
224225 node = self .get_node_by_id (node_id )
225226 self .node_double_clicked .emit (node )
227+ if isinstance (node ,SubGraph ):
228+ node .enter ()
226229
227230 def _on_node_selected (self , node_id ):
228231 """
@@ -745,6 +748,9 @@ def format_color(clr):
745748 if pos :
746749 node .model .pos = [float (pos [0 ]), float (pos [1 ])]
747750
751+ # set node parent
752+ node .set_parent (self ._current_node_space )
753+
748754 node .update ()
749755
750756 undo_cmd = NodeAddedCmd (self , node , node .model .pos )
@@ -782,6 +788,24 @@ def add_node(self, node, pos=None):
782788 node .update ()
783789 self ._undo_stack .push (NodeAddedCmd (self , node , pos ))
784790
791+ def set_node_space (self , node ):
792+ """
793+ Set the node space of the node graph.
794+
795+ Args:
796+ node (NodeGraphQt.SubGraph): node object.
797+ """
798+ self ._current_node_space = node
799+
800+ def get_node_space (self ):
801+ """
802+ Get the node space of the node graph.
803+
804+ Returns:
805+ node (NodeGraphQt.SubGraph): node object or None.
806+ """
807+ return self ._current_node_space
808+
785809 def delete_node (self , node ):
786810 """
787811 Remove the node from the node graph.
@@ -793,6 +817,8 @@ def delete_node(self, node):
793817 'node must be a instance of a NodeObject.'
794818 self .nodes_deleted .emit ([node .id ])
795819 self ._undo_stack .push (NodeRemovedCmd (self , node ))
820+ if isinstance (node ,SubGraph ):
821+ self .delete_nodes (node .children ())
796822
797823 def delete_nodes (self , nodes ):
798824 """
@@ -803,6 +829,7 @@ def delete_nodes(self, nodes):
803829 """
804830 self .nodes_deleted .emit ([n .id for n in nodes ])
805831 self ._undo_stack .beginMacro ('delete nodes' )
832+ [self .delete_nodes (n .children ()) for n in nodes if isinstance (n ,SubGraph )]
806833 [self ._undo_stack .push (NodeRemovedCmd (self , n )) for n in nodes ]
807834 self ._undo_stack .endMacro ()
808835
@@ -866,7 +893,7 @@ def get_node_by_id(self, node_id=None):
866893 Returns:
867894 NodeGraphQt.NodeObject: node object.
868895 """
869- return self ._model .nodes .get (node_id )
896+ return self ._model .nodes .get (node_id , None )
870897
871898 def get_node_by_name (self , name ):
872899 """
@@ -1009,6 +1036,9 @@ def _deserialize(self, data, relative_pos=False, pos=None):
10091036 self .add_node (node , n_data .get ('pos' ))
10101037 node .set_graph (self )
10111038
1039+ # set node parent
1040+ [node .set_parent_id (node .parent_id ) for node in nodes .values ()]
1041+
10121042 # build the connections.
10131043 for connection in data .get ('connections' , []):
10141044 nid , pname = connection .get ('in' , ('' , '' ))
@@ -1064,6 +1094,10 @@ def save_session(self, file_path):
10641094 file_path (str): path to the saved node layout.
10651095 """
10661096 serliazed_data = self ._serialize (self .all_nodes ())
1097+ node_space = self .get_node_space ()
1098+ if node_space is not None :
1099+ node_space = node_space .id
1100+ serliazed_data ['graph' ] = {'node_space' :node_space ,'pipe_layout' :self ._viewer .get_pipe_layout ()}
10671101 file_path = file_path .strip ()
10681102 with open (file_path , 'w' ) as file_out :
10691103 json .dump (serliazed_data , file_out , indent = 2 , separators = (',' , ':' ))
@@ -1104,6 +1138,12 @@ def import_session(self, file_path):
11041138 return
11051139
11061140 self ._deserialize (layout_data )
1141+ node_space_id = layout_data ['graph' ]['node_space' ]
1142+
1143+ # deserialize graph data
1144+ self .set_node_space (self .get_node_by_id (node_space_id ))
1145+ self ._viewer .set_pipe_layout (layout_data ['graph' ]['pipe_layout' ])
1146+
11071147 self ._undo_stack .clear ()
11081148 self ._model .session = file_path
11091149 self .session_changed .emit (file_path )
@@ -1126,6 +1166,17 @@ def copy_nodes(self, nodes=None):
11261166 return True
11271167 return False
11281168
1169+ def cut_nodes (self , nodes = None ):
1170+ """
1171+ Cut nodes to the clipboard.
1172+
1173+ Args:
1174+ nodes (list[NodeGraphQt.BaseNode]): list of nodes (default: selected nodes).
1175+ """
1176+ nodes = nodes or self .selected_nodes ()
1177+ self .copy_nodes (nodes )
1178+ self .delete_nodes (nodes )
1179+
11291180 def paste_nodes (self ):
11301181 """
11311182 Pastes nodes copied from the clipboard.
@@ -1140,6 +1191,9 @@ def paste_nodes(self):
11401191 self .clear_selection ()
11411192 nodes = self ._deserialize (serial_data , relative_pos = True )
11421193 [n .set_selected (True ) for n in nodes ]
1194+ # set node parent
1195+ [n .set_parent (self ._current_node_space ) for n in nodes ]
1196+
11431197 self ._undo_stack .endMacro ()
11441198
11451199 def duplicate_nodes (self , nodes ):
0 commit comments