class MetaDataContainer: def __init__(self): pass self.metaHeader = list(); self.metaData = list(); self.headerLookup = dict(); self.syncLookup = dict(); def getValueByName(self, entry, name): idx = self.headerLookup[name]; return entry[idx]; def getIndexByLookupFieldValue(self, value): try: index = self.syncLookup[value]; except KeyError: return None return index; def getMetaDataByLookupFieldValue(self, value): index = self.syncLookup[value]; if (index is not None): return self.metaData[index]; def prepareSyncLookup(self, syncField): self.syncLookup = dict() self.syncField = syncField; for index, line in enumerate(self.metaData): #line = self.metaData[index]; # prepare syncField speedup if self.syncField is not None: syncColNum = int(self.headerLookup[self.syncField]); #print syncColNum self.syncLookup[line[syncColNum]] = index; def loadFromFile(self, file): # only csv supported if (not file.strip().endswith(".csv")): # check first to avoid double onChange callback call if (nuke.thisNode().knob(KNOBNAME_METADATAFILE).value() != ""): nuke.message("This is not a .csv file!"); nuke.thisNode().knob(KNOBNAME_METADATAFILE).setValue("") return; f = open(file, "r"); content = f.readlines(); for linenumber, line in enumerate(content): # line = content[linenumber]; #print line if (linenumber == 0): self.metaHeader = line.split("\t"); # store the column number for each name for col, data in enumerate(self.metaHeader): self.headerLookup[data] = int(col); continue; dataColumns = line.split("\t"); self.metaData.append(dataColumns); #dataEntry = dict(); #for col, data in enumerate(metaHeader): # # print col # # key = header[col]; # dataEntry[data] = dataColumns[col]; #self.metaData.append(dataEntry); #print self.metaData import nuke import threading nodeTemplate = """ Group { name MetaDataFromFile addUserKnob {20 properties l Properties} addUserKnob {2 metadatafile l "Metadata File" t "CSV Metadata file"} addUserKnob {1 prefix l "Key Prefix" t} prefix "ext/" addUserKnob {3 offset l "Offset"} addUserKnob {26 ""} addUserKnob {22 btn_sync l "Synchronize" T "metaDataFromFile.synchronize()"} addUserKnob {4 srcfield l "Sync Input Field" M {"No Sync" ""}} addUserKnob {4 targetfield l "Sync External Field" M {"No Sync" ""}} metadatafile "" knobChanged "metaDataFromFile.userKnobChanged()" updateUI "metaDataFromFile.updateUI()" } Input { inputs 0 name Input1 xpos 0 } ModifyMetaData { metadata { } name ModifyMetaData xpos 0 ypos 150 } Output { name Output1 xpos 0 ypos 300 } end_group """ metaDataLineTemplate = """{set "%s%s" "\[python metaDataFromFile.getMetaField(%s)]"}""" userKnobTemplate = "addUserKnob {1 %s}" KNOBNAME_METADATAFILE = "metadatafile" KNOBNAME_OFFSET ="offset" KNOBNAME_SRCFIELD = "srcfield" KNOBNAME_TARGETFIELD = "targetfield" KNOBNAME_PREFIX = "prefix" def loadMetadataFile(node, file): meta = MetaDataContainer(); meta.loadFromFile(file); metaKeys = list(); prefix = node.knob(KNOBNAME_PREFIX).value() # set values for each frame for num, key in enumerate(meta.metaHeader): pass metaKeys.append(metaDataLineTemplate % (prefix.strip(), key, int(num))) # get the metadata node mmd = nuke.allNodes(group=nuke.thisGroup(), filter="ModifyMetaData")[0] mmd["metadata"].fromScript("\n".join(metaKeys)); # fill src/target fields if node.input(0) is not None: srcFields = node.input(0).metadata().keys() node.knob(KNOBNAME_SRCFIELD).setValues(srcFields); targetFields = meta.metaHeader; node.knob(KNOBNAME_TARGETFIELD).setValues(targetFields); return meta import linecache def getMetaField(column, offset = None): try: if (offset is None): offset = int(nuke.thisParent().knob(KNOBNAME_OFFSET).value()); # add 1, the first line is the header idx = int(nuke.frame())- offset + 1 file = getMetaFile() return linecache.getline(file,int(idx)).split("\t")[column].strip() except Exception as e: return "metadata error: %s" %e def getMetaFile(): return nuke.thisParent().knob(KNOBNAME_METADATAFILE).value(); def updateUI(): pass def userKnobChanged(): if ((nuke.thisKnob().name() == KNOBNAME_METADATAFILE) or (nuke.thisKnob().name() == "inputChange") or (nuke.thisKnob().name() == KNOBNAME_PREFIX)) : loadMetadataFile(nuke.thisNode(), nuke.thisNode().knob(KNOBNAME_METADATAFILE).value()); def createNode(): from PySide2 import QtWidgets QtWidgets.QApplication.clipboard().setText(nodeTemplate) newNode = nuke.nodePaste("%clipboard%") #nuke.addKnobChanged(userKnobChanged, node=newNode) pass def synchronize(): meta = loadMetadataFile(nuke.thisNode(),nuke.thisNode().knob(KNOBNAME_METADATAFILE).value() ); srcField = nuke.thisNode().knob(KNOBNAME_SRCFIELD).value(); targetField = nuke.thisNode().knob(KNOBNAME_TARGETFIELD).value(); meta.prepareSyncLookup(targetField); # search for a matching value at the current frame metaValue = nuke.thisNode().metadata(srcField) index = nuke.frame()-meta.getIndexByLookupFieldValue(metaValue) - 1; if (index is None): nuke.message("Cannot synchronize: meta data value '%s' (%s) not found in external field '%s'" % (metaValue, srcField, targetField)) return nuke.thisNode().knob(KNOBNAME_OFFSET).setValue(index); if __name__ == "__main__": load();