1414import shutil
1515
1616from .ntp_node_tree import NTP_NodeTree
17+ from .options import NTPOptions
1718from .node_settings import NodeInfo , ST
1819from .utils import *
1920
@@ -46,14 +47,6 @@ class NTP_Operator(Operator):
4647 bl_idname = ""
4748 bl_label = ""
4849
49- mode : bpy .props .EnumProperty (
50- name = "Mode" ,
51- items = [
52- ('SCRIPT' , "Script" , "Copy just the node group to the Blender clipboard" ),
53- ('ADDON' , "Addon" , "Create a full addon" )
54- ]
55- )
56-
5750 # node tree input sockets that have default properties
5851 if bpy .app .version < (4 , 0 , 0 ):
5952 default_sockets_v3 = {'VALUE' , 'INT' , 'BOOLEAN' , 'VECTOR' , 'RGBA' }
@@ -74,9 +67,6 @@ def __init__(self):
7467 # File (TextIO) or string (StringIO) the add-on/script is generated into
7568 self ._file : TextIO = None
7669
77- # Path to the current directory
78- self ._dir : str = None
79-
8070 # Path to the directory of the zip file
8171 self ._zip_dir : str = None
8272
@@ -108,11 +98,49 @@ def __init__(self):
10898 for name in RESERVED_NAMES :
10999 self ._used_vars [name ] = 0
110100
101+ # Generate socket default, min, and max values
102+ self ._include_group_socket_values = True
103+
104+ # Set dimensions of generated nodes
105+ self ._should_set_dimensions = True
106+
107+ if bpy .app .version >= (3 , 4 , 0 ):
108+ # Set default values for hidden sockets
109+ self ._set_unavailable_defaults = False
110+
111111 def _write (self , string : str , indent : str = None ):
112112 if indent is None :
113113 indent = self ._inner
114114 self ._file .write (f"{ indent } { string } \n " )
115115
116+ def _setup_options (self , options : NTPOptions ) -> bool :
117+ # General
118+ self ._mode = options .mode
119+ self ._include_group_socket_values = options .include_group_socket_values
120+ self ._should_set_dimensions = options .set_dimensions
121+ if bpy .app .version >= (3 , 4 , 0 ):
122+ self ._set_unavailable_defaults = options .set_unavailable_defaults
123+
124+ #Script
125+ if options .mode == 'SCRIPT' :
126+ self ._include_imports = options .include_imports
127+ #Addon
128+ elif options .mode == 'ADDON' :
129+ self ._dir_path = bpy .path .abspath (options .dir_path )
130+ self ._name_override = options .name_override
131+ self ._description = options .description
132+ self ._author_name = options .author_name
133+ self ._version = options .version
134+ self ._location = options .location
135+ self ._category = options .category
136+ self ._custom_category = options .custom_category
137+ if options .menu_id in dir (bpy .types ):
138+ self ._menu_id = options .menu_id
139+ else :
140+ self .report ({'ERROR' }, f"{ options .menu_id } is not a valid menu" )
141+ return False
142+ return True
143+
116144 def _setup_addon_directories (self , context : Context , nt_var : str ) -> bool :
117145 """
118146 Finds/creates directories to save add-on to
@@ -124,15 +152,13 @@ def _setup_addon_directories(self, context: Context, nt_var: str) -> bool:
124152 Returns:
125153 (bool): success of addon directory setup
126154 """
127- # find base directory to save new addon
128- self ._dir = bpy .path .abspath (context .scene .ntp_options .dir_path )
129- if not self ._dir or self ._dir == "" :
155+ if not self ._dir_path or self ._dir_path == "" :
130156 self .report ({'ERROR' },
131157 ("NodeToPython: No save location found. Please select "
132158 "one in the NodeToPython Options panel" ))
133159 return False
134160
135- self ._zip_dir = os .path .join (self ._dir , nt_var )
161+ self ._zip_dir = os .path .join (self ._dir_path , nt_var )
136162 self ._addon_dir = os .path .join (self ._zip_dir , nt_var )
137163
138164 if not os .path .exists (self ._addon_dir ):
@@ -150,12 +176,19 @@ def _create_header(self, name: str) -> None:
150176 """
151177
152178 self ._write ("bl_info = {" , "" )
153- self ._write (f"\t \" name\" : \" { name } \" ," , "" )
154- self ._write ("\t \" author\" : \" Node To Python\" ," , "" )
155- self ._write ("\t \" version\" : (1, 0, 0)," , "" )
179+ if self ._name_override and self ._name_override != "" :
180+ name = self ._name_override
181+ self ._write (f"\t \" name\" : { str_to_py_str (name )} ," , "" )
182+ if self ._description and self ._description != "" :
183+ self .write (f"\t \" description\" : { str_to_py_str (self ._description )} ," "" )
184+ self ._write (f"\t \" author\" : { str_to_py_str (self ._author_name )} ," , "" )
185+ self ._write (f"\t \" version\" : { vec3_to_py_str (self ._version )} ," , "" )
156186 self ._write (f"\t \" blender\" : { bpy .app .version } ," , "" )
157- self ._write ("\t \" location\" : \" Object\" ," , "" ) # TODO
158- self ._write ("\t \" category\" : \" Node\" " , "" )
187+ self ._write (f"\t \" location\" : { str_to_py_str (self ._location )} ," , "" )
188+ category = self ._category
189+ if category == "Custom" :
190+ category = self ._custom_category
191+ self ._write (f"\t \" category\" : { str_to_py_str (category )} ," , "" )
159192 self ._write ("}\n " , "" )
160193 self ._write ("import bpy" , "" )
161194 self ._write ("import mathutils" , "" )
@@ -172,8 +205,8 @@ def _init_operator(self, idname: str, label: str) -> None:
172205 label (str): appearence inside Blender
173206 """
174207 self ._write (f"class { self ._class_name } (bpy.types.Operator):" , "" )
175- self ._write (f"\t bl_idname = \" object .{ idname } \" " , "" )
176- self ._write (f"\t bl_label = \" { label } \" " , "" )
208+ self ._write (f"\t bl_idname = \" node .{ idname } \" " , "" )
209+ self ._write (f"\t bl_label = { str_to_py_str ( label ) } " , "" )
177210 self ._write ("\t bl_options = {\' REGISTER\' , \' UNDO\' }" , "" )
178211 self ._write ("" )
179212
@@ -389,6 +422,9 @@ def _set_group_socket_defaults(self, socket_interface: NodeSocketInterface,
389422 with the input/output
390423 socket_var (str): variable name for the socket
391424 """
425+ if not self ._include_group_socket_values :
426+ return
427+
392428 if socket_interface .type not in self .default_sockets_v3 :
393429 return
394430
@@ -485,6 +521,8 @@ def _set_tree_socket_defaults(self, socket_interface: NodeTreeInterfaceSocket,
485521 with the input/output
486522 socket_var (str): variable name for the socket
487523 """
524+ if not self ._include_group_socket_values :
525+ return
488526 if type (socket_interface ) in self .nondefault_sockets_v4 :
489527 return
490528
@@ -710,6 +748,10 @@ def _set_input_defaults(self, node: Node) -> None:
710748
711749 for i , input in enumerate (node .inputs ):
712750 if input .bl_idname not in DONT_SET_DEFAULTS and not input .is_linked :
751+ if bpy .app .version >= (3 , 4 , 0 ):
752+ if (not self ._set_unavailable_defaults ) and input .is_unavailable :
753+ continue
754+
713755 # TODO: this could be cleaner
714756 socket_var = f"{ node_var } .inputs[{ i } ]"
715757
@@ -1006,13 +1048,19 @@ def _save_image(self, img: bpy.types.Image) -> None:
10061048 if img is None :
10071049 return
10081050
1051+ img_str = img_to_py_str (img )
1052+
1053+ if not img .has_data :
1054+ self .report ({'WARNING' }, f"{ img_str } has no data" )
1055+ return
1056+
10091057 # create image dir if one doesn't exist
10101058 img_dir = os .path .join (self ._addon_dir , IMAGE_DIR_NAME )
10111059 if not os .path .exists (img_dir ):
10121060 os .mkdir (img_dir )
10131061
10141062 # save the image
1015- img_str = img_to_py_str ( img )
1063+
10161064 img_path = f"{ img_dir } /{ img_str } "
10171065 if not os .path .exists (img_path ):
10181066 img .save_render (img_path )
@@ -1212,6 +1260,9 @@ def _set_dimensions(self, node_tree: NodeTree) -> None:
12121260 Parameters:
12131261 node_tree (NodeTree): node tree we're obtaining nodes from
12141262 """
1263+ if not self ._should_set_dimensions :
1264+ return
1265+
12151266 self ._write (f"#Set dimensions" )
12161267 for node in node_tree .nodes :
12171268 node_var = self ._node_vars [node ]
@@ -1305,7 +1356,7 @@ def _create_register_func(self) -> None:
13051356 """
13061357 self ._write ("def register():" , "" )
13071358 self ._write (f"bpy.utils.register_class({ self ._class_name } )" , "\t " )
1308- self ._write ("bpy.types.VIEW3D_MT_object .append(menu_func)" , "\t " )
1359+ self ._write (f "bpy.types.{ self . _menu_id } .append(menu_func)" , "\t " )
13091360 self ._write ("" )
13101361
13111362 def _create_unregister_func (self ) -> None :
@@ -1314,7 +1365,7 @@ def _create_unregister_func(self) -> None:
13141365 """
13151366 self ._write ("def unregister():" , "" )
13161367 self ._write (f"bpy.utils.unregister_class({ self ._class_name } )" , "\t " )
1317- self ._write ("bpy.types.VIEW3D_MT_object .remove(menu_func)" , "\t " )
1368+ self ._write (f "bpy.types.{ self . _menu_id } .remove(menu_func)" , "\t " )
13181369 self ._write ("" )
13191370
13201371 def _create_main_func (self ) -> None :
@@ -1347,18 +1398,12 @@ def _report_finished(self, object: str):
13471398 object (str): the copied node tree or encapsulating structure
13481399 (geometry node modifier, material, scene, etc.)
13491400 """
1350- if self .mode == 'SCRIPT' :
1401+ if self ._mode == 'SCRIPT' :
13511402 location = "clipboard"
13521403 else :
1353- location = self ._dir
1404+ location = self ._dir_path
13541405 self .report ({'INFO' }, f"NodeToPython: Saved { object } to { location } " )
13551406
13561407 # ABSTRACT
13571408 def execute (self ):
13581409 return {'FINISHED' }
1359-
1360- def invoke (self , context , event ):
1361- return context .window_manager .invoke_props_dialog (self )
1362-
1363- def draw (self , context ):
1364- self .layout .prop (self , "mode" )
0 commit comments