Updated
This commit is contained in:
971
Scripts/Animation/aTools/animTools/animationCrashRecovery.py
Normal file
971
Scripts/Animation/aTools/animTools/animationCrashRecovery.py
Normal file
@ -0,0 +1,971 @@
|
||||
'''
|
||||
========================================================================================================================
|
||||
Author: Alan Camilo
|
||||
www.alancamilo.com
|
||||
Modified: Michael Klimenko
|
||||
|
||||
Requirements: aTools Package
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
To install aTools, please follow the instructions in the file how_to_install.txt
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
To unistall aTools, go to menu (the last button on the right), Uninstall
|
||||
|
||||
========================================================================================================================
|
||||
'''
|
||||
|
||||
from maya import cmds
|
||||
from maya import OpenMaya
|
||||
from maya import OpenMayaAnim
|
||||
from aTools.generalTools.aToolsGlobals import aToolsGlobals as G
|
||||
from aTools.commonMods import uiMod
|
||||
from aTools.commonMods import utilMod
|
||||
from aTools.commonMods import animMod
|
||||
from aTools.commonMods import aToolsMod
|
||||
|
||||
import os
|
||||
import time
|
||||
import datetime
|
||||
import math
|
||||
|
||||
class AnimationCrashRecovery(object):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
|
||||
G.animationCrashRecovery = self
|
||||
|
||||
self.deferredQueue = []
|
||||
self.animCurvesNames = []
|
||||
self.animCurvesInfo = {}
|
||||
self.nonKeyedAttrInfo = {}
|
||||
self.baseFolderName = "animationCrashRecovery"
|
||||
self.baseLatestFolderName = "latest"
|
||||
self.baseBackupFolderName = "backup"
|
||||
self.infoDataFileName = "infoData"
|
||||
self.selectedObjs = []
|
||||
self.ignoreAttrs = ["visibility"]
|
||||
self.curveExt = "curve"
|
||||
self.attrExt = "attr"
|
||||
self.curvesInFile = []
|
||||
self.nonKeyedAttrsInFile = []
|
||||
self.mayaFileName = None
|
||||
self.pause = False
|
||||
self.mayaFileName = utilMod.getMayaFileName()
|
||||
self.mayaFilePath = utilMod.getMayaFileName("path")
|
||||
G.ACR_messages = G.ACR_messages or {"anim":[], "node":[], "scene":[], "mdg":[]}
|
||||
self.blinkingLedState = False
|
||||
self.saveRecommended = True
|
||||
self.checkNodeCreated = True
|
||||
G.lastSaveWarning = G.lastSaveWarning or None
|
||||
self.redBlinkingSecs = 300#300 = 5 minutes
|
||||
self.daysToKeepOldFiles = 5*86400#5days
|
||||
self.nodesCreated = []
|
||||
#self.daysToKeepOldFiles = 10#TMP
|
||||
|
||||
self.checkForCrashLog()
|
||||
self.checkAndClearOldFiles()
|
||||
|
||||
#G.deferredManager.removeFromQueue("ACR")#TMP
|
||||
|
||||
def switch(self, onOff):
|
||||
|
||||
|
||||
self.removeMessages()
|
||||
utilMod.killScriptJobs("G.animationCrashRecoveryScriptJobs")
|
||||
|
||||
if onOff:
|
||||
|
||||
#self.saveAllAnimationData(update=True)
|
||||
self.addAnimSceneMessages()
|
||||
self.addNodeMessages()
|
||||
self.addMdgMessages()
|
||||
G.animationCrashRecoveryScriptJobs.append(cmds.scriptJob(runOnce = False, killWithScene = False, event =('SelectionChanged', self.addNodeMessages )))
|
||||
|
||||
|
||||
self.recommendSaving(True)
|
||||
#self.recommendSaving(False)#TMP
|
||||
|
||||
else:
|
||||
G.deferredManager.removeFromQueue("ACR")
|
||||
self.setLed("off")
|
||||
|
||||
def setLed(self, state):
|
||||
|
||||
if not cmds.image("animationCrashRecoveryLed", query=True, exists=True): return
|
||||
|
||||
self.blinkingRed(False)
|
||||
|
||||
if state == "on":
|
||||
if self.saveRecommended:
|
||||
image = "ACR_red"
|
||||
ann = "Animation Crash Recovery recommends you to save"
|
||||
G.lastSaveWarning = time.time() if not G.lastSaveWarning else G.lastSaveWarning
|
||||
|
||||
if time.time() - G.lastSaveWarning >= self.redBlinkingSecs: self.blinkingRed(True)
|
||||
|
||||
else:
|
||||
image = "ACR_green"
|
||||
ann = "Animation Crash Recovery is ON"
|
||||
G.lastSaveWarning = None
|
||||
|
||||
cmds.image("animationCrashRecoveryLed", edit=True, image= uiMod.getImagePath(image), ann=ann)
|
||||
|
||||
elif state == "off":
|
||||
cmds.image("animationCrashRecoveryLed", edit=True, image= uiMod.getImagePath("ACR_off"), ann="Animation Crash Recovery is OFF")
|
||||
|
||||
elif state == "blinking":
|
||||
self.blinkingLedState = not self.blinkingLedState
|
||||
image = "ACR_white_half" if self.blinkingLedState else "ACR_white_bright"
|
||||
cmds.image("animationCrashRecoveryLed", edit=True, image= uiMod.getImagePath(image), ann="Animation Crash Recovery is saving animation")
|
||||
|
||||
elif state == "blinking_red":
|
||||
self.blinkingLedState = not self.blinkingLedState
|
||||
image = "ACR_red_half" if self.blinkingLedState else "ACR_red_bright"
|
||||
cmds.image("animationCrashRecoveryLed", edit=True, image= uiMod.getImagePath(image), ann="Animation Crash Recovery HIGHLY recommends you to save")
|
||||
|
||||
|
||||
|
||||
def blinkingRed(self, onOff):
|
||||
|
||||
if onOff: G.aToolsBar.timeoutInterval.setInterval(self.toggleRed, .3, id="ACR_red_blinking")
|
||||
else: G.aToolsBar.timeoutInterval.stopInterval("ACR_red_blinking")
|
||||
|
||||
|
||||
def toggleRed(self):
|
||||
self.setLed("blinking_red")
|
||||
|
||||
|
||||
|
||||
def optionBoxWindow(self, *args):
|
||||
|
||||
sceneId = aToolsMod.getSceneId()
|
||||
idFolder = "%s%s%s"%(self.baseFolderName, os.sep, sceneId)
|
||||
bkpFolder = "%s%s%s"%(idFolder, os.sep, self.baseBackupFolderName)
|
||||
infoData = aToolsMod.loadFileWithUser(bkpFolder, self.infoDataFileName, ext="info")
|
||||
infoDataFile = "%s%s%s%s%s.info"%(G.USER_FOLDER, os.sep, bkpFolder, os.sep, self.infoDataFileName)
|
||||
modDate = os.path.getmtime(infoDataFile) if os.path.isfile(infoDataFile) else None
|
||||
|
||||
|
||||
if not infoData or not modDate:
|
||||
cmds.warning("There is no crash file to restore.")
|
||||
return
|
||||
|
||||
|
||||
def loadWindow():
|
||||
|
||||
mayaFileName = infoData["mayaFileName"]
|
||||
message = "%s\n%s\n\nWarning: Loading crash files after editing your Maya file can lead to unpredictable results."%(mayaFileName, time.ctime(modDate))
|
||||
|
||||
formLayout = cmds.setParent(query=True)
|
||||
icon = cmds.image(image= uiMod.getImagePath("ACR_white_bright"))
|
||||
titleText = cmds.text(label="Do you want to load?", font="boldLabelFont", align="left")
|
||||
messageText = cmds.text(label=message, align="left")
|
||||
buttonLoad = cmds.button(label='Load', command='cmds.layoutDialog(dismiss="load")')
|
||||
buttonLoadSel = cmds.button(label='Load On Selection', command='cmds.layoutDialog(dismiss="load_selection")', w=110)
|
||||
|
||||
cmds.formLayout (formLayout, edit=True, width=300, height=170,
|
||||
attachPosition = [
|
||||
(icon, 'left', 10, 0),
|
||||
(icon, 'top', 10, 0),
|
||||
(titleText, 'top', 10, 0),
|
||||
(messageText, 'left', 10, 0),
|
||||
(messageText, 'top', 0, 30),
|
||||
(buttonLoad, 'left', 10, 0),
|
||||
(buttonLoad, 'bottom', 10, 100),
|
||||
(buttonLoadSel, 'bottom', 10, 100)
|
||||
],
|
||||
attachForm = [
|
||||
(buttonLoad, 'left', 10),
|
||||
(buttonLoadSel, 'right', 10)
|
||||
],
|
||||
attachControl = [
|
||||
(titleText, 'left', 10, icon),
|
||||
(buttonLoad, 'right', 5, buttonLoadSel)
|
||||
])
|
||||
|
||||
|
||||
def window(dismiss):
|
||||
|
||||
if dismiss == "dismiss": return
|
||||
|
||||
onlySelectedNodes = True if dismiss == "load_selection" else False
|
||||
|
||||
self.loadData(onlySelectedNodes, self.baseBackupFolderName)
|
||||
|
||||
|
||||
window(cmds.layoutDialog(title="aTools Animation Crash Recovery", ui=loadWindow))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def checkForAnimationSaved(self, clearDeferredQueue=False, *args):
|
||||
if clearDeferredQueue: self.deferredQueue = []
|
||||
|
||||
sceneId = aToolsMod.getSceneId()
|
||||
idFolder = "%s%s%s"%(self.baseFolderName, os.sep, sceneId)
|
||||
latestFolder = "%s%s%s"%(idFolder, os.sep, self.baseLatestFolderName)
|
||||
infoFile = "%s%s%s%s%s.info"%(G.USER_FOLDER, os.sep, latestFolder, os.sep, self.infoDataFileName)
|
||||
mayaFile = cmds.file(query=True, sceneName=True)
|
||||
|
||||
if not os.path.isfile(infoFile) or not os.path.isfile(mayaFile): return
|
||||
|
||||
mayaFileModTime = os.path.getmtime(mayaFile)
|
||||
infoFileModTime = os.path.getmtime(infoFile)
|
||||
|
||||
if mayaFileModTime < infoFileModTime:
|
||||
|
||||
infoData = aToolsMod.loadFileWithUser(latestFolder, self.infoDataFileName, ext="info")
|
||||
|
||||
if not infoData: return
|
||||
|
||||
height = 170
|
||||
completed = infoData["completed"]
|
||||
mayaFileModTimeStr = time.ctime(mayaFileModTime)
|
||||
infoFileModTimeStr = time.ctime(infoFileModTime)
|
||||
message = "This Maya file:\n%s\n\n"%(mayaFileModTimeStr)
|
||||
message += "Latest Animation Crash Recovery file:\n%s"%(infoFileModTimeStr)
|
||||
|
||||
if not completed:
|
||||
message += "\n\n*Some animation may not be loaded.\nAnimation Crash Recovery did not finish saving before Maya crashed."
|
||||
height += 40
|
||||
|
||||
self.warningForLoading(message, height)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def warningForLoading(self, message, height):
|
||||
|
||||
def warningWindow():
|
||||
|
||||
formLayout = cmds.setParent(query=True)
|
||||
icon = cmds.image(image= uiMod.getImagePath("ACR_white_bright"))
|
||||
titleText = cmds.text(label="You have newer animation. Do you want to load?", font="boldLabelFont", align="left")
|
||||
messageText = cmds.text(label=message, align="left")
|
||||
buttonLoad = cmds.button(label='Load', command='cmds.layoutDialog(dismiss="load")')
|
||||
buttonMaybe = cmds.button(label='Maybe Later', command='cmds.layoutDialog(dismiss="maybe")', w=100)
|
||||
|
||||
|
||||
cmds.formLayout (formLayout, edit=True, width=300, height=height,
|
||||
attachPosition = [
|
||||
(icon, 'left', 10, 0),
|
||||
(icon, 'top', 10, 0),
|
||||
(titleText, 'top', 10, 0),
|
||||
(messageText, 'left', 10, 0),
|
||||
(messageText, 'top', 0, 30),
|
||||
(buttonLoad, 'left', 10, 0),
|
||||
(buttonLoad, 'bottom', 10, 100),
|
||||
(buttonMaybe, 'bottom', 10, 100)
|
||||
],
|
||||
attachForm = [
|
||||
(buttonLoad, 'left', 10),
|
||||
(buttonMaybe, 'right', 10)
|
||||
],
|
||||
attachControl = [
|
||||
(titleText, 'left', 10, icon),
|
||||
(buttonLoad, 'right', 5, buttonMaybe)
|
||||
])
|
||||
|
||||
|
||||
|
||||
def window(dismiss):
|
||||
if dismiss == "load":
|
||||
self.loadData()
|
||||
else:
|
||||
cmds.warning("If you want to load later, go to aTools menu/Animation Crash Recovery option box")
|
||||
|
||||
self.saveBackup()
|
||||
|
||||
window(cmds.layoutDialog(title="aTools Animation Crash Recovery", ui=warningWindow))
|
||||
|
||||
|
||||
def saveBackup(self):
|
||||
|
||||
sceneId = aToolsMod.getSceneId()
|
||||
idFolder = "%s%s%s"%(self.baseFolderName, os.sep, sceneId)
|
||||
latestFolder = "%s%s%s"%(idFolder, os.sep, self.baseLatestFolderName)
|
||||
bkpFolder = "%s%s%s"%(idFolder, os.sep, self.baseBackupFolderName)
|
||||
|
||||
print("henlo")
|
||||
print(f"backup: {bkpFolder}")
|
||||
aToolsMod.deleteFolderWithUser(bkpFolder)
|
||||
aToolsMod.renameFolderWithUser(latestFolder, bkpFolder)
|
||||
aToolsMod.deleteFolderWithUser(latestFolder)
|
||||
|
||||
|
||||
def getSavedData(self, crashFolder=None, onlySelectedNodes=False, *args):
|
||||
|
||||
idFolder = aToolsMod.getSceneId()
|
||||
crashFolder = self.baseLatestFolderName if not crashFolder else crashFolder
|
||||
folder = "%s%s%s%s%s"%(self.baseFolderName, os.sep, idFolder, os.sep, crashFolder)
|
||||
filePath = cmds.file(query=True, sceneName=True)
|
||||
fileModTime = None
|
||||
|
||||
|
||||
if os.path.isfile(filePath):
|
||||
fileModTime = os.path.getmtime(filePath)
|
||||
|
||||
curveFiles = aToolsMod.readFilesWithUser(folder, ext=self.curveExt)
|
||||
attrFiles = aToolsMod.readFilesWithUser(folder, ext=self.attrExt)
|
||||
|
||||
status = "aTools Animation Crash Recovery - Step 1/3 - Loading crash files..."
|
||||
utilMod.startProgressBar(status)
|
||||
totalSteps = len(curveFiles + attrFiles)
|
||||
firstStep = 0
|
||||
thisStep = 0
|
||||
estimatedTime = None
|
||||
startChrono = None
|
||||
progressInfo = [startChrono, firstStep, thisStep, totalSteps, estimatedTime, status]
|
||||
|
||||
data = self.getDataFromFiles("anim", folder, curveFiles, fileModTime, self.curveExt, progressInfo, onlySelectedNodes)
|
||||
|
||||
if not data: return
|
||||
|
||||
animData = data[0]
|
||||
progressInfo = data[1]
|
||||
data = self.getDataFromFiles("attr", folder, attrFiles, fileModTime, self.attrExt, progressInfo, onlySelectedNodes)
|
||||
attrData = data[0]
|
||||
|
||||
if not data: return
|
||||
|
||||
utilMod.setProgressBar(endProgress=True)
|
||||
|
||||
return {"fileModTime":fileModTime, "animData":animData, "attrData":attrData}
|
||||
|
||||
|
||||
|
||||
def getDataFromFiles(self, animAttr, folder, infoFiles, fileModTime, ext, progressInfo, onlySelectedNodes):
|
||||
currSel = animMod.getObjsSel()
|
||||
data = {"data":[], "modTime":None}
|
||||
infoFileModTimeList = []
|
||||
startChrono, firstStep, thisStep, totalSteps, estimatedTime, status = progressInfo
|
||||
initialStep = thisStep
|
||||
|
||||
for n, loopFile in enumerate(infoFiles):
|
||||
if cmds.progressBar(G.progBar, query=True, isCancelled=True ):
|
||||
utilMod.setProgressBar(endProgress=True)
|
||||
return
|
||||
|
||||
thisStep = n+initialStep
|
||||
startChrono = utilMod.chronoStart(startChrono, firstStep, thisStep, totalSteps, estimatedTime, status)
|
||||
|
||||
infoFileStr = loopFile.replace(":", "_aTools_")[0:-(len(ext)+1)]
|
||||
infoFilePath = aToolsMod.getSaveFilePath("%s%s%s"%(folder, os.sep, infoFileStr), ext=ext)
|
||||
infoFileModTimeList.append(os.path.getmtime(infoFilePath))
|
||||
|
||||
if infoFileModTimeList[-1] > fileModTime: #load only what is newer
|
||||
object = loopFile.replace("_aTools_", ":")[0:-(len(ext)+1)]
|
||||
value = aToolsMod.loadFileWithUser(folder, infoFileStr, ext=ext)
|
||||
|
||||
if onlySelectedNodes:
|
||||
if animAttr == "anim":
|
||||
obj = value["objects"][0]
|
||||
else:
|
||||
obj = object.split(".")[0]
|
||||
|
||||
if obj not in currSel: continue
|
||||
|
||||
|
||||
data["data"].append({"_modTime":infoFileModTimeList[-1],"object":object, "value":value})
|
||||
|
||||
estimatedTime = utilMod.chronoEnd(startChrono, firstStep, thisStep, totalSteps)
|
||||
|
||||
#file mod date
|
||||
data["data"].sort() #sort newer files last
|
||||
if len(infoFileModTimeList) > 0:
|
||||
data["modTime"] = max(infoFileModTimeList)
|
||||
|
||||
progressInfo = [startChrono, firstStep, thisStep, totalSteps, estimatedTime, status]
|
||||
#blend animation data=================
|
||||
|
||||
return [data, progressInfo]
|
||||
|
||||
def loadData(self, onlySelectedNodes=False, crashFolder=None, *args):
|
||||
|
||||
cmds.waitCursor(state=True)
|
||||
cmds.refresh(suspend=True)
|
||||
cmds.undoInfo(openChunk=True)
|
||||
utilMod.startProgressBar("aTools Animation Crash Recovery - Loading data...")
|
||||
|
||||
self.pause = True
|
||||
savedData = self.getSavedData(crashFolder, onlySelectedNodes)
|
||||
|
||||
if savedData:
|
||||
|
||||
animData = savedData["animData"]["data"]
|
||||
attrData = savedData["attrData"]["data"]
|
||||
|
||||
self.applyAttrData(attrData)
|
||||
self.applyAnimData(animData)
|
||||
if not crashFolder: self.loadInfoData()
|
||||
|
||||
utilMod.setProgressBar(endProgress=True)
|
||||
|
||||
self.pause = False
|
||||
|
||||
cmds.undoInfo(closeChunk=True)
|
||||
cmds.refresh(suspend=False)
|
||||
cmds.waitCursor(state=False)
|
||||
|
||||
def blendAnimData(self, acrAnimData):
|
||||
|
||||
blendedAnimData = {"objects":[], "animData":[]}
|
||||
|
||||
for loopData in acrAnimData:
|
||||
data = loopData["value"]
|
||||
objects = data["objects"]
|
||||
animData = data["animData"]
|
||||
|
||||
blendedAnimData["objects"].extend(objects)
|
||||
blendedAnimData["animData"].extend(animData)
|
||||
|
||||
return blendedAnimData
|
||||
|
||||
def applyAnimData(self, animData):
|
||||
|
||||
if len(animData) == 0 : return
|
||||
animData = self.blendAnimData(animData)
|
||||
animMod.applyAnimData(animData, pasteInPlace=False, showProgress=True, status="aTools Animation Crash Recovery - Step 3/3 - Applying animation data...")
|
||||
|
||||
|
||||
|
||||
def applyAttrData(self, attrData):
|
||||
|
||||
firstStep = 0
|
||||
totalSteps = len(attrData)
|
||||
estimatedTime = None
|
||||
status = "aTools Animation Crash Recovery - Step 2/3 - Applying attributes data..."
|
||||
startChrono = None
|
||||
|
||||
for thisStep, loopData in enumerate(attrData):
|
||||
if cmds.progressBar(G.progBar, query=True, isCancelled=True ): return
|
||||
startChrono = utilMod.chronoStart(startChrono, firstStep, thisStep, totalSteps, estimatedTime, status)
|
||||
|
||||
objAttr = loopData["object"]
|
||||
value = loopData["value"]["value"]
|
||||
|
||||
if not cmds.objExists(objAttr): continue
|
||||
if not cmds.getAttr(objAttr, settable=True): continue
|
||||
if cmds.getAttr(objAttr, lock=True): continue
|
||||
if cmds.getAttr(objAttr, type=True) == "string": continue
|
||||
|
||||
cmds.cutKey(objAttr)
|
||||
|
||||
|
||||
if type(value) is list: #translate, rotate, scale
|
||||
value = value[0]
|
||||
cmds.setAttr(objAttr, value[0],value[1],value[2], clamp=True)
|
||||
else: #translatex, translatey, etc
|
||||
cmds.setAttr(objAttr, value, clamp=True)
|
||||
|
||||
|
||||
estimatedTime = utilMod.chronoEnd(startChrono, firstStep, thisStep, totalSteps)
|
||||
|
||||
|
||||
def getAllAnimCurves(self):
|
||||
return cmds.ls(type=["animCurveTA","animCurveTL","animCurveTT","animCurveTU"])
|
||||
|
||||
def getAllNonKeyedAttrs(self):
|
||||
return self.getNonKeyedAttrs(cmds.ls(transforms=True, visible=True))
|
||||
#return self.getNonKeyedAttrs([loopObj for loopObj in cmds.ls() if "transform" in cmds.nodeType(loopObj, inherited=True)])
|
||||
|
||||
|
||||
def saveSelectedCurve(self, *args):
|
||||
|
||||
if self.pause: return
|
||||
|
||||
curveMsg = args[0]
|
||||
curves = [OpenMaya.MFnDependencyNode(curveMsg[n]).name() for n in range(curveMsg.length())]
|
||||
|
||||
|
||||
function = lambda *args:self.sendDataToSaveDeferred(curves, [])
|
||||
G.deferredManager.sendToQueue(function, 50, "ACR")
|
||||
|
||||
|
||||
|
||||
|
||||
def saveSelectedAttr(self, msg, mplug, otherMplug, clientData):
|
||||
|
||||
if self.pause: return
|
||||
|
||||
if OpenMaya.MNodeMessage.kAttributeSet == (OpenMaya.MNodeMessage.kAttributeSet & msg):
|
||||
#nodeName, attrName = mplug.name().split('.')
|
||||
nonKeyedAttr = mplug.name()
|
||||
|
||||
#if cmds.keyframe(nonKeyedAttr, query=True): return
|
||||
|
||||
function = lambda *args:self.sendDataToSaveDeferred([], [nonKeyedAttr])
|
||||
G.deferredManager.sendToQueue(function, 50, "ACR")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def getNonKeyedAttrs(self, animObjects):
|
||||
objAttrs = animMod.getAllChannels(animObjects, changed=True, withAnimation=False)
|
||||
nonKeyedObjAttrs = []
|
||||
|
||||
|
||||
for n, loopObj in enumerate(animObjects):
|
||||
loopObjAttrs = objAttrs[n]
|
||||
if not loopObjAttrs: continue
|
||||
for loopAttr in loopObjAttrs:
|
||||
if loopAttr in self.ignoreAttrs: continue
|
||||
objAttr = "%s.%s"%(loopObj, loopAttr)
|
||||
if not cmds.objExists(objAttr): continue
|
||||
frameCount = cmds.keyframe(objAttr, query=True, keyframeCount=True)
|
||||
if frameCount <= 0:
|
||||
nonKeyedObjAttrs.append(objAttr)
|
||||
|
||||
return nonKeyedObjAttrs
|
||||
|
||||
def saveAllAnimationData(self, update=False, *args):#nao precisa???
|
||||
#print "saveAllAnimationData"
|
||||
if update:
|
||||
self.curvesInFile = self.getAllAnimCurves()
|
||||
self.nonKeyedAttrsInFile = self.getAllNonKeyedAttrs()
|
||||
|
||||
self.sendDataToSaveDeferred(self.curvesInFile, self.nonKeyedAttrsInFile)
|
||||
|
||||
def sendDataToSaveDeferred(self, curves, nonKeyedAttrs):
|
||||
|
||||
if not len(curves) > 0 and not len(nonKeyedAttrs) > 0:
|
||||
return
|
||||
|
||||
for loopCurve in curves:
|
||||
|
||||
curveStr = loopCurve.replace(":", "_aTools_")
|
||||
if not cmds.objExists(loopCurve):
|
||||
if curveStr in self.deferredQueue: self.deferredQueue.remove(curveStr)
|
||||
continue
|
||||
|
||||
if curveStr in self.deferredQueue: continue
|
||||
|
||||
self.deferredQueue.append(curveStr)
|
||||
function = lambda function=self.saveCurve, mayaFileName=self.mayaFileName, attrStr=curveStr, *args: self.sendToDeferredManager(function, mayaFileName, attrStr)
|
||||
G.deferredManager.sendToQueue(function, 50, "ACR")
|
||||
|
||||
|
||||
for loopNonKeyedAttr in nonKeyedAttrs:
|
||||
|
||||
nonKeyedAttrsStr = loopNonKeyedAttr.replace(":", "_aTools_")
|
||||
|
||||
if not cmds.objExists(loopNonKeyedAttr):
|
||||
if nonKeyedAttrsStr in self.deferredQueue: self.deferredQueue.remove(nonKeyedAttrsStr)
|
||||
continue
|
||||
|
||||
if cmds.keyframe(loopNonKeyedAttr, query=True): continue
|
||||
|
||||
if nonKeyedAttrsStr in self.deferredQueue: continue
|
||||
|
||||
self.deferredQueue.append(nonKeyedAttrsStr)
|
||||
function = lambda function=self.saveNonKeyedAttrs, mayaFileName=self.mayaFileName, attrStr=nonKeyedAttrsStr, *args: self.sendToDeferredManager(function, mayaFileName, attrStr)
|
||||
G.deferredManager.sendToQueue(function, 50, "ACR")
|
||||
|
||||
|
||||
G.deferredManager.sendToQueue(lambda *args:self.stopBlinking(self.mayaFileName), 50, "ACR")
|
||||
|
||||
def stopBlinking(self, mayaFileName):
|
||||
if G.deferredManager.inQueue("ACR") <= 1:
|
||||
self.setLed("on")
|
||||
self.saveInfoData(mayaFileName, completed=True)
|
||||
self.checkDeletedNodesCreated()
|
||||
|
||||
|
||||
def checkDeletedNodesCreated(self):
|
||||
|
||||
if len(G.ACR_messages["mdg"]) == 0: return
|
||||
|
||||
toRemove = []
|
||||
|
||||
for loopNode in self.nodesCreated:
|
||||
if not cmds.objExists(loopNode): toRemove.append(loopNode)
|
||||
|
||||
for loopNode in toRemove: self.nodesCreated.remove(loopNode)
|
||||
if len(self.nodesCreated) == 0: self.recommendSaving(False)
|
||||
|
||||
|
||||
def sendToDeferredManager(self, function, mayaFileName, attrStr):
|
||||
function(mayaFileName, attrStr)
|
||||
|
||||
def saveCurve(self, mayaFileName, curveStr):
|
||||
self.setLed("blinking")
|
||||
|
||||
sceneId = aToolsMod.getSceneId()
|
||||
curve = curveStr.replace("_aTools_", ":")
|
||||
animData = animMod.getAnimData([curve])
|
||||
|
||||
if curveStr in self.deferredQueue: self.deferredQueue.remove(curveStr)
|
||||
|
||||
if animData is None: return
|
||||
|
||||
if sceneId not in self.animCurvesInfo: self.animCurvesInfo[sceneId] = {}
|
||||
|
||||
if curveStr in self.animCurvesInfo[sceneId]:
|
||||
if self.animCurvesInfo[sceneId][curveStr] == animData: return
|
||||
|
||||
self.animCurvesInfo[sceneId][curveStr] = animData
|
||||
|
||||
#save curve to disk
|
||||
aToolsMod.saveFileWithUser("%s%s%s%s%s"%(self.baseFolderName, os.sep, sceneId, os.sep, self.baseLatestFolderName), curveStr, animData, ext=self.curveExt)
|
||||
self.saveInfoData(mayaFileName)
|
||||
|
||||
|
||||
def saveInfoData(self, mayaFileName, completed=False):
|
||||
|
||||
sceneId = aToolsMod.getSceneId()
|
||||
currFrame = cmds.currentTime(query=True)
|
||||
currSel = cmds.ls(selection=True)
|
||||
infoData = {"mayaFileName":mayaFileName, "currFrame":currFrame, "currSel":currSel, "completed":completed}
|
||||
|
||||
aToolsMod.saveFileWithUser("%s%s%s%s%s"%(self.baseFolderName, os.sep, sceneId, os.sep, self.baseLatestFolderName), self.infoDataFileName, infoData, ext="info")
|
||||
|
||||
def loadInfoData(self):
|
||||
sceneId = aToolsMod.getSceneId()
|
||||
infoData = aToolsMod.loadFileWithUser("%s%s%s%s%s"%(self.baseFolderName, os.sep, sceneId, os.sep, self.baseLatestFolderName), self.infoDataFileName, ext="info")
|
||||
|
||||
if not infoData: return
|
||||
|
||||
currFrame = infoData["currFrame"]
|
||||
currSel = infoData["currSel"]
|
||||
|
||||
if currFrame: cmds.currentTime(currFrame)
|
||||
if len(currSel) > 0: cmds.select(currSel, replace=True)
|
||||
|
||||
def saveNonKeyedAttrs(self, mayaFileName, nonKeyedAttrsStr):
|
||||
self.setLed("blinking")
|
||||
|
||||
sceneId = aToolsMod.getSceneId()
|
||||
nonKeyedAttr = nonKeyedAttrsStr.replace("_aTools_", ":")
|
||||
attrData = self.getNonKeyedAttrData(nonKeyedAttr)
|
||||
|
||||
if nonKeyedAttrsStr in self.deferredQueue: self.deferredQueue.remove(nonKeyedAttrsStr)
|
||||
|
||||
if attrData is None: return
|
||||
|
||||
if sceneId not in self.nonKeyedAttrInfo: self.nonKeyedAttrInfo[sceneId] = {}
|
||||
|
||||
if nonKeyedAttrsStr in self.nonKeyedAttrInfo[sceneId]:
|
||||
if self.nonKeyedAttrInfo[sceneId][nonKeyedAttrsStr] == attrData: return
|
||||
|
||||
self.nonKeyedAttrInfo[sceneId][nonKeyedAttrsStr] = attrData
|
||||
|
||||
#save curve to disk
|
||||
aToolsMod.saveFileWithUser("%s%s%s%s%s"%(self.baseFolderName, os.sep, sceneId, os.sep, self.baseLatestFolderName), nonKeyedAttrsStr, attrData, ext=self.attrExt)
|
||||
self.saveInfoData(mayaFileName)
|
||||
|
||||
|
||||
def checkAndClearOldFiles(self):
|
||||
|
||||
allIdFolders = aToolsMod.readFoldersWithUser(self.baseFolderName)
|
||||
timeNow = time.time()
|
||||
|
||||
for loopIdFolder in allIdFolders:
|
||||
idFolder = "%s%s%s"%(self.baseFolderName, os.sep, loopIdFolder)
|
||||
modDate = None
|
||||
|
||||
for loopInfoFile in [self.baseLatestFolderName, self.baseBackupFolderName]:
|
||||
infoDataFile = "%s%s%s%s%s%s%s.info"%(G.USER_FOLDER, os.sep, idFolder, os.sep, loopInfoFile, os.sep, self.infoDataFileName)
|
||||
if os.path.isfile(infoDataFile):
|
||||
modDate = os.path.getmtime(infoDataFile)
|
||||
break
|
||||
|
||||
if not modDate: return
|
||||
|
||||
if timeNow - modDate >= self.daysToKeepOldFiles:
|
||||
aToolsMod.deleteFolderWithUser(idFolder)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def clearLatestFolder(self):
|
||||
self.deferredQueue = []
|
||||
G.deferredManager.removeFromQueue("ACR")
|
||||
|
||||
sceneId = aToolsMod.getSceneId()
|
||||
idFolder = "%s%s%s"%(self.baseFolderName, os.sep, sceneId)
|
||||
latestFolder = "%s%s%s"%(idFolder, os.sep, self.baseLatestFolderName)
|
||||
|
||||
aToolsMod.deleteFolderWithUser(latestFolder)
|
||||
|
||||
|
||||
|
||||
def getNonKeyedAttrData(self, nonKeyedAttr):
|
||||
value = None
|
||||
|
||||
if cmds.objExists(nonKeyedAttr): value = cmds.getAttr(nonKeyedAttr)
|
||||
return {"value":value}
|
||||
|
||||
|
||||
def recommendSaving(self, trueFalse):
|
||||
self.saveRecommended = trueFalse
|
||||
|
||||
if not trueFalse: self.addMdgMessages()
|
||||
self.setLed("on")
|
||||
|
||||
def isCrashSaving(self):
|
||||
t = datetime.date.today()
|
||||
todaySt = ".%s%s%s."%(str(t.year).zfill(4),str(t.month).zfill(2),str(t.day).zfill(2))
|
||||
|
||||
return (todaySt in utilMod.getMayaFileName())
|
||||
|
||||
def beforeSave(self, *args):
|
||||
pass
|
||||
|
||||
def afterSave(self, *args):
|
||||
|
||||
if self.isCrashSaving():
|
||||
self.saveCrashLog(cmds.file(query=True, sceneName=True), self.mayaFilePath, self.mayaFileName)
|
||||
return
|
||||
|
||||
self.mayaFileName = utilMod.getMayaFileName()
|
||||
self.mayaFilePath = utilMod.getMayaFileName("path")
|
||||
|
||||
self.recommendSaving(False)
|
||||
self.addMdgMessages()
|
||||
self.clearLatestFolder()
|
||||
|
||||
def afterNew(self, *args):
|
||||
#print "afterOpen"
|
||||
self.mayaFileName = utilMod.getMayaFileName()
|
||||
self.mayaFilePath = utilMod.getMayaFileName("path")
|
||||
|
||||
self.recommendSaving(False)
|
||||
|
||||
def beforeOpen(self, *args):
|
||||
self.pause = True
|
||||
|
||||
def afterOpen(self, *args):
|
||||
self.pause = False
|
||||
#print "afterOpen"
|
||||
self.mayaFileName = utilMod.getMayaFileName()
|
||||
self.mayaFilePath = utilMod.getMayaFileName("path")
|
||||
|
||||
self.recommendSaving(False)
|
||||
|
||||
function = lambda *args: self.checkForAnimationSaved(clearDeferredQueue=True)
|
||||
G.deferredManager.sendToQueue(function, 80, "ACR")
|
||||
#self.checkForAnimationSaved(clearDeferredQueue=True)
|
||||
|
||||
def checkForCrashLog(self):
|
||||
|
||||
crashLog = self.loadCrashLog()
|
||||
|
||||
if crashLog and "crashFilePath" in crashLog: self.warnCrashLog(crashLog)
|
||||
|
||||
def saveCrashLog(self, crashFilePath, beforeCrashPath, beforeCrashName):
|
||||
|
||||
crashData = {"crashFilePath":crashFilePath, "beforeCrashPath":beforeCrashPath, "beforeCrashName":beforeCrashName}
|
||||
aToolsMod.saveFileWithUser("%s"%(self.baseFolderName), "crashLog", crashData, ext="info")
|
||||
|
||||
def loadCrashLog(self):
|
||||
|
||||
return aToolsMod.loadFileWithUser("%s"%(self.baseFolderName), "crashLog", ext="info")
|
||||
|
||||
def warnCrashLog(self, crashLog):
|
||||
|
||||
crashFilePath = crashLog["crashFilePath"]
|
||||
beforeCrashPath = crashLog["beforeCrashPath"]
|
||||
beforeCrashName = crashLog["beforeCrashName"]
|
||||
|
||||
if not os.path.isfile(crashFilePath) or not os.path.isfile(beforeCrashPath): return
|
||||
|
||||
crashFileModTime = time.ctime(os.path.getmtime(crashFilePath))
|
||||
beforeCrashModTime = time.ctime(os.path.getmtime(beforeCrashPath))
|
||||
message = "Looks like last Maya session crashed and saved a crash file.\n\nOriginal file:\n%s\n%s\n\nCrash saved file:\n%s\n%s\n\nDo you want to open the crash saved file?"%(beforeCrashName, beforeCrashModTime, crashFilePath, crashFileModTime)
|
||||
confirm = cmds.confirmDialog( title='aTools Animation Crash Recovery', message=message, button=['Yes','No'], defaultButton='Yes', cancelButton='No', dismissString='No' )
|
||||
|
||||
if confirm == 'Yes':
|
||||
if cmds.file(query=True, sceneName=True):
|
||||
message = "Save current file first? If you click NO, changes will be lost."
|
||||
confirm = cmds.confirmDialog( title='aTools Animation Crash Recovery', message=message, button=['Yes','No'], defaultButton='Yes', cancelButton='No', dismissString='No' )
|
||||
|
||||
if confirm == 'Yes': cmds.file(save=True)
|
||||
|
||||
cmds.file(new=True, force=True)
|
||||
cmds.file(crashFilePath, open=True, prompt=True)
|
||||
|
||||
aToolsMod.deleteFileWithUser("%s"%(self.baseFolderName), "crashLog", ext="info")
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
def sceneUpdate(self, *args):
|
||||
self.clearOldFiles()
|
||||
self.mayaFileName = utilMod.getMayaFileName()
|
||||
#print "sceneUpdate", args, self.mayaFileName
|
||||
|
||||
|
||||
def beforeSaveCheck(self, retCode, *args):
|
||||
self.clearOldFiles()
|
||||
OpenMaya.MScriptUtil.setBool(retCode, True)
|
||||
|
||||
print "beforeSaveCheck", args, self.mayaFileName
|
||||
"""
|
||||
|
||||
|
||||
|
||||
def afterNodeCreated(self, *args):
|
||||
|
||||
if not self.checkNodeCreated: return
|
||||
|
||||
nodeCreated = OpenMaya.MFnDependencyNode(args[0]).name()
|
||||
nodeType = cmds.nodeType(nodeCreated)
|
||||
|
||||
if nodeType in ["animCurveTA","animCurveTL","animCurveTT","animCurveTU"]:
|
||||
return
|
||||
|
||||
print(("nodeCreated", nodeCreated, nodeType))
|
||||
|
||||
if nodeCreated not in self.nodesCreated: self.nodesCreated.append(nodeCreated)
|
||||
|
||||
self.recommendSaving(True)
|
||||
#self.removeMdgMessages()
|
||||
|
||||
def afterNodeParent(self, *args):
|
||||
|
||||
if not self.checkNodeCreated: return
|
||||
|
||||
dag = args[0]
|
||||
firstObj = dag.partialPathName()
|
||||
|
||||
#print "firstObj",firstObj
|
||||
if not firstObj: return
|
||||
|
||||
dag = args[1]
|
||||
secondObj = dag.partialPathName()
|
||||
|
||||
#print "secondObj",secondObj
|
||||
if not firstObj: return
|
||||
|
||||
#if firstObj not in self.nodesCreated: self.nodesCreated.append(firstObj)
|
||||
|
||||
print(("parented", firstObj, secondObj))
|
||||
|
||||
self.recommendSaving(True)
|
||||
self.removeMdgMessages()
|
||||
|
||||
def addAnimSceneMessages(self):
|
||||
|
||||
self.removeMessages()
|
||||
|
||||
#ANIM MESSAGES
|
||||
G.ACR_messages["anim"].append(OpenMayaAnim.MAnimMessage.addAnimCurveEditedCallback(self.saveSelectedCurve))
|
||||
|
||||
#SCENE MESSAGES
|
||||
G.ACR_messages["scene"].append(OpenMaya.MSceneMessage.addCallback( OpenMaya.MSceneMessage.kBeforeSave, self.beforeSave))
|
||||
G.ACR_messages["scene"].append(OpenMaya.MSceneMessage.addCallback( OpenMaya.MSceneMessage.kAfterSave, self.afterSave))
|
||||
G.ACR_messages["scene"].append(OpenMaya.MSceneMessage.addCallback( OpenMaya.MSceneMessage.kBeforeOpen, self.beforeOpen))
|
||||
G.ACR_messages["scene"].append(OpenMaya.MSceneMessage.addCallback( OpenMaya.MSceneMessage.kAfterOpen, self.afterOpen))
|
||||
G.ACR_messages["scene"].append(OpenMaya.MSceneMessage.addCallback( OpenMaya.MSceneMessage.kAfterNew, self.afterNew))
|
||||
#G.ACR_messages["scene"].append(OpenMaya.MSceneMessage.addCallback( OpenMaya.MSceneMessage.kSceneUpdate, self.sceneUpdate))
|
||||
#G.ACR_messages["scene"].append(OpenMaya.MSceneMessage.addCheckCallback( OpenMaya.MSceneMessage.kBeforeSaveCheck, self.beforeSaveCheck))
|
||||
|
||||
|
||||
|
||||
def addMdgMessages(self):
|
||||
self.removeMdgMessages()
|
||||
#MDG MESSAGES
|
||||
G.ACR_messages["mdg"].append(OpenMaya.MDGMessage.addNodeAddedCallback(self.afterNodeCreated))
|
||||
G.ACR_messages["mdg"].append(OpenMaya.MDagMessage.addParentAddedCallback(self.afterNodeParent, "_noData_"))
|
||||
|
||||
|
||||
def addNodeMessages(self):
|
||||
self.removeNodeMessages()
|
||||
#NODE MESSAGES
|
||||
currSel = cmds.ls(selection=True)
|
||||
MSelectionList = OpenMaya.MSelectionList()
|
||||
OpenMaya.MGlobal.getActiveSelectionList(MSelectionList)
|
||||
node = OpenMaya.MObject()
|
||||
|
||||
for n, loopSel in enumerate(currSel):
|
||||
|
||||
MSelectionList.getDependNode(n, node)
|
||||
G.ACR_messages["node"].append(OpenMaya.MNodeMessage.addAttributeChangedCallback(node, self.saveSelectedAttr, None))
|
||||
|
||||
|
||||
|
||||
|
||||
def removeMessages(self):
|
||||
|
||||
try:
|
||||
for loopId in G.ACR_messages["anim"]:
|
||||
OpenMayaAnim.MAnimMessage.removeCallback(loopId)
|
||||
except: pass
|
||||
|
||||
self.removeNodeMessages()
|
||||
|
||||
try:
|
||||
for loopId in G.ACR_messages["scene"]:
|
||||
OpenMaya.MSceneMessage.removeCallback(loopId)
|
||||
except: pass
|
||||
|
||||
self.removeMdgMessages()
|
||||
|
||||
G.ACR_messages["anim"] = []
|
||||
G.ACR_messages["scene"] = []
|
||||
|
||||
def removeMdgMessages(self):
|
||||
|
||||
try:
|
||||
for loopId in G.ACR_messages["mdg"]:
|
||||
OpenMaya.MDGMessage.removeCallback(loopId)
|
||||
except: pass
|
||||
|
||||
G.ACR_messages["mdg"] = []
|
||||
|
||||
def removeNodeMessages(self):
|
||||
try:
|
||||
for loopId in G.ACR_messages["node"]:
|
||||
OpenMaya.MNodeMessage.removeCallback(loopId)
|
||||
except: pass
|
||||
|
||||
G.ACR_messages["node"] = []
|
||||
|
||||
|
||||
"""
|
||||
import maya.OpenMaya as om
|
||||
import maya.OpenMayaAnim as oma
|
||||
|
||||
def undoTest(*args):
|
||||
print 'Checking Undo callback'
|
||||
|
||||
|
||||
def undoRedoCallback(arg):
|
||||
global callbackIDs
|
||||
|
||||
Null = om.MObject()
|
||||
objs = cmds.ls(sl=1)
|
||||
|
||||
if arg == 'add':
|
||||
|
||||
undoID = oma.MAnimMessage.addAnimCurveEditedCallback(undoTest)
|
||||
#undoID = oma.MAnimMessage.addAnimKeyframeEditedCallback(undoTest)
|
||||
#undoID = oma.MAnimMessage.addNodeAnimKeyframeEditedCallback(undoTest)
|
||||
#undoID = oma.MAnimMessage.addAnimKeyframeEditCheckCallback(undoTest)
|
||||
#undoID = oma.MAnimMessage.addAnimKeyframeEditedCallback(undoTest)
|
||||
|
||||
|
||||
callbackIDs = [undoID]
|
||||
|
||||
elif arg == 'remove':
|
||||
try:
|
||||
for i in callbackIDs:
|
||||
oma.MAnimMessage.removeCallback(i)
|
||||
except:
|
||||
print 'There is no ID to delete'
|
||||
|
||||
undoRedoCallback("add")
|
||||
undoRedoCallback("remove")
|
||||
|
||||
MNodeMessage.addAttributeChangedCallback
|
||||
|
||||
"""
|
||||
|
Reference in New Issue
Block a user