1 from builtins
import object
4 import maya.cmds
as cmds
5 import maya.api.OpenMaya
as omAPI
6 from pyJsonAttrPatternInfo
import PyJsonAttrPatternInfo
as JsonKeys
7 from pyJsonAttrPatternInfo
import jsonDebug
as jsonDebug
10 def attributeTypeName(node, attr):
12 Find the name of an attribute's type. Some translation is required since
13 some construction types don't exactly match the class type as returned
14 by the attributeQuery command.
16 Returns None if the type is either unknown or not one of the ones this
17 script currently supports.
19 attributeType = cmds.attributeQuery( attr, node=node, attributeType=
True )
21 if attributeType
in JsonKeys.kNumericTypes:
24 if attributeType
in JsonKeys.kTypeMatrix:
27 if attributeType == JsonKeys.kTypeEnum:
30 if attributeType == JsonKeys.kTypeMessage:
33 if attributeType == JsonKeys.kTypeString:
39 class JSONPatternCreator(object):
41 Utility class to build JSON pattern objects from a set of attributes.
42 The most common use is to create a set of patterns from a node and
45 import JSONPatternCreator
46 patternCreator = JSONPatternCreator.JSONPatternCreator()
47 jsonNodePattern = patternCreator.nodeAsJSON( mayaNodeName )
48 json.dumps( jsonNodePattern, sort_keys=True, indent=4, separators=(',', ': ') )
53 Initialize the pattern creator.
60 def numericAttributeAsJSON(self, attr):
62 Find the numeric-specific attribute parameters
63 attr = Attribute belonging to the pattern
64 RETURN = JSON object representing the numeric-attribute-specific parameters
65 It's best to merge these into the main object one by one, rather
66 than making it a sub-object.
71 defaultValue = cmds.attributeQuery( attr, node=self.node, listDefault=
True )
72 if defaultValue !=
None and len(defaultValue) > 0:
73 pattern[JsonKeys.kKeyDefault] = defaultValue[0]
76 if cmds.attributeQuery( attr, node=self.node, minExists=
True ):
77 value = cmds.attributeQuery( attr, node=self.node, min=
True )
80 pattern[JsonKeys.kKeyMin] = value[0]
82 pattern[JsonKeys.kKeyMin] = value
84 if cmds.attributeQuery( attr, node=self.node, maxExists=
True ):
85 value = cmds.attributeQuery( attr, node=self.node, max=
True )
88 pattern[JsonKeys.kKeyMax] = value[0]
90 pattern[JsonKeys.kKeyMax] = value
92 if cmds.attributeQuery( attr, node=self.node, softMinExists=
True ):
93 value = cmds.attributeQuery( attr, node=self.node, softMin=
True )
95 pattern[JsonKeys.kKeySoftMin] = value[0]
97 if cmds.attributeQuery( attr, node=self.node, softMaxExists=
True ):
98 value = cmds.attributeQuery( attr, node=self.node, softMax=
True )
100 pattern[JsonKeys.kKeySoftMax] = value[0]
105 def compoundAttributeAsJSON(self, attr):
107 Find the compound-specific attribute parameters
108 attr = Attribute belonging to the pattern
109 RETURN = JSON object representing the compound-attribute-specific parameters
110 It's best to merge these into the main object one by one, rather
111 than making it a sub-object.
118 def lightDataAttributeAsJSON(self, attr):
120 Find the lightData-specific attribute parameters
121 attr = Attribute belonging to the pattern
122 RETURN = JSON object representing the lightData-attribute-specific parameters
123 It's best to merge these into the main object one by one, rather
124 than making it a sub-object.
131 def stringAttributeAsJSON(self, attr):
133 Find the string-specific attribute parameters
134 attr = Attribute belonging to the pattern
135 RETURN = JSON object representing the string-attribute-specific parameters
136 It's best to merge these into the main object one by one, rather
137 than making it a sub-object.
140 stringDefault = cmds.attributeQuery( attr, node=self.node, listDefault=
True )
141 if stringDefault !=
None and len(stringDefault) > 0:
142 pattern[JsonKeys.kKeyDefault] = stringDefault[0]
146 def matrixAttributeAsJSON(self, attr):
148 Find the matrix-specific attribute parameters
149 attr = Attribute belonging to the pattern
150 RETURN = JSON object representing the matrix-attribute-specific parameters
151 It's best to merge these into the main object one by one, rather
152 than making it a sub-object.
155 matrixDefault = cmds.attributeQuery( attr, node=self.node, listDefault=
True )
156 if matrixDefault !=
None and len(matrixDefault) > 0:
157 pattern[JsonKeys.kKeyDefault] = matrixDefault[0]
161 def typedAttributeAsJSON(self, attr):
163 Find the typed-specific attribute parameters
164 attr = Attribute belonging to the pattern
165 RETURN = JSON object representing the typed-attribute-specific parameters
166 It's best to merge these into the main object one by one, rather
167 than making it a sub-object.
174 def enumAttributeAsJSON(self, attr):
176 Find the enum-specific attribute parameters
177 attr = Attribute belonging to the pattern
178 RETURN = JSON object representing the enum-attribute-specific parameters
179 It's best to merge these into the main object one by one, rather
180 than making it a sub-object.
183 enumDefault = cmds.attributeQuery( attr, node=self.node, listDefault=
True )
184 if enumDefault !=
None and len(enumDefault) > 0:
185 pattern[JsonKeys.kKeyDefault] = int(enumDefault[0])
186 enumList = cmds.attributeQuery( attr, node=self.node, listEnum=
True)
187 if enumList !=
None and len(enumList) > 0:
188 pattern[JsonKeys.kKeyEnumNames] = enumList[0].split(
':')
192 def attributeAsJSON(self, attr):
194 Convert the attribute into its JSON attribute pattern equivalent.
195 attr = Attribute belonging to the pattern
196 RETURN = JSON object representing the attribute
198 jsonDebug(
'attributeAsJSON(%s)' % attr)
200 jsonAttr[
'name'] = attr
202 shortName = cmds.attributeQuery( attr, node=self.node, shortName=
True )
204 if shortName != attr: jsonAttr[
'shortName'] = shortName
206 niceName = cmds.attributeQuery( attr, node=self.node, niceName=
True )
207 jsonAttr[
'niceName'] = niceName
209 attributeType = attributeTypeName(self.node, attr)
210 jsonAttr[
'attributeType'] = attributeType
211 jsonDebug(
'... type %s' % attributeType )
213 categories = cmds.attributeQuery( attr, node=self.node, categories=
True )
214 if categories !=
None and len(categories) > 0:
215 jsonAttr[
'categories'] = categories
223 if cmds.attributeQuery( attr, node=self.node, affectsAppearance=
True ):
224 flagList.append(
'affectsAppearance' )
225 if cmds.attributeQuery( attr, node=self.node, affectsWorldspace=
True ):
226 flagList.append(
'affectsWorldspace' )
227 if not cmds.attributeQuery( attr, node=self.node, cachedInternally=
True ):
228 flagList.append(
'!cached' )
229 if not cmds.attributeQuery( attr, node=self.node, writable=
True ):
230 flagList.append(
'!canConnectAsDst' )
232 if not cmds.attributeQuery( attr, node=self.node, readable=
True ):
234 flagList.append(
'!canConnectAsSrc' )
235 if cmds.attributeQuery( attr, node=self.node, multi=
True ):
236 flagList.append(
'array' )
240 if cmds.attributeQuery( attr, node=self.node, indexMatters=
True )
and not isReadable:
241 flagList.append(
'indexMatters' )
242 if cmds.attributeQuery( attr, node=self.node, channelBox=
True ):
243 flagList.append(
'channelBox' )
244 if not cmds.attributeQuery( attr, node=self.node, connectable=
True ):
245 flagList.append(
'!connectable' )
246 if cmds.attributeQuery( attr, node=self.node, hidden=
True ):
247 flagList.append(
'hidden' )
248 if cmds.attributeQuery( attr, node=self.node, indeterminant=
True ):
249 flagList.append(
'indeterminant' )
250 if cmds.attributeQuery( attr, node=self.node, internalSet=
True ):
251 flagList.append(
'internal' )
252 if cmds.attributeQuery( attr, node=self.node, keyable=
True ):
253 flagList.append(
'keyable' )
255 flagList.append(
'!keyable' )
256 if cmds.attributeQuery( attr, node=self.node, renderSource=
True ):
257 flagList.append(
'renderSource' )
258 if not cmds.attributeQuery( attr, node=self.node, storable=
True ):
259 flagList.append(
'!storable' )
260 if cmds.attributeQuery( attr, node=self.node, usedAsColor=
True ):
261 flagList.append(
'usedAsColor' )
262 if cmds.attributeQuery( attr, node=self.node, usedAsFilename=
True ):
263 flagList.append(
'usedAsFilename' )
264 if cmds.attributeQuery( attr, node=self.node, usesMultiBuilder=
True ):
265 flagList.append(
'usesArrayDataBuilder' )
266 if cmds.attributeQuery( attr, node=self.node, worldspace=
True ):
267 flagList.append(
'worldspace' )
270 if len(flagList) > 0:
271 jsonAttr[
'flags'] = flagList
275 if attributeType ==
'enum':
276 jsonDebug(
'... decoding enum attribute parameters' )
277 extraInfo = self.enumAttributeAsJSON(attr )
278 elif attributeType
in JsonKeys.kNumericTypes:
279 jsonDebug(
'... decoding numeric attribute parameters' )
280 extraInfo = self.numericAttributeAsJSON(attr )
281 elif attributeType
in JsonKeys.kTypeMatrix:
282 jsonDebug(
'... decoding matrix attribute parameters' )
283 extraInfo = self.matrixAttributeAsJSON(attr )
284 elif attributeType ==
'string':
285 jsonDebug(
'... decoding string attribute parameters' )
286 extraInfo = self.stringAttributeAsJSON(attr )
287 elif attributeType ==
'message':
288 jsonDebug(
'... decoding message attribute parameters' )
289 elif attributeType ==
'compound':
290 jsonDebug(
'... decoding compound attribute parameters' )
291 extraInfo = self.compoundAttributeAsJSON(attr )
292 elif attributeType ==
'lightData':
293 jsonDebug(
'... decoding lightData attribute parameters' )
294 extraInfo = self.lightDataAttributeAsJSON(attr )
295 elif attributeType ==
'typed':
296 jsonDebug(
'... decoding typed attribute parameters' )
297 extraInfo = self.typedAttributeAsJSON(attr )
299 if extraInfo !=
None:
300 for extraKey
in extraInfo:
301 jsonAttr[extraKey] = extraInfo[extraKey]
306 def attributeListAsJSON(self, patternName, attrs):
308 Convert the list of attributes into a JSON attribute pattern object.
309 Any attributes not supported will be written out as an (ignored) JSON property
310 "unsupportedAttributes".
311 patternName = Name of the new pattern
312 attrs = Attributes belonging to the pattern
313 RETURN = JSON object containing the pattern for the attribute list
316 if patternName ==
None or len(patternName) == 0:
317 raise ValueError(
'Pattern name cannot be empty' )
318 if attrs ==
None or len(attrs) == 0:
321 unsupportedAttrs = []
324 pattern[
"name"] = patternName
327 attrType = attributeTypeName( self.node, attr )
329 supportedAttrs.append( attr )
331 unsupportedAttrs.append(
'%s:%s' % (attr, cmds.attributeQuery( attr, node=self.node, attributeType=
True )) )
332 unsupportedAttrs.sort()
333 supportedAttrs.sort()
335 if len(unsupportedAttrs) > 0:
336 pattern[
"unsupportedAttributes"] = unsupportedAttrs
339 for attr
in supportedAttrs:
340 attrPatternList.append( self.attributeAsJSON( attr ) )
341 pattern[
"attributes"] = attrPatternList
346 def nodeAsJSON(self, node):
348 node = Node based on which the pattern is to be created
349 RETURN = JSON object containing patterns for all node attributes
351 Method to walk through the list of attributes in a node and return the
352 supported types out in a JSON attribute pattern format. The returned
353 object can then be written out to a file or processed directly as a
354 JSON attribute pattern.
356 There will be up to three attribute patterns specified in the object,
357 one for each of static, dynamic, and extension attributes. Each of the
358 patterns is only created if there is at least one attribute of that type.
360 The names of the patterns for a node named "NODE" will be:
364 You really don't need me to explain which is which do you?
367 self.dgNode = omAPI.MSelectionList().add(node).getDependNode(0)
368 jsonDebug(
'Getting node information from %s' % str(self.dgNode) )
372 dynamicAttributes = cmds.listAttr( node, userDefined=
True )
373 extensionAttributes = cmds.listAttr( node, extension=
True )
374 allAttributes = cmds.listAttr( node )
375 staticAttributes = [attr
for attr
in allAttributes
if not attr
in extensionAttributes
and not attr
in dynamicAttributes]
378 newPattern = self.attributeListAsJSON(
"static_%s" % node, staticAttributes )
379 if newPattern !=
None:
380 patternList.append( newPattern )
384 newPattern = self.attributeListAsJSON(
"dynamic_%s" % node, dynamicAttributes )
385 if newPattern !=
None:
386 patternList.append( newPattern )
390 newPattern = self.attributeListAsJSON(
"extension_%s" % node, extensionAttributes )
391 if newPattern !=
None:
392 patternList.append( newPattern )
394 except Exception
as e:
395 print(
'ERR: Failed pattern creation on node %s (%s)' % (node, str(e)))
403 Run an internal consistency test on the pattern creator to verify its
404 functions are operating correctly.
406 factoryIsLoaded = cmds.pluginInfo(
'pyJsonAttrPatternFactory.py', query=
True, loaded=
True)
407 if not factoryIsLoaded:
409 cmds.loadPlugin(
'pyJsonAttrPatternFactory.py', quiet=
True)
413 factoryIsLoaded = cmds.pluginInfo(
'pyJsonAttrPatternFactory.py', query=
True, loaded=
True)
418 if not factoryIsLoaded:
419 print(
'Warning: JSON attribute pattern factory could not be loaded, test aborted')
425 "name": "testPattern",
428 "name" : "floatWithRanges",
430 "defaultValue" : 0.5,
435 "attributeType" : "float"
438 "name" : "float3WithRanges",
439 "shortName" : "ftwr",
440 "defaultValue" : [7.5, 7.6, 7.7],
441 "min" : [-17.0, -17.1, -17.2],
442 "max" : [27.0, 27.1, 27.2],
443 "attributeType" : "float3"
449 cmds.createAttrPatterns( patternType=
'json', patternDefinition=patterns )
450 cmds.file( force=
True, new=
True )
451 node = cmds.createNode(
'addMatrix' )
452 cmds.applyAttrPattern( node, patternName=
'testPattern' )
454 jsonString = self.nodeAsJSON( node )
455 print(json.dumps(jsonString, indent=4, separators=(
',',
': ')))