from UserCustomization import UserCustomBase, CustomInfo
from MessageInterpreter import MessageInterpreter
from Message import Message
from MessageRegistry import theMessageRegistry
from MessageDocs import *
from Messaging import printDebug
from SceneGraphUtilities import CollectNodes
import AlternativeIO
import ModelIO
import ShotIO
import StoryboardIO
import BehaviorIO
import EnvironmentIO
from EnvironmentLibrary import EnvironmentLibrary
def instantiate():
return MessageLabelCustomization()
class MessageLabelCustomization(UserCustomBase):
"""
Registers a bunch of helper messages which allow users to send messages
by label instead of IDs. Mostly used for sending messages from HTML pages.
"""
def __init__(self):
self.__myInterpreter = MessageLabelInterpreter()
def getInterpreter(self, isInteractive):
if isInteractive:
return self.__myInterpreter
return None
class MessageLabelInterpreter(MessageInterpreter):
kAlternativeSetId = 1
kAlternativeId = 2
kNodeId = 4
kSlideId = 8
kShotId = 16
kBehaviorId = 32
kEnvironmentId = 64
kList = 65536
kLabelSuffix = '_BYLABEL'
def __init__(self):
MessageInterpreter.__init__(self)
self.__myDocument = None
self.__myAlternatives = None
self.__myModels = None
self.__myIsActive = True
self.__myMessages = {
"ALTERNATIVE_DELETE_SET" : (self.kAlternativeSetId, )
, "ALTERNATIVE_RENAME_SET" : (self.kAlternativeSetId, 0)
, "ALTERNATIVE_CREATE_DATA" : (self.kAlternativeSetId, 0, 0, 0, 0)
, "ALTERNATIVE_CREATE" : (self.kAlternativeSetId, 0, 0, 0, 0)
, "ALTERNATIVE_DELETE" : (self.kAlternativeSetId, self.kAlternativeId)
, "ALTERNATIVE_RENAME" : (self.kAlternativeSetId, self.kAlternativeId, 0)
, "ALTERNATIVE_DUPLICATE" : (self.kAlternativeSetId, self.kAlternativeId, 0)
, "ALTERNATIVE_ADD_ITEMS" : (self.kAlternativeSetId, self.kAlternativeId, self.kNodeId | self.kList)
, "ALTERNATIVE_REMOVE_ITEMS" : (self.kAlternativeSetId, self.kAlternativeId, self.kNodeId | self.kList)
, "ALTERNATIVE_SET_IMAGE" : (self.kAlternativeSetId, self.kAlternativeId, 0)
, "BEHAVIOR_START" : (self.kBehaviorId, 0)
, "BEHAVIOR_STOP" : (self.kBehaviorId, 0)
, "BEHAVIORS_LINK" : (self.kBehaviorId | self.kList, )
, "BEHAVIORS_UNLINK" : (self.kBehaviorId | self.kList, )
, "BEHAVIOR_SELECT_ASSIGNED" : (self.kBehaviorId, 0)
, "BEHAVIOR_PARAMETER_CHANGE" : (self.kBehaviorId, 0, 0)
, "BEHAVIOR_ADD_NODES" : (self.kBehaviorId, self.kNodeId | self.kList)
, "BEHAVIOR_REMOVE_NODES" : (self.kBehaviorId, self.kNodeId | self.kList)
, "BEHAVIOR_ENABLE" : (self.kBehaviorId, 0)
, "BEHAVIOR_SHOW_PROPERTIES" : (self.kBehaviorId, )
, "BEHAVIOR_UI_PROPERTIES" : (self.kBehaviorId, )
, "BEHAVIOR_UI_CONTROLS" : (self.kBehaviorId, 0)
, "BEHAVIOR_SAVE_THUMBNAIL" : (self.kBehaviorId, )
, "ENVIRONMENT_LOAD" : (self.kEnvironmentId, )
, "ENVIRONMENT_RENAME" : (self.kEnvironmentId, 0)
, "ENVIRONMENT_UPDATE_LIST" : (self.kEnvironmentId | self.kList, )
, "ENVIRONMENT_CHANGE_BACKDROP_IMAGE" : (self.kEnvironmentId | self.kList, 0, 0)
, "ENVIRONMENT_CHANGE_BACKPLATE_PARAMETERS" : (self.kEnvironmentId | self.kList, 0)
, "ENVIRONMENT_CHANGE_LIGHTING_PARAMETERS" : (self.kEnvironmentId | self.kList, 0, 0, 0, 0)
, "SHOT_EDIT" : (self.kShotId, )
, "SHOT_DELETE" : (self.kShotId, )
, "SHOT_RENAME" : (self.kShotId, 0)
, "SHOT_CHANGE_TYPE" : (self.kShotId, 0, 0)
, "SHOT_SET_PARAMETERS" : (self.kShotId, 0, 0)
, "SHOT_SAVE_THUMBNAIL" : (self.kShotId, 0)
, "SHOT_REFRESH_THUMBNAIL" : (self.kShotId, )
, "SHOT_MOVE_TO_KEYFRAME" : (self.kShotId, )
, "SHOT_POSITION_AFTER" : (self.kShotId, self.kShotId)
, "SHOT_UPDATE_DIALOG_DATA" : (self.kShotId, )
, "SHOT_SHOW_STAGESHOTS" : (self.kShotId, )
, "SLIDE_DELETE" : (self.kSlideId | self.kList, )
, "SLIDE_DUPLICATE" : (self.kSlideId | self.kList, )
, "SLIDE_ITEM_ADD" : (0, self.kSlideId, 0, 0)
, "SLIDE_ITEM_POSITION_CHANGE" : (self.kSlideId, 0, 0, 0)
, "SLIDE_ITEM_REMOVE" : (self.kSlideId, 0, 0)
, "SLIDE_POSITION_CHANGE" : (self.kSlideId, 0)
, "SLIDE_RENAME" : (self.kSlideId, 0)
, "SLIDE_THUMBNAIL_SAVE" : (self.kSlideId, )
, "SLIDE_THUMBNAIL_SET" : (self.kSlideId, 0)
, "DELETE" : (self.kNodeId | self.kList, )
, "UNDELETE" : (self.kNodeId | self.kList, )
, "HIDE" : (self.kNodeId | self.kList, 0)
, "HIDE_UNSELECTED" : (self.kNodeId | self.kList, )
, "DUPLICATE" : (self.kNodeId | self.kList, )
, "MIRROR_DUPLICATE" : (0, self.kNodeId | self.kList)
, "SHOW" : (self.kNodeId | self.kList, )
, "NODE_RENAME" : (self.kNodeId | self.kList, 0)
, "GROUP_CREATE" : (0, self.kNodeId, 0)
, "GROUP_NODES" : (self.kNodeId | self.kList, )
, "UNGROUP_NODES" : (self.kNodeId | self.kList, )
, "GROUP_COLLAPSE" : (self.kNodeId | self.kList, )
, "GROUP_EXPAND" : (self.kNodeId | self.kList, )
, "ALIGN_TO_FLOOR" : (self.kNodeId | self.kList, )
, "SELECT" : (self.kNodeId | self.kList, 0)
, "SELECT_VISIBLE" : (self.kNodeId | self.kList, 0)
, "MODEL_SELECT" : (self.kNodeId | self.kList, 0)
, "MODEL_HIGHLIGHT" : (self.kNodeId | self.kList, )
, "ROTATE_WORLDSPACE" : (0, 0, 0, 0, self.kNodeId | self.kList)
, "TRANSLATE_WORLDSPACE" : (0, 0, 0, 0, self.kNodeId | self.kList)
, "PIVOT_MOVE_WORLDSPACE" : (0, 0, 0, 0, self.kNodeId | self.kList)
, "MODEL_DELETE" : (self.kNodeId | self.kList, )
, "MODEL_RENAME" : (self.kNodeId | self.kList, 0)
, "MODEL_SWITCH_UP_AXIS" : (self.kNodeId, )
, "MODEL_SCALE" : (self.kNodeId | self.kList, 0)
, "MODEL_IMPORT" : (self.kNodeId, 0, 0)
, "MODEL_IMPORT_REPLACE" : (self.kNodeId, 0, 0)
, "MODEL_PREPARE" : (self.kNodeId, 0, 0)
, "MODEL_PREPARE_START" : (self.kNodeId, )
, "MODEL_PREPARE_STOP" : (self.kNodeId, )
, "MODEL_IMPORT_CHANGE_ORIGINAL" : (self.kNodeId, 0 )
, "GEOMETRY_FLIP_NORMALS" : (self.kNodeId | self.kList, 0)
, "GEOMETRY_COPY_TRANSFORM" : (self.kNodeId | self.kList, )
, "GEOMETRY_PASTE_TRANSFORM" : (self.kNodeId | self.kList, )
, "GEOMETRY_SET_TRANSFORM" : (self.kNodeId | self.kList, 0)
, "GEOMETRY_UP_AXIS_SWITCH" : (self.kNodeId | self.kList, )
, "GEOMETRY_SCALE" : (self.kNodeId | self.kList, 0)
, "SHADOW_SET_RECEIVER" : (self.kNodeId | self.kList, 0)
, "SHADOW_SET_RECEIVER_OBJECTS" : (self.kNodeId | self.kList, 0)
, "SHADOW_SET_CASTER_OBJECTS" : (self.kNodeId | self.kList, 0)
, "SHADOW_LIGHT_CREATE" : (self.kNodeId | self.kList, )
, "SHADOW_LIGHT_DELETE" : (self.kNodeId, )
, "SHADOW_RECEIVER_REPLACE" : (self.kNodeId | self.kList, self.kNodeId | self.kList)
, "PATCH_CURRENT_OBJECT" : (self.kNodeId, )
, "PATCH_OPERATION" : (self.kNodeId, 0, 0, 0)
, "PATCH_OPERATION_EXPLICIT" : (self.kNodeId, 0, 0)
, "PATCH_REQUEST_STATE" : (self.kNodeId, )
, "PATCH_STATE" : (self.kNodeId, 0, 0)
, "APPEARANCE_PARAMETERS_CHANGE" : (self.kNodeId | self.kList, 0, 0, 0, 0, 0)
, "MATERIAL_COMPUTE_OCCLUSION" : (self.kNodeId | self.kList, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
, "TEXTURE_MATRIX_TRANSFORM" : (self.kNodeId | self.kList, 0, 0, 0)
}
for id in self.__myMessages:
self.__createMessageWrapper(id, self.__myMessages[id])
def deactivate(self):
self.__myDocument = None
self.__myModels = None
self.__myAlternatives = None
self.__myBehaviors = None
self.__myStoryboard = None
self.__myEnvironments = None
def __createMessageWrapper(self, messageId, messageData):
"""
Registers a message named 'MESSAGE_NAME_BYLABEL' for the specified
messageId and adds a method to this class instance to handle the new message.
"""
dataTypes = theMessageRegistry.dataType(messageId)
id = messageId + self.kLabelSuffix
if theMessageRegistry.isRegistered(id):
printDebug(["NOTE: Message id %s already registered" % id])
return
if dataTypes is None:
printDebug(["ERROR: Type info not found for message " + messageId])
return
if len(dataTypes) != len(messageData):
printDebug(["ERROR: Number of arguments for message " + messageId + " does not match!"])
printDebug([" Expected: %d got %d" % (len(dataTypes), len(messageData))])
return
documentation = eval(messageId + "_Doc")
theMessageRegistry.register( id, dataTypes, Message.kUndoable, documentation )
setattr(self, id, self.__handleMessage)
def __handleMessage(self, message):
"""
Accepts the label form of all messages listed in self.__myMessages, and then
sends out an appropriate
"""
originalMessage = message.id[:-len(self.kLabelSuffix)]
assert(originalMessage in self.__myMessages)
self.__processLabelMessage(originalMessage, message.data,
self.__myMessages[originalMessage])
def __processLabelMessage(self, messageId, data, flags):
data = list(data)
data = self.__processAlternativeLabels(data, flags)
data = self.__processNodeLabels(data, flags)
data = self.__processShotLabels(data, flags)
data = self.__processBehaviorLabels(data, flags)
data = self.__processSlideLabels(data, flags)
data = self.__processEnvironmentLabels(data, flags)
dataTypes = theMessageRegistry.dataType(messageId)
data = self.__fixDataTypes(data, dataTypes)
printDebug(["Labels Converted:" + messageId + str(tuple(data))])
self.sendMessage(messageId, tuple(data))
def __fixDataTypes(self, data, dataTypes):
"""
Finds any fields in data which aren't the right data type (as specified by
dataTypes) and tries to cast them to the correct type.
data - list of data fields
dataTypes - a list of python types
"""
for i in range(len(data)):
if data.__class__ != dataTypes[i]:
try:
data[i] = dataTypes[i](data[i])
except:
printDebug(["ERROR: Cannot cast message parameter " + str(i) + "to the correct type!"])
return data
def __processSlideLabels(self, data, flags):
"""
Replaces all slide labels with corresponding slide ids
"""
for (i, flag) in enumerate(flags):
if flag & self.kSlideId and flag & self.kList:
slideIds = []
for label in data[i]:
slideIds.append(self.__getSlideId(label))
data[i] = slideIds
elif flag & (self.kSlideId):
data[i] = self.__getSlideId(data[i])
return data
def __getSlideId(self, slideLabel):
for slide in self.__myStoryBoard:
if slide.name() == slideLabel:
return slide.id()
return slideLabel
def __processEnvironmentLabels(self, data, flags):
"""
Replaces all environment labels with corresponding environment ids
"""
for (i, flag) in enumerate(flags):
if flag & self.kEnvironmentId and flag & self.kList:
environmentIds = []
for label in data[i]:
environmentIds.append(self.__getEnvironmentId(label))
data[i] = environmentIds
elif flag & self.kEnvironmentId:
data[i] = self.__getEnvironmentId(data[i])
return data
def __getEnvironmentId(self, environmentLabel):
for environment in self.__myEnvironments:
if environment.name() == environmentLabel:
return environment.id()
theLibrary = EnvironmentLibrary.instance()
libraryList = theLibrary.getAllEnvironmentLibraries()
for library in libraryList:
for id in sorted(theLibrary.getEnvironmentIds(library)):
if (theLibrary.getEnvironmentName(id) == environmentLabel):
return id
return environmentLabel
def __processShotLabels(self, data, flags):
"""
Replaces all slide labels with corresponding slide ids
"""
for (i, flag) in enumerate(flags):
if flag & self.kShotId and flag & self.kList:
shotIds = []
for label in data[i]:
shotIds.append(self.__getShotId(label))
data[i] = shotIds
elif flag & self.kShotId:
data[i] = self.__getShotId(data[i])
return data
def __getShotId(self, shotLabel):
for shot in self.__myShots.shots:
if shot.getLabel() == shotLabel:
return shot.getId().getFullId()
return shotLabel
def __processBehaviorLabels(self, data, flags):
"""
Replaces all behavior labels with corresponding behavior ids
"""
for (i, flag) in enumerate(flags):
if flag & self.kBehaviorId and flag & self.kList:
behaviorIds = []
for label in data[i]:
behaviorIds.append(self.__getBehaviorId(label))
data[i] = behaviorIds
elif flag & self.kBehaviorId:
data[i] = self.__getBehaviorId(data[i])
return data
def __getBehaviorId(self, behaviorLabel):
for name, behavior in self.__myBehaviors.iteritems():
if behavior.getLabel() == behaviorLabel:
return name
return behaviorLabel
def __processNodeLabels(self, data, flags):
"""
Replaces all lists of node labels with a corresponding list of node ids.
"""
for (i, flag) in enumerate(flags):
if flag & self.kNodeId and flag & self.kList:
data[i] = self.__getNodeIds(data[i])
elif flag & self.kNodeId:
nodeIds = self.__getNodeIds([ data[i] ])
if len(nodeIds > 0):
data[i] = nodeId[0]
return data
def __getNodeIds(self, nodeLabels):
"""
Given a list of node labels returns a list of all node ids that have
one of the specified labels.
"""
nodeIds = CollectNodes(
self.__myModels.root,
conditions=[lambda node: node.getLabel() in nodeLabels],
idsOnly=True
)
return nodeIds
def __processAlternativeLabels(self, data, flags):
"""
Replaces one pair of alternative set label & alternative label
with the corresponding alternative set name & alternative name.
- Assumes there is only one pair of alternative set & alternative specified.
- Works if only the alternative set label exists
"""
if self.kAlternativeSetId not in flags:
return data
setLabelIndex = -1
altLabelIndex = -1
for (i, flag) in enumerate(flags):
if flag == self.kAlternativeSetId:
setLabelIndex = i
break
setLabel = data[setLabelIndex]
altLabel = None
if self.kAlternativeId in flags:
for (i, flag) in enumerate(flags):
if flag == self.kAlternativeId:
altLabelIndex = i
break
altLabel = data[altLabelIndex]
(setName, altName) = self.__getAlternativeNames(setLabel, altLabel)
data[setLabelIndex] = setName
data[altLabelIndex] = altName
else:
(setName, altName) = self.__getAlternativeNames(setLabel, None)
data[setLabelIndex] = setName
return data
def __getAlternativeNames(self, setLabel, altLabel):
"""
Given an alernative set label and an alternative label, returns
a tuple containing the corresponding alternative set name and
alternative name.
Returns None for either name if the specified label is invalid.
"""
altName = setName = None
for alternativeSet in self.__myAlternatives:
if alternativeSet.label == setLabel:
setName = alternativeSet.name
break
if setName is None:
return (None, None)
if altLabel is None:
return (setName, None)
for alternative in alternativeSet:
if alternative.label == altLabel:
altName = alternative.name
break
return (setName, altName)
def CONVERT_LABELS(self, message):
(msgId, data, flags) = message.data
intflags = []
for flag in flags:
try:
intflags.append( int(flag) )
except:
intflags.append(0)
self.__processLabelMessage(msgId, data, intflags)
def APPLICATION_CLOSE_SCENE(self, message):
self.__myAlternatives = None
self.__myModels = None
self.__myStoryboard = None
self.__myBehaviors = None
self.__myEnvironments = None
self.__myShots = None
def SET_DOCUMENT(self, message):
(self.__myDocument, ) = message.data
self.__myAlternatives = self.__myDocument.get(AlternativeIO.id)
self.__myModels = self.__myDocument.get(ModelIO.id)
self.__myStoryBoard = self.__myDocument.get(StoryboardIO.id)
self.__myBehaviors = self.__myDocument.get(BehaviorIO.id)
self.__myEnvironments = self.__myDocument.get(EnvironmentIO.id)
self.__myShots = self.__myDocument.get(ShotIO.id)
CONVERT_LABELS_Doc = \
[(
"""
Calls the specified message after processing the specified fields as labels
and converting them into ids.
Valid data flags are:
1 - Alternative Set
2 - Alternative
4 - Node
8 - Slide
16 - Shot
32 - Behavior
64 - Environment
65536 - List
For example to call SHOT_PLAY using shot labels your parameters would be as follows:
('SHOT_PLAY', ( ('Front Wheels','Back Wheels'), False ), (65552, 0) )
Adding 16 + 65536 = 65552 implies that the parameter is a list of shots lables to be converted to ids.
"""
),
[[("messageId"),("The message to call")],
[("messageData"),("A tuple containing the data for the message.")],
[("dataFlags"),("A tuple of flags identifying which messageData fields are labels, and how to convert them.")],
],
]
theMessageRegistry.register(
"CONVERT_LABELS"
, (unicode, tuple, tuple)
, Message.kUndoable
, CONVERT_LABELS_Doc)
def info():
customInfo = CustomInfo()
customInfo.vendor = 'Autodesk'
customInfo.version = '2.0'
customInfo.api = '2013'
customInfo.shortInfo = "Send messages by label intead of ID."
customInfo.longInfo = \
"""Register a bunch of helper messages for users to send messages by label instead of ID. Mostly \
used for sending messages from HTML pages.
"""
return customInfo