3 import maya.cmds
as cmds
4 import maya.api.OpenMaya
as omAPI
5 from pyJsonAttrPatternInfo
import PyJsonAttrPatternInfo
as JsonKeys
6 from pyJsonAttrPatternInfo
import jsonDebug
as jsonDebug
9 def attributeTypeName(node, attr):
11 Find the name of an attribute's type. Some translation is required since
12 some construction types don't exactly match the class type as returned
13 by the attributeQuery command.
15 Returns None if the type is either unknown or not one of the ones this
16 script currently supports.
18 attributeType = cmds.attributeQuery( attr, node=node, attributeType=
True )
20 if attributeType
in JsonKeys.kNumericTypes:
23 if attributeType
in JsonKeys.kTypeMatrix:
26 if attributeType == JsonKeys.kTypeEnum:
29 if attributeType == JsonKeys.kTypeMessage:
32 if attributeType == JsonKeys.kTypeString:
38 class JSONPatternCreator:
40 Utility class to build JSON pattern objects from a set of attributes.
41 The most common use is to create a set of patterns from a node and
44 import JSONPatternCreator
45 patternCreator = JSONPatternCreator.JSONPatternCreator()
46 jsonNodePattern = patternCreator.nodeAsJSON( mayaNodeName )
47 json.dumps( jsonNodePattern, sort_keys=True, indent=4 )
52 Initialize the pattern creator.
59 def numericAttributeAsJSON(self, attr):
61 Find the numeric-specific attribute parameters
62 attr = Attribute belonging to the pattern
63 RETURN = JSON object representing the numeric-attribute-specific parameters
64 It's best to merge these into the main object one by one, rather
65 than making it a sub-object.
70 defaultValue = cmds.attributeQuery( attr, node=self.node, listDefault=
True )
71 if defaultValue !=
None and len(defaultValue) > 0:
72 pattern[JsonKeys.kKeyDefault] = defaultValue[0]
75 if cmds.attributeQuery( attr, node=self.node, minExists=
True ):
76 value = cmds.attributeQuery( attr, node=self.node, min=
True )
79 pattern[JsonKeys.kKeyMin] = value[0]
81 pattern[JsonKeys.kKeyMin] = value
83 if cmds.attributeQuery( attr, node=self.node, maxExists=
True ):
84 value = cmds.attributeQuery( attr, node=self.node, max=
True )
87 pattern[JsonKeys.kKeyMax] = value[0]
89 pattern[JsonKeys.kKeyMax] = value
91 if cmds.attributeQuery( attr, node=self.node, softMinExists=
True ):
92 value = cmds.attributeQuery( attr, node=self.node, softMin=
True )
94 pattern[JsonKeys.kKeySoftMin] = value[0]
96 if cmds.attributeQuery( attr, node=self.node, softMaxExists=
True ):
97 value = cmds.attributeQuery( attr, node=self.node, softMax=
True )
99 pattern[JsonKeys.kKeySoftMax] = value[0]
104 def compoundAttributeAsJSON(self, attr):
106 Find the compound-specific attribute parameters
107 attr = Attribute belonging to the pattern
108 RETURN = JSON object representing the compound-attribute-specific parameters
109 It's best to merge these into the main object one by one, rather
110 than making it a sub-object.
117 def lightDataAttributeAsJSON(self, attr):
119 Find the lightData-specific attribute parameters
120 attr = Attribute belonging to the pattern
121 RETURN = JSON object representing the lightData-attribute-specific parameters
122 It's best to merge these into the main object one by one, rather
123 than making it a sub-object.
130 def stringAttributeAsJSON(self, attr):
132 Find the string-specific attribute parameters
133 attr = Attribute belonging to the pattern
134 RETURN = JSON object representing the string-attribute-specific parameters
135 It's best to merge these into the main object one by one, rather
136 than making it a sub-object.
139 stringDefault = cmds.attributeQuery( attr, node=self.node, listDefault=
True )
140 if stringDefault !=
None and len(stringDefault) > 0:
141 pattern[JsonKeys.kKeyDefault] = stringDefault[0]
145 def matrixAttributeAsJSON(self, attr):
147 Find the matrix-specific attribute parameters
148 attr = Attribute belonging to the pattern
149 RETURN = JSON object representing the matrix-attribute-specific parameters
150 It's best to merge these into the main object one by one, rather
151 than making it a sub-object.
154 matrixDefault = cmds.attributeQuery( attr, node=self.node, listDefault=
True )
155 if matrixDefault !=
None and len(matrixDefault) > 0:
156 pattern[JsonKeys.kKeyDefault] = matrixDefault[0]
160 def typedAttributeAsJSON(self, attr):
162 Find the typed-specific attribute parameters
163 attr = Attribute belonging to the pattern
164 RETURN = JSON object representing the typed-attribute-specific parameters
165 It's best to merge these into the main object one by one, rather
166 than making it a sub-object.
173 def enumAttributeAsJSON(self, attr):
175 Find the enum-specific attribute parameters
176 attr = Attribute belonging to the pattern
177 RETURN = JSON object representing the enum-attribute-specific parameters
178 It's best to merge these into the main object one by one, rather
179 than making it a sub-object.
182 enumDefault = cmds.attributeQuery( attr, node=self.node, listDefault=
True )
183 if enumDefault !=
None and len(enumDefault) > 0:
184 pattern[JsonKeys.kKeyDefault] = int(enumDefault[0])
185 enumList = cmds.attributeQuery( attr, node=self.node, listEnum=
True)
186 if enumList !=
None and len(enumList) > 0:
187 pattern[JsonKeys.kKeyEnumNames] = enumList[0].split(
':')
191 def attributeAsJSON(self, attr):
193 Convert the attribute into its JSON attribute pattern equivalent.
194 attr = Attribute belonging to the pattern
195 RETURN = JSON object representing the attribute
197 jsonDebug(
'attributeAsJSON(%s)' % attr)
199 jsonAttr[
'name'] = attr
201 shortName = cmds.attributeQuery( attr, node=self.node, shortName=
True )
203 if shortName != attr: jsonAttr[
'shortName'] = shortName
205 niceName = cmds.attributeQuery( attr, node=self.node, niceName=
True )
206 jsonAttr[
'niceName'] = niceName
208 attributeType = attributeTypeName(self.node, attr)
209 jsonAttr[
'attributeType'] = attributeType
210 jsonDebug(
'... type %s' % attributeType )
212 categories = cmds.attributeQuery( attr, node=self.node, categories=
True )
213 if categories !=
None and len(categories) > 0:
214 jsonAttr[
'categories'] = categories
222 if cmds.attributeQuery( attr, node=self.node, affectsAppearance=
True ):
223 flagList.append(
'affectsAppearance' )
224 if cmds.attributeQuery( attr, node=self.node, affectsWorldspace=
True ):
225 flagList.append(
'affectsWorldspace' )
226 if not cmds.attributeQuery( attr, node=self.node, cachedInternally=
True ):
227 flagList.append(
'!cached' )
228 if not cmds.attributeQuery( attr, node=self.node, writable=
True ):
229 flagList.append(
'!canConnectAsDst' )
231 if not cmds.attributeQuery( attr, node=self.node, readable=
True ):
233 flagList.append(
'!canConnectAsSrc' )
234 if cmds.attributeQuery( attr, node=self.node, multi=
True ):
235 flagList.append(
'array' )
239 if cmds.attributeQuery( attr, node=self.node, indexMatters=
True )
and not isReadable:
240 flagList.append(
'indexMatters' )
241 if cmds.attributeQuery( attr, node=self.node, channelBox=
True ):
242 flagList.append(
'channelBox' )
243 if not cmds.attributeQuery( attr, node=self.node, connectable=
True ):
244 flagList.append(
'!connectable' )
245 if cmds.attributeQuery( attr, node=self.node, hidden=
True ):
246 flagList.append(
'hidden' )
247 if cmds.attributeQuery( attr, node=self.node, indeterminant=
True ):
248 flagList.append(
'indeterminant' )
249 if cmds.attributeQuery( attr, node=self.node, internalSet=
True ):
250 flagList.append(
'internal' )
251 if cmds.attributeQuery( attr, node=self.node, keyable=
True ):
252 flagList.append(
'keyable' )
254 flagList.append(
'!keyable' )
255 if cmds.attributeQuery( attr, node=self.node, renderSource=
True ):
256 flagList.append(
'renderSource' )
257 if not cmds.attributeQuery( attr, node=self.node, storable=
True ):
258 flagList.append(
'!storable' )
259 if cmds.attributeQuery( attr, node=self.node, usedAsColor=
True ):
260 flagList.append(
'usedAsColor' )
261 if cmds.attributeQuery( attr, node=self.node, usedAsFilename=
True ):
262 flagList.append(
'usedAsFilename' )
263 if cmds.attributeQuery( attr, node=self.node, usesMultiBuilder=
True ):
264 flagList.append(
'usesArrayDataBuilder' )
265 if cmds.attributeQuery( attr, node=self.node, worldspace=
True ):
266 flagList.append(
'worldspace' )
269 if len(flagList) > 0:
270 jsonAttr[
'flags'] = flagList
274 if attributeType ==
'enum':
275 jsonDebug(
'... decoding enum attribute parameters' )
276 extraInfo = self.enumAttributeAsJSON(attr )
277 elif attributeType
in JsonKeys.kNumericTypes:
278 jsonDebug(
'... decoding numeric attribute parameters' )
279 extraInfo = self.numericAttributeAsJSON(attr )
280 elif attributeType
in JsonKeys.kTypeMatrix:
281 jsonDebug(
'... decoding matrix attribute parameters' )
282 extraInfo = self.matrixAttributeAsJSON(attr )
283 elif attributeType ==
'string':
284 jsonDebug(
'... decoding string attribute parameters' )
285 extraInfo = self.stringAttributeAsJSON(attr )
286 elif attributeType ==
'message':
287 jsonDebug(
'... decoding message attribute parameters' )
288 elif attributeType ==
'compound':
289 jsonDebug(
'... decoding compound attribute parameters' )
290 extraInfo = self.compoundAttributeAsJSON(attr )
291 elif attributeType ==
'lightData':
292 jsonDebug(
'... decoding lightData attribute parameters' )
293 extraInfo = self.lightDataAttributeAsJSON(attr )
294 elif attributeType ==
'typed':
295 jsonDebug(
'... decoding typed attribute parameters' )
296 extraInfo = self.typedAttributeAsJSON(attr )
298 if extraInfo !=
None:
299 for extraKey
in extraInfo:
300 jsonAttr[extraKey] = extraInfo[extraKey]
305 def attributeListAsJSON(self, patternName, attrs):
307 Convert the list of attributes into a JSON attribute pattern object.
308 Any attributes not supported will be written out as an (ignored) JSON property
309 "unsupportedAttributes".
310 patternName = Name of the new pattern
311 attrs = Attributes belonging to the pattern
312 RETURN = JSON object containing the pattern for the attribute list
315 if patternName ==
None or len(patternName) == 0:
316 raise ValueError(
'Pattern name cannot be empty' )
317 if attrs ==
None or len(attrs) == 0:
320 unsupportedAttrs = []
323 pattern[
"name"] = patternName
326 attrType = attributeTypeName( self.node, attr )
328 supportedAttrs.append( attr )
330 unsupportedAttrs.append(
'%s:%s' % (attr, cmds.attributeQuery( attr, node=self.node, attributeType=
True )) )
331 unsupportedAttrs.sort()
332 supportedAttrs.sort()
334 if len(unsupportedAttrs) > 0:
335 pattern[
"unsupportedAttributes"] = unsupportedAttrs
338 for attr
in supportedAttrs:
339 attrPatternList.append( self.attributeAsJSON( attr ) )
340 pattern[
"attributes"] = attrPatternList
345 def nodeAsJSON(self, node):
347 node = Node based on which the pattern is to be created
348 RETURN = JSON object containing patterns for all node attributes
350 Method to walk through the list of attributes in a node and return the
351 supported types out in a JSON attribute pattern format. The returned
352 object can then be written out to a file or processed directly as a
353 JSON attribute pattern.
355 There will be up to three attribute patterns specified in the object,
356 one for each of static, dynamic, and extension attributes. Each of the
357 patterns is only created if there is at least one attribute of that type.
359 The names of the patterns for a node named "NODE" will be:
363 You really don't need me to explain which is which do you?
366 self.dgNode = omAPI.MSelectionList().add(node).getDependNode(0)
367 jsonDebug(
'Getting node information from %s' % str(self.dgNode) )
371 dynamicAttributes = cmds.listAttr( node, userDefined=
True )
372 extensionAttributes = cmds.listAttr( node, extension=
True )
373 allAttributes = cmds.listAttr( node )
374 staticAttributes = [attr
for attr
in allAttributes
if not attr
in extensionAttributes
and not attr
in dynamicAttributes]
377 newPattern = self.attributeListAsJSON(
"static_%s" % node, staticAttributes )
378 if newPattern !=
None:
379 patternList.append( newPattern )
383 newPattern = self.attributeListAsJSON(
"dynamic_%s" % node, dynamicAttributes )
384 if newPattern !=
None:
385 patternList.append( newPattern )
389 newPattern = self.attributeListAsJSON(
"extension_%s" % node, extensionAttributes )
390 if newPattern !=
None:
391 patternList.append( newPattern )
394 print 'ERR: Failed pattern creation on node %s (%s)' % (node, str(e))
402 Run an internal consistency test on the pattern creator to verify its
403 functions are operating correctly.
405 factoryIsLoaded = cmds.pluginInfo(
'pyJsonAttrPatternFactory.py', query=
True, loaded=
True)
406 if not factoryIsLoaded:
408 cmds.loadPlugin(
'pyJsonAttrPatternFactory.py', quiet=
True)
412 factoryIsLoaded = cmds.pluginInfo(
'pyJsonAttrPatternFactory.py', query=
True, loaded=
True)
417 if not factoryIsLoaded:
418 print 'Warning: JSON attribute pattern factory could not be loaded, test aborted'
424 "name": "testPattern",
427 "name" : "floatWithRanges",
429 "defaultValue" : 0.5,
434 "attributeType" : "float"
437 "name" : "float3WithRanges",
438 "shortName" : "ftwr",
439 "defaultValue" : [7.5, 7.6, 7.7],
440 "min" : [-17.0, -17.1, -17.2],
441 "max" : [27.0, 27.1, 27.2],
442 "attributeType" : "float3"
448 cmds.createAttrPatterns( patternType=
'json', patternDefinition=patterns )
449 cmds.file( force=
True, new=
True )
450 node = cmds.createNode(
'addMatrix' )
451 cmds.applyAttrPattern( node, patternName=
'testPattern' )
453 jsonString = self.nodeAsJSON( node )
454 print json.dumps(jsonString, indent=4)