@@ -158,13 +158,28 @@ def __init__(self, parent=None, **kwargs):
158158 kwargs .get ('viewer' ) or NodeViewer (undo_stack = self ._undo_stack ))
159159 self ._viewer .set_layout_direction (layout_direction )
160160
161+ self ._context_menu = {}
162+
163+ self ._register_context_menu ()
161164 self ._register_builtin_nodes ()
162165 self ._wire_signals ()
163166
164167 def __repr__ (self ):
165168 return '<{}("root") object at {}>' .format (
166169 self .__class__ .__name__ , hex (id (self )))
167170
171+ def _register_context_menu (self ):
172+ """
173+ Register the default context menus.
174+ """
175+ if not self ._viewer :
176+ return
177+ menus = self ._viewer .context_menus ()
178+ if menus .get ('graph' ):
179+ self ._context_menu ['graph' ] = NodeGraphMenu (self , menus ['graph' ])
180+ if menus .get ('nodes' ):
181+ self ._context_menu ['nodes' ] = NodesMenu (self , menus ['nodes' ])
182+
168183 def _register_builtin_nodes (self ):
169184 """
170185 Register the default builtin nodes to the :meth:`NodeGraph.node_factory`
@@ -688,68 +703,62 @@ def get_context_menu(self, menu):
688703 Returns:
689704 NodeGraphQt.NodeGraphMenu or NodeGraphQt.NodesMenu: context menu object.
690705 """
691- menus = self ._viewer .context_menus ()
692- if menus .get (menu ):
693- if menu == 'graph' :
694- return NodeGraphMenu (self , menus [menu ])
695- elif menu == 'nodes' :
696- return NodesMenu (self , menus [menu ])
706+ return self ._context_menu .get (menu )
697707
698- @staticmethod
699- def _build_context_menu_command (menu , data ):
708+ def _deserialize_context_menu (self , menu , menu_data ):
700709 """
701- Create context menu command from serialized data .
710+ Populate context menu from a dictionary .
702711
703712 Args:
704713 menu (NodeGraphQt.NodeGraphMenu or NodeGraphQt.NodesMenu):
705- menu object .
706- data ( dict): serialized menu command data.
714+ parent context menu .
715+ menu_data (list[ dict] or dict ): serialized menu data.
707716 """
717+ if not menu :
718+ raise ValueError ('No context menu named: "{}"' .format (menu ))
719+
708720 import sys
709721 import importlib .util
710722
711- full_path = os .path .abspath (data ['file' ])
712- base_dir , file_name = os .path .split (full_path )
713- base_name = os .path .basename (base_dir )
714- file_name , _ = file_name .split ('.' )
723+ def build_menu_command (menu , data ):
724+ """
725+ Create menu command from serialized data.
715726
716- mod_name = '{}.{}' .format (base_name , file_name )
727+ Args:
728+ menu (NodeGraphQt.NodeGraphMenu or NodeGraphQt.NodesMenu):
729+ menu object.
730+ data (dict): serialized menu command data.
731+ """
732+ full_path = os .path .abspath (data ['file' ])
733+ base_dir , file_name = os .path .split (full_path )
734+ base_name = os .path .basename (base_dir )
735+ file_name , _ = file_name .split ('.' )
717736
718- spec = importlib .util .spec_from_file_location (mod_name , full_path )
719- mod = importlib .util .module_from_spec (spec )
720- sys .modules [mod_name ] = mod
721- spec .loader .exec_module (mod )
737+ mod_name = '{}.{}' .format (base_name , file_name )
722738
723- cmd_func = getattr ( mod , data [ 'function_name' ] )
724- cmd_name = data . get ( 'label' ) or '<command>'
725- cmd_shortcut = data . get ( 'shortcut' )
726- menu . add_command ( cmd_name , cmd_func , cmd_shortcut )
739+ spec = importlib . util . spec_from_file_location ( mod_name , full_path )
740+ mod = importlib . util . module_from_spec ( spec )
741+ sys . modules [ mod_name ] = mod
742+ spec . loader . exec_module ( mod )
727743
728- def _build_context_menu (self , menu , menu_data ):
729- """
730- Populate context menu from a dictionary.
731-
732- Args:
733- menu (NodeGraphQt.NodeGraphMenu or NodeGraphQt.NodesMenu):
734- parent context menu.
735- menu_data (list[dict] or dict): serialized menu data.
736- """
737- if not menu :
738- raise ValueError ('No context menu named: "{}"' .format (menu ))
744+ cmd_func = getattr (mod , data ['function_name' ])
745+ cmd_name = data .get ('label' ) or '<command>'
746+ cmd_shortcut = data .get ('shortcut' )
747+ menu .add_command (cmd_name , cmd_func , cmd_shortcut )
739748
740749 if isinstance (menu_data , dict ):
741750 item_type = menu_data .get ('type' )
742751 if item_type == 'separator' :
743752 menu .add_separator ()
744753 elif item_type == 'command' :
745- self . _build_context_menu_command (menu , menu_data )
754+ build_menu_command (menu , menu_data )
746755 elif item_type == 'menu' :
747756 sub_menu = menu .add_menu (menu_data ['label' ])
748757 items = menu_data .get ('items' , [])
749- self ._build_context_menu (sub_menu , items )
758+ self ._deserialize_context_menu (sub_menu , items )
750759 elif isinstance (menu_data , list ):
751760 for item_data in menu_data :
752- self ._build_context_menu (menu , item_data )
761+ self ._deserialize_context_menu (menu , item_data )
753762
754763 def set_context_menu (self , menu_name , data ):
755764 """
@@ -782,7 +791,7 @@ def set_context_menu(self, menu_name, data):
782791 data (dict): serialized menu data.
783792 """
784793 context_menu = self .get_context_menu (menu_name )
785- self ._build_context_menu (context_menu , data )
794+ self ._deserialize_context_menu (context_menu , data )
786795
787796 def set_context_menu_from_file (self , file_path , menu = None ):
788797 """
@@ -803,8 +812,7 @@ def set_context_menu_from_file(self, file_path, menu=None):
803812 with open (file_path ) as f :
804813 data = json .load (f )
805814 context_menu = self .get_context_menu (menu )
806- self ._build_context_menu (context_menu , data )
807-
815+ self ._deserialize_context_menu (context_menu , data )
808816
809817 def disable_context_menu (self , disabled = True , name = 'all' ):
810818 """
@@ -2126,6 +2134,9 @@ def __init__(self, parent=None, node=None, node_factory=None, **kwargs):
21262134 del self ._widget
21272135 del self ._sub_graphs
21282136
2137+ # clone context menu from the parent node graph.
2138+ self ._clone_context_menu_from_parent ()
2139+
21292140 def __repr__ (self ):
21302141 return '<{}("{}") object at {}>' .format (
21312142 self .__class__ .__name__ , self ._node .name (), hex (id (self )))
@@ -2136,6 +2147,46 @@ def _register_builtin_nodes(self):
21362147 """
21372148 return
21382149
2150+ def _clone_context_menu_from_parent (self ):
2151+ """
2152+ Clone the context menus from the parent node graph.
2153+ """
2154+ undo_regex = re .compile (r'^\&Undo(?:| \w+)' )
2155+ redo_regex = re .compile (r'^\&Redo(?:| \w+)' )
2156+
2157+ def clone_menu (menu , menu_to_clone ):
2158+ """
2159+ Args:
2160+ menu (NodeGraphQt.NodeGraphMenu):
2161+ menu_to_clone (NodeGraphQt.NodeGraphMenu):
2162+ """
2163+ sub_items = []
2164+ for idx , item in enumerate (menu_to_clone .get_items ()):
2165+ if item is None :
2166+ menu .add_separator ()
2167+ continue
2168+ name = item .name ()
2169+ if isinstance (item , NodeGraphMenu ):
2170+ sub_menu = menu .add_menu (name )
2171+ sub_items .append ([sub_menu , item ])
2172+ continue
2173+
2174+ if idx <= 1 :
2175+ if undo_regex .match (name ) or redo_regex .match (name ):
2176+ continue
2177+ menu .add_command (
2178+ name ,
2179+ func = item .slot_function ,
2180+ shortcut = item .qaction .shortcut ()
2181+ )
2182+
2183+ for sub_menu , to_clone in sub_items :
2184+ clone_menu (sub_menu , to_clone )
2185+
2186+ graph_menu = self .get_context_menu ('graph' )
2187+ parent_menu = self .parent_graph .get_context_menu ('graph' )
2188+ clone_menu (graph_menu , parent_menu )
2189+
21392190 def _build_port_nodes (self ):
21402191 """
21412192 Build the corresponding input & output nodes from the parent node ports
0 commit comments