sorry to necro
I have no idea how i stumbled across this one, but just for completeness sake: while brecht is correct to the point itself, your goal can still be achieved!
In other words: "think goal - not implementation details".
Instead of using your new node as input, use it as dummy and auto-add the correct uv-map whenever you draw a connection.
This node has been successfully tested in 2.78, the complete example is at the bottom.
step by step description
The update-function is a magic function, called on every update of the node itself (creation of the node, linking/unlinking/adding and removing sockets). It checks if we linked a uv_map and if so, replaces itself with the uv_map-node requested:
# this is called on linking/unlinking/adding/removing sockets and on startup before init
def update(self):# search for links and replace node with fitting uv_map node
for idx, skt in enumerate(self.outputs):
if not skt.is_linked: continue
ntree = self.id_data
link = skt.links[0]
lskt = link.to_socket# input-socket you linked to
ntree.links.remove(link)# clear out old link
uv_node = ntree.nodes.new('ShaderNodeUVMap')# add new uv-map node
uv_node.location = self.location# copy location
ntree.links.new(uv_node.outputs[0], lskt)# set up new link
uv_node.uv_map = skt.name# copy uv-map-id
# optionally: copy selection state
# (activate or deselect the uv_map-node)
if self==ntree.nodes.active:
ntree.nodes.active = uv_node
elif not self.select:
uv_node.select = False
# optionally: remove this dummy-node
ntree.nodes.remove(self)
break # we allready found our link
else:# only if we found no link, we rebuild our sockets
self.rebuild()
If there's no link made, the update-function calls self.rebuild. This function tries to fetch all uv_maps from the selected object and builds sockets, naming them accordingly. We also call it when updating the requested name for the referred object:
# build new sockets to link from
def rebuild(self):
if not self.is_updating:
uv_maps = self.uv_textures()
if uv_maps!=sorted(self.outputs.keys()):
self.is_updating = True # locking to prevent infinite updates
self.outputs.clear()# kick out old sockets
for uv_map in uv_maps:# add new sockets
self.outputs.new('NodeSocketVector', uv_map)
self.is_updating = False # locking to prevent infinite updates
This is a helper-function to fetch the uv_maps and sort them by name. If we can't find the requested object, we'll try the active object instead, otherwise we try the first object in bpy.data. If we find no uv_maps we print the error and return a blank list, so self.rebuild will clear out all sockets:
def uv_textures(self):
obj = bpy.data.objects.get(self.object) # first try name
if obj==None: # then try active
obj = bpy.context.scene.objects.active
if obj==None: # finally try first object
obj = bpy.data.objects[0]
try:
k = sorted(obj.data.uv_textures.keys())
except Exception as e:
print(self.name,": ", e.args[0])
k = []
return k
Of course, one could just as easily write the node to contain any uv_map on any object what-so-ever, the question is how you would produce the correct inputnode.
Hopefully we one day may be able to extend the build-in nodetrees functionality, but I wouldn't hold my breath (appearantly 2.8x provides partial support for that, though I couldn't stress test it yet).
The complete example:
import bpy
# Derived from the Node base type.
class MyCustomNode(bpy.types.Node):
# === Basics ===
'''A dummy-node for linking uv_maps to nodes''' # Description string
# Optional identifier string. If not explicitly defined,
bl_idname = 'CustomNodeType'# the python class name is used.
bl_label = 'UV-Map helper' # Label for nice name display
bl_icon = 'SOUND' # Icon identifier
# === Custom Properties === # we store the object-name and the update-state here
is_updating = bpy.props.BoolProperty(default=False)# update-state safety lock
object = bpy.props.StringProperty(update=lambda self,context:self.update())
# this is called on linking/unlinking/adding/removing sockets and on startup before init
def update(self):# search for links and replace node with fitting uv_map node
for idx, skt in enumerate(self.outputs):
if not skt.is_linked: continue
ntree = self.id_data
link = skt.links[0]
lskt = link.to_socket# input-socket you linked to
ntree.links.remove(link)# clear out old link
uv_node = ntree.nodes.new('ShaderNodeUVMap')# add new uv-map node
uv_node.location = self.location# copy location
ntree.links.new(uv_node.outputs[0], lskt)# set up new link
uv_node.uv_map = skt.name# copy uv-map-id
# optionally: copy selection state
# (activate or deselect the uv_map-node)
if self==ntree.nodes.active:
ntree.nodes.active = uv_node
elif not self.select:
uv_node.select = False
# optionally: remove this dummy-node
ntree.nodes.remove(self)
break # we allready found our link
else:# only if we found no link, we rebuild our sockets
self.rebuild()
# build new sockets to link from
def rebuild(self):
if not self.is_updating:
uv_maps = self.uv_textures()
if uv_maps!=sorted(self.outputs.keys()):
self.is_updating = True # locking to prevent infinite updates
self.outputs.clear()# kick out old sockets
for uv_map in uv_maps:# add new sockets
self.outputs.new('NodeSocketVector', uv_map)
self.is_updating = False # locking to prevent infinite updates
def uv_textures(self):
obj = bpy.data.objects.get(self.object) # first try name
if obj==None: # then try active
obj = bpy.context.scene.objects.active
if obj==None: # finally try first object
obj = bpy.data.objects[0]
try:
k = sorted(obj.data.uv_textures.keys())
except Exception as e:
print(self.name,": ", e.args[0])
k = []
return k# return the sorted uv_map-ids (else print the error and return blank list)
def draw_buttons(self, context, layout):
layout.prop_search(self, "object", bpy.data, "objects")
### Node Categories ###
import nodeitems_utils
from nodeitems_utils import NodeCategory, NodeItem
# we want this node only to show up in shader node trees
class MyNodeCategory(NodeCategory):
@classmethod
def poll(cls, context):
return context.space_data.tree_type == 'ShaderNodeTree'
# all categories in a list
node_categories = [
# identifier, label, items list
MyNodeCategory("SOMENODES", "Dummy-Nodes", items=[
# type, label, settings (as dict of repr-strings)
NodeItem("CustomNodeType", label="UV-Map helper", settings={
}),
]),
]
def register():
bpy.utils.register_class(MyCustomNode)
nodeitems_utils.register_node_categories("CUSTOM_NODES", node_categories)
def unregister():
nodeitems_utils.unregister_node_categories("CUSTOM_NODES")
bpy.utils.unregister_class(MyCustomNode)
if __name__ == "__main__":
try:
unregister()
except:
pass
register()