Browse Source

Cleanup in I/O

this commit also changes the way new sockets are added to always make them directly
except for Armature Link nodes.
Joseph Brandenburg 6 months ago
parent
commit
81ed53b133
1 changed files with 67 additions and 133 deletions
  1. 67 133
      i_o.py

+ 67 - 133
i_o.py

@@ -9,6 +9,10 @@ from mathutils import  Vector
 
 from .base_definitions import NODES_REMOVED, SOCKETS_REMOVED
 
+add_inputs_bl_idnames = [
+    "UtilityDriver", "UtilityFCurve", "DeformerMorphTargetDeform",
+    "MantisSchemaGroup", "LinkArmature",
+    ]
 
 # this works but it is really ugly and probably quite inneficient
 # TODO: make hotkeys for export and import and reload from file
@@ -62,6 +66,52 @@ def fix_custom_parameter(n, property_definition, ):
     
     
 
+def get_socket_data(socket):
+    socket_data = {}
+    socket_data["name"] = socket.name
+    socket_data["bl_idname"] = socket.bl_idname
+    socket_data["is_output"] = socket.is_output
+    socket_data["is_multi_input"] = socket.is_multi_input
+
+    # if socket.bl_idname == 'TransformSpaceSocket':
+    #     prGreen(socket.default_value)
+    
+    # here is where we'll handle a socket_data'socket special data
+    if socket.bl_idname == "EnumMetaBoneSocket":
+        socket_data["bone"] = socket.bone
+    if socket.bl_idname in ["EnumMetaBoneSocket", "EnumMetaRigSocket", "EnumCurveSocket"]:
+        if sp := socket.get("search_prop"): # may be None
+            socket_data["search_prop"] = sp.name # this is an object.
+    #
+
+    # v = socket.get("default_value") # this doesn't seem to work, see below
+    if hasattr(socket, "default_value"):
+        v = socket.default_value
+    else:
+        v = None
+    v_type = type(v)
+    if v is None:
+        return socket_data # we don't need to store this.
+    if not is_jsonable(v):
+        v = tuple(v)
+    if not is_jsonable(v):
+        raise RuntimeError(f"Error serializing data in {socket.node.name}::{socket.name} for value of type {v_type}")
+    socket_data["default_value"] = v
+    # at this point we can get the custom parameter ui hints if we want
+    if not socket.is_output:
+        # try and get this data
+        if v := getattr(socket,'min', None):
+            socket_data["min"] = v
+        if v := getattr(socket,'max', None):
+            socket_data["max"] = v
+        if v := getattr(socket,'soft_min', None):
+            socket_data["soft_min"] = v
+        if v := getattr(socket,'soft_max', None):
+            socket_data["soft_max"] = v
+        if v := getattr(socket,'description', None):
+            socket_data["description"] = v
+    return socket_data
+    #
 
 def export_to_json(trees, path="", write_file=True, only_selected=False):
     # ignore these because they are either unrelated python stuff or useless or borked
@@ -182,68 +232,12 @@ def export_to_json(trees, path="", write_file=True, only_selected=False):
                     node_props[propname] = tuple(location_acc)
                     # this works!
 
-                    # n.parent = None
-                
-                # if propname == "location":
-                #     print (v, n.location)
-                # if parent:
-                #     n.parent = parent
-            # now we need to get the sockets...
-
-            # WHY IS THIS FUNCTION DEFINED IN THIS SCOPE?
-            def socket_data(s):
-                socket = {}
-                socket["name"] = s.name
-                socket["bl_idname"] = s.bl_idname
-                socket["is_output"] = s.is_output
-                socket["is_multi_input"] = s.is_multi_input
-
-                # if s.bl_idname == 'TransformSpaceSocket':
-                #     prGreen(s.default_value)
-                
-                # here is where we'll handle a socket's special data
-                if s.bl_idname == "EnumMetaBoneSocket":
-                    socket["bone"] = s.bone
-                if s.bl_idname in ["EnumMetaBoneSocket", "EnumMetaRigSocket", "EnumCurveSocket"]:
-                    if sp := s.get("search_prop"): # may be None
-                        socket["search_prop"] = sp.name # this is an object.
-                #
-
-                # v = s.get("default_value") # this doesn't seem to work, see below
-                if hasattr(s, "default_value"):
-                    v = s.default_value
-                else:
-                    v = None
-                v_type = type(v)
-                if v is None:
-                    return socket # we don't need to store this.
-                if not is_jsonable(v):
-                    v = tuple(v)
-                if not is_jsonable(v):
-                    raise RuntimeError(f"Error serializing data in {s.node.name}::{s.name} for value of type {v_type}")
-                socket["default_value"] = v
-                # at this point we can get the custom parameter ui hints if we want
-                if not s.is_output:
-                    # try and get this data
-                    if v := getattr(s,'min', None):
-                        socket["min"] = v
-                    if v := getattr(s,'max', None):
-                        socket["max"] = v
-                    if v := getattr(s,'soft_min', None):
-                        socket["soft_min"] = v
-                    if v := getattr(s,'soft_max', None):
-                        socket["soft_max"] = v
-                    if v := getattr(s,'description', None):
-                        socket["description"] = v
-                return socket
-                #
-
             for i, s in enumerate(n.inputs):
-                socket = socket_data(s)
+                socket = get_socket_data(s)
                 socket["index"]=i
                 sockets[s.identifier] = socket
             for i, s in enumerate(n.outputs):
-                socket = socket_data(s)
+                socket = get_socket_data(s)
                 socket["index"]=i
                 sockets[s.identifier] = socket
             
@@ -285,8 +279,6 @@ def export_to_json(trees, path="", write_file=True, only_selected=False):
                 raise RuntimeError(wrapRed(f"Error saving index of socket: {problem}"))
             g, h = l.from_socket.name, l.to_socket.name
 
-            # print (f"{a}:{b} --> {c}:{d})")
-            # this data is good enough
             if base_tree:
                 if (only_selected and l.from_node.select) and (not l.to_node.select):
                     # handle an output in the tree
@@ -391,6 +383,7 @@ def export_to_json(trees, path="", write_file=True, only_selected=False):
             nodes["MANTIS_AUTOGEN_GROUP_OUTPUT"]=out_node
 
         export_data[tree.name] = (tree_info, tree_in_out, nodes, links,) # f_curves)
+    
     import json
 
     if not write_file:
@@ -412,13 +405,18 @@ def do_import_from_file(filepath, context):
     for tree in all_trees:
         tree.is_exporting = True
         tree.do_live_update = False
+    
+    def do_cleanup(tree):
+        tree.is_exporting = False
+        tree.do_live_update = True
+        tree.prevent_next_exec = True
 
     with open(filepath, 'r', encoding='utf-8') as f:
         data = json.load(f)
         do_import(data,context)
 
         for tree in all_trees:
-            tree.do_live_update = True
+            do_cleanup(tree)
 
         tree = bpy.data.node_groups[list(data.keys())[-1]]
         try:
@@ -429,9 +427,7 @@ def do_import_from_file(filepath, context):
     # otherwise:
     # repeat this because we left the with, this is bad and ugly but I don't care
     for tree in all_trees:
-        tree.is_exporting = False
-        tree.do_live_update = True
-        tree.prevent_next_exec = True
+            do_cleanup(tree)
     return {'CANCELLED'}
 
 def do_import(data, context):
@@ -490,7 +486,6 @@ def do_import(data, context):
         # Go BACK through and set the index/position now that all items exist.
         interface_sockets.sort(key=lambda a : a[1])
         for (socket, position) in interface_sockets:
-            print (socket.name, position)
             tree.interface.move(socket, position)
         
     # Now go and do nodes and links
@@ -558,36 +553,16 @@ def do_import(data, context):
                             if s_val["index"] > removed_index:
                                 s_val["index"]-=1
                         if s_val["index"] >= len(n.inputs):
-                            if n.bl_idname == "UtilityDriver":
-                                with bpy.context.temp_override(**{'node':n}):
-                                    bpy.ops.mantis.driver_node_add_variable()
-                                socket = n.inputs[int(s_val["index"])]
-                            elif n.bl_idname == "UtilityFCurve":
-                                with bpy.context.temp_override(**{'node':n}):
-                                    bpy.ops.mantis.fcurve_node_add_kf()
-                                socket = n.inputs[int(s_val["index"])]
-                            elif n.bl_idname == "MantisSchemaGroup":
+                            if n.bl_idname in add_inputs_bl_idnames:
                                 socket = n.inputs.new(s_val["bl_idname"], s_val["name"], identifier=s_id, use_multi_input=s_val["is_multi_input"])
-                                # for k,v in s_val.items():
-                                #     print(f"{k}:{v}")
-                                # print (s_id)
-                                # raise NotImplementedError(s_val["is_multi_input"])
                             elif n.bl_idname in ["NodeGroupOutput"]:
-                                # print (len(n.inputs), len(n.outputs))
-                                pass
-                            elif n.bl_idname == "LinkArmature":
-                                with bpy.context.temp_override(**{'node':n}):
-                                    bpy.ops.mantis.link_armature_node_add_target()
-                                socket = n.inputs[int(s_val["index"])]
-                            elif n.bl_idname == "DeformerMorphTargetDeform": # this one doesn't use an operator since I figure out how to do dynamic node stuff
-                                socket = n.inputs.new(s_val["bl_idname"], s_val["name"], identifier=s_id)
+                                pass # this is dealt with separately
                             else:
                                 prWhite(n.name, s_val["name"], s_id)
                                 for k,v in propslist["sockets"].items():
                                     print(k,v)
                                 prRed(s_val["index"], len(n.inputs))
                                 raise NotImplementedError(wrapRed(f"{n.bl_idname} needs to be handled in JSON load."))
-                            # if n.bl_idname in ['']
                         else: # most of the time
                             socket = n.inputs[int(s_val["index"])]
                 except IndexError:
@@ -596,14 +571,7 @@ def do_import(data, context):
                         is_output = "output" if {s_val["is_output"]} else "input"
                         prRed(s_val, type(s_val))
                         raise RuntimeError(is_output, n.name, s_val["name"], s_id, len(n.inputs))
-
                 
-#                if propslist["bl_idname"] == "UtilityMetaRig":#and i == 0:
-#                    pass#prRed (i, s_id, s_val)
-#                if propslist["bl_idname"] == "UtilityMetaRig":# and i > 0:
-#                       prRed("Not Found: %s" % (s_id))
-#                       prOrange(propslist["sockets"][s_id])
-#                       socket = fix_custom_parameter(n, propslist["sockets"][s_id]
                 for s_p, s_v in s_val.items():
                     if s_p not in ["default_value"]:
                         if s_p == "search_prop" and n.bl_idname == 'UtilityMetaRig':
@@ -622,20 +590,15 @@ def do_import(data, context):
                     try:
                         setattr(socket, s_p , s_v)
                     except TypeError as e:
-                        prRed("Can't set socket due to type mismatch: ", socket, s_p, s_v)
+                        prRed("Can't set socket due to type mismatch: ", n.name, socket.name, s_p, s_v)
                         # raise e
                     except ValueError as e:
-                        prRed("Can't set socket due to type mismatch: ", socket, s_p, s_v)
+                        prRed("Can't set socket due to type mismatch: ", n.name,  socket.name, s_p, s_v)
                         # raise e
                     except AttributeError as e:
                         prWhite("Tried to write a read-only property, ignoring...")
                         prWhite(f"{socket.node.name}[{socket.name}].{s_p} is read only, cannot set value to {s_v}")
-                        # raise e
-                # not sure if this is true:
-                    # this can find properties that aren't node in/out
-                    # we should also be checking those above actually
-                # TODO:
-                # find out why "Bone hide" not being found
+
             for p, v in propslist.items():
                 if p == "sockets": # it's the sockets dict
                     continue
@@ -653,12 +616,6 @@ def do_import(data, context):
                 except Exception as e:
                     print (p)
                     raise e
-#        raise NotImplementedError
-
-        
-        
-#        for k,v in tree_sock_id_map.items():
-#            print (wrapGreen(k), "   ", wrapPurple(v))
         
         for l in links:
 
@@ -668,14 +625,6 @@ def do_import(data, context):
             name1=l[6]
             name2=l[7]
             
-            # prWhite(l[0], l[1], " --> ", l[2], l[3])
-            
-            # l has...
-            # node 1
-            # identifier 1
-            # node 2
-            # identifier 2
-            
             # if the from/to socket or node has been removed, continue
             from_node = tree.nodes.get(l[0])
             if not from_node:
@@ -702,8 +651,6 @@ def do_import(data, context):
             else: # we can raise a runtime error here actually
                 from_sock = None
                 
-                
-            
             to_node = tree.nodes[l[2]]
             if hasattr(to_node, "node_tree"):
                 try:
@@ -713,15 +660,9 @@ def do_import(data, context):
                     id2 = None
             elif to_node.bl_idname in ["NodeGroupOutput"]:
                 id2 = tree_sock_id_map.get(l[3])
-#                prPurple(to_node.name)
-#                for inp in to_node.inputs:
-#                    prPurple(inp.name, inp.identifier)
-#                prOrange (l[3], id2)
             elif to_node.bl_idname in ["SchemaArrayOutput", "SchemaConstOutput", "SchemaOutgoingConnection"]:
                 # try the index instead
                 id2 = to_node.inputs[l[5]].identifier
-                # try to get by name
-                #id2 = to_node.inputs[name2]
 
             for to_sock in to_node.inputs:
                 if to_sock.identifier == id2: break
@@ -738,6 +679,7 @@ def do_import(data, context):
                     prRed (f"Failed: {l[0]}:{l[1]} --> {l[2]}:{l[3]}")
                     prRed (f" got node: {from_node.name}, {to_node.name}")
                     prRed (f" got socket: {from_sock}, {to_sock}")
+                    prOrange(to_node.inputs.keys())
 
                     if from_sock is None:
                         prOrange ("Candidates...")
@@ -759,17 +701,10 @@ def do_import(data, context):
             if (n := tree.nodes.get(name)) and (p := tree.nodes.get(p)):
                 n.parent = p
             # otherwise the frame node is missing because it was not included in the data e.g. when grouping nodes.
-
+        
         tree.is_executing = False
         tree.do_live_update = True
         
-        # try:
-        #     tree=context.space_data.path[0].node_tree
-        #     tree.update_tree(context)
-        # except: #update tree can cause all manner of errors
-        #     pass
-
-
 
 
 
@@ -885,7 +820,6 @@ class MantisImportNodeTree(Operator, ImportHelper):
         maxlen=255,  # Max internal buffer length, longer would be clamped.
     )
 
-
     def execute(self, context):
         return do_import_from_file(self.filepath, context)