recently at work i was asked on doing a “singleton” maya node. The plan was to have a sceneData node that contains data about the scene (a serialized xml sheet),
and should only be once in every scene. I managed to do this with the c++ API. Basically what im doing is utilizing the post constructor to search for types of that
node and copy the data of the pre-existing node over to the newly created one. Then i delete the pre-existing one.
There are two things about this:
I couldnt get this to work with the python API. It doesnt let me access the MObject of itself within the postConstructor.
It exists and is of type kPluginDependNode, but MPlugs that try to access its attributes remain empty (as do MFnDependencyNode function sets).
What are your thoughts on singleton nodes in Maya? Is there an easier way, common practice to do this?
There are nodes in Maya that cannot be duplicated (time for example), but they are pre-existing in a new scene.
How much data do you need to serialize? If it’s reasonably sized, you can just dump it to a binary-encoded string in the fileInfo. if the data is already xml, you can just decode it and read it with little extra fuss. I typically use base64Encode to stash the info so I don’t have to worry about ma vs mb or escaping/punctuation and so on. You could do the same with pickle, although the long term viability of that data depends on the classes that you’re serialzing not changing.
If possible, go with a builtin facility like fileInfo instead of creating dependencies on custom plugins and so on. Nothing stinks like trying to open somebody else’s scene and getting lots of errors because it depends on a plugin or something that died a long time ago or you don’t have — outsourcers etc will be vey irritated if they run into that often, and even you can get bit by it if you open older files from previous projects. Passive data is best when it’s really passive
We always use time1 to store this type of scene data onto, wrapped by Red9 Meta which has a sceneReview setup inside it already. Like Seth I use an encoder to just push any data I want onto custom attrs on Time1. Nice thing about piggy-backing time1 is that you don’t need to manage any custom nodes.
FWIW, here’s my code. This versions depends on YAML, but I bet you could swap the built-in JSON in for it with no extra work
'''
mayaPersist - namespace for functions related to storing data in fileInfo objects inside the current Maya file
I've tested this (a little) with as many as 100,000 integers - it works but it's slooow at that size
< 1000 values seems imperceptible
'''
import yaml
import base64
from maya.cmds import fileInfo
import itertools
def save(key, value):
'''
save the specified value as a base64 encoded yaml dunp at key 'key'
'''
encoded =encode(value)
fileInfo(key, encoded)
def load(key):
'''
return the value stored at 'key', or None if the value can't be found
@note it is possible to store a 'None' in the value, so this doesn't prove that the key does not exist !
'''
answer = fileInfo(key, q=True)
if not answer:
return None
return decode(answer[0])
def exists(key):
'''
returns true if the specified key exists
'''
answer = fileInfo(key, q=True)
return len(answer) != 0
def ls():
'''
a generator that returns all of the key-value pairs in this file's fileInfo
@note: these are not decoded, because they contain a mix of native stirngs and b64 values
'''
all_values = fileInfo(q=True)
keys = itertools.islice(all_values, 0, None, 2)
values = itertools.islice(all_values, 1, None, 2)
return itertools.izip(keys, values)
def delete(key):
'''
remove the key and any data stored with it from this file
'''
fileInfo(rm=key)
def decode(value):
'''
convert a base64'ed yaml object back into a maya object
if the object is not encoded (eg, one of the default string values) return it untouched
'''
try:
val = base64.b64decode(value)
return yaml.load(val)
except TypeError:
return value
def encode (value):
'''
return the supplied value encoded into base64-packed YAML dump
'''
return base64.b64encode(yaml.dump(value))
Thanks for your answers guys. I guess the fileInfo is custom built for such. What exactly is fileInfo, though? I searched for a fileinfo node type, but didnt get anything?
It’s not a node - its a command that stores strings directly in the file header as a key-value pair. If you try the mel command ‘fileInfo -q’ you’ll see theres a bunch of metadata (file version, host OS and so on) already stored that way.
The encoding business makes sure that your data doesn’t accidentally mess up MA files - if you leave a dangling quote or something it could mess up the file parser. With the encodiing it just looks like a solid block of random alphanumeric characters, but it decodes back to your original JSON/YAML/XML or whatever string
Have you guys tried the new MetaData api in Maya yet, I keep meaning to take a look at it and maybe do some wrap in the Red9 setups to utilize it, seems like a robust way to interface scene level data for more complex data.
This is pretty awesome. I’ve been looking for a way to dump data into a scene. Out of curiosity why not use the pickle module to serialize/deserialize? Is yaml native?
no, yaml is an external module. You could do the same thing with JSON. The main reason not to use pickle is not to have to worry about versioning issues - if you change a class next year you might have trouble recovering this year’s pickled instances. Stickling with yaml (or json, or xml) makes sure that it’s pure data and so more survivable.
@theodox: Do you know how to get/set the fileInfo stuff in c++ ? I couldn’t locate anything that resembled it. The only example i could find is just calling the mel command.
void maTranslator::writeFileInfo(fstream& f)
{
//
// There's no direct access to the scene's fileInfo from within the API,
// so we have to call MEL's 'fileInfo' command.
//
}