@@ -158,21 +158,13 @@ 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 ._build_context_menu ()
162161 self ._register_builtin_nodes ()
163162 self ._wire_signals ()
164163
165164 def __repr__ (self ):
166165 return '<{}("root") object at {}>' .format (
167166 self .__class__ .__name__ , hex (id (self )))
168167
169- def _build_context_menu (self ):
170- """
171- build the essential menus commands for the graph context menu.
172- """
173- from NodeGraphQt .base .graph_actions import build_context_menu
174- build_context_menu (self )
175-
176168 def _register_builtin_nodes (self ):
177169 """
178170 Register the default builtin nodes to the :meth:`NodeGraph.node_factory`
@@ -703,6 +695,117 @@ def get_context_menu(self, menu):
703695 elif menu == 'nodes' :
704696 return NodesMenu (self , menus [menu ])
705697
698+ @staticmethod
699+ def _build_context_menu_command (menu , data ):
700+ """
701+ Create context menu command from serialized data.
702+
703+ Args:
704+ menu (NodeGraphQt.NodeGraphMenu or NodeGraphQt.NodesMenu):
705+ menu object.
706+ data (dict): serialized menu command data.
707+ """
708+ import sys
709+ import importlib .util
710+
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 ('.' )
715+
716+ mod_name = '{}.{}' .format (base_name , file_name )
717+
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 )
722+
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 )
727+
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 ))
739+
740+ if isinstance (menu_data , dict ):
741+ item_type = menu_data .get ('type' )
742+ if item_type == 'separator' :
743+ menu .add_separator ()
744+ elif item_type == 'command' :
745+ self ._build_context_menu_command (menu , menu_data )
746+ elif item_type == 'menu' :
747+ sub_menu = menu .add_menu (menu_data ['label' ])
748+ items = menu_data .get ('items' , [])
749+ self ._build_context_menu (sub_menu , items )
750+ elif isinstance (menu_data , list ):
751+ for item_data in menu_data :
752+ self ._build_context_menu (menu , item_data )
753+
754+ def set_context_menu (self , menu_name , data ):
755+ """
756+ Populate a context menu from serialized data.
757+
758+ serialized menu data example:
759+
760+ .. highlight:: python
761+ .. code-block:: python
762+
763+ [
764+ {
765+ 'type': 'menu',
766+ 'label': 'test sub menu',
767+ 'items': [
768+ {
769+ 'type': 'command',
770+ 'label': 'test command',
771+ 'file': '../path/to/my/test_module.py',
772+ 'function': 'run_test',
773+ 'shortcut': 'Ctrl+b'
774+ },
775+
776+ ]
777+ },
778+ ]
779+
780+ Args:
781+ menu_name (str): name of the parent context menu to populate under.
782+ data (dict): serialized menu data.
783+ """
784+ context_menu = self .get_context_menu (menu_name )
785+ self ._build_context_menu (context_menu , data )
786+
787+ def set_context_menu_from_file (self , file_path , menu = None ):
788+ """
789+ Populate a context menu from a serialized json file.
790+
791+ Menu Types:
792+ - ``"graph"`` context menu from the node graph.
793+ - ``"nodes"`` context menu for the nodes.
794+
795+ Args:
796+ menu (str): name of the parent context menu to populate under.
797+ file_path (str): serialized menu commands json file.
798+ """
799+ menu = menu or 'graph'
800+ if not os .path .isfile (file_path ):
801+ return
802+
803+ with open (file_path ) as f :
804+ data = json .load (f )
805+ context_menu = self .get_context_menu (menu )
806+ self ._build_context_menu (context_menu , data )
807+
808+
706809 def disable_context_menu (self , disabled = True , name = 'all' ):
707810 """
708811 Disable/Enable context menus from the node graph.
@@ -1255,7 +1358,7 @@ def get_unique_name(self, name):
12551358 if name not in node_names :
12561359 return name
12571360
1258- regex = re .compile (r'[\w ]+(?: )* (\d+)' )
1361+ regex = re .compile (r'\w+ (\d+)$ ' )
12591362 search = regex .search (name )
12601363 if not search :
12611364 for x in range (1 , len (node_names ) + 2 ):
0 commit comments