Abort a cmds.file load before load completes via Open Api

Abort a cmds.file load before load completes via Open Api

mikey.n
Enthusiast Enthusiast
1,466 Views
4 Replies
Message 1 of 5

Abort a cmds.file load before load completes via Open Api

mikey.n
Enthusiast
Enthusiast

Hi all, got a question that I'm posting after not really getting to the bottom of it over on SO:
https://stackoverflow.com/questions/50104364/how-to-abort-the-loading-of-a-file-in-maya-started-via-...

 

I have code that runs a batch operation on files in Maya. The actual implementation isn't important, just know that it gets fed a list of filepaths, maya opens the files, then an operation is performed on each file in turn.

 

In cases where a reference isn't valid for whatever reason (like an invalid path) I want to abort loading of that whole scene and skip to the next in the batch list.

 

Looking at other questions on this site and elsewhere I've only been able to see users asking how to query references. I already have a reasonable function for doing so, it's what to do after that function returns an invalid reference path that stumps me.

 

Resolving invalid reference paths has historically been done manually via the popup, however in large batches constantly babysitting the maya instance isn't feasible. Nor is suppressing the popup window itself, as I believe that will still open the file, and run the batch operation on the scene while it's in an invalid state.

I have been through the maya cmds python module to try and not load references, however when using the loadReferenceDepth flag along with cmds.file I'm still getting a popup:

 

cmds.file(r'c:\path\to\file.ma', o=1, f=1, lrd='none')  #invalid ref popup during file cmd

The second approach was to look into the maya open api and register a callback on the before open event. The code below is functionally the same as how the batch is set up:

 

import maya.OpenMaya as OpenM
batchFileList = [r"c:\path\to\file.ma", r"c:\path\to\file2.ma"] def _test_raise_exception(arg0, arg1, arg2): #pretending that a file ref failed below. Ref path validation code would go here. print '\n'*5+'Test Logging'+'\n'*5 return False
cId = OpenM.MSceneMessage.addCheckFileCallback(OpenM.MSceneMessage.kBeforeOpenCheck, _test_raise_exception) for file_ in batchFileList: try:
rv
= cmds.file(file_, o=1) #do stuff to the maya scene here except: #something didn't validate on load, except and skip, logging the occurrence. print 'load cancelled' OpenM.MSceneMessage.removeCallback(cId)

 

However, even though the addCheckFileCallback indicates that if the callback function returns False the operation is aborted, the file is loaded anyway.

Likewise, replacing the return False for a raise RuntimeError doesn't let me catch the exception. Instead the cmds.file completes and only prints out a small message in the log saying "python callback failed". The python open api docs say that the bindings prefer exceptions over MStatus return codes though, s...

We have removed the MStatus class. Python exceptions must be used instead of MStatus.

Am I missing something here? There must be a way to do this. Specifically I'm looking to abort a cmds.file load, not a workaround to this particular use case.

 

Thanks for your time!

0 Likes
Accepted solutions (1)
1,467 Views
4 Replies
Replies (4)
Message 2 of 5

Anonymous
Not applicable
Accepted solution

The documentation you're referring to is for the Maya Python API 2, but the classes you're using are from the Maya Python API 1.

 

In API 1 most methods take exactly the same parameters as in C++. This means that a CheckFileCallback will be passed three parameters, the first of which is a reference to a C++ bool variable. To abort the file load you must set that variable to false, which requires using either ctypes or MScriptUtil.

 

In API 2 the CheckFileCallback returns True or False to indicate whether the file load should proceed. So in your example change 'import maya.OpenMaya' to 'import maya.api.OpenMaya' and remove one parameter from _test_raise_exception's parameter list and you should be good to go.

Message 3 of 5

mikey.n
Enthusiast
Enthusiast

Thanks for the insight! Funnily that bit about exceptions was pulled from the 1.0 api notes, but I couldn't find anything concrete on the 1.0 api docs themselves.

I'll have to retest the above as we transition to 2018, baring in mind the api differences and imports. I'll report back once I've had a chance to do that.

0 Likes
Message 4 of 5

Anonymous
Not applicable

I should have been more precise. The documentation you linked to for the addCheckFileCallback() is for the Maya Python API 2.0.

0 Likes
Message 5 of 5

mikey.n
Enthusiast
Enthusiast

 

So I've come back around to looking at this and like you say I was referring to API 2.
Switching over had the effect of properly aborting the file load, thanks for the hint!

 

However after testing, it seems you can't raise an exception in the callback. Well obviously you can, but the function then doesn't return the False it needs to abort the load. The exception was also caught internally it seemed. There was also a secondary problem where simply returning False wouldn't halt execution of the program, and didn't give enough feedback as to why the operation was aborted.

The solution I found was to pass in some clientdata to the callback in which we set an exception, then check for that when we're sure the callback would've been hit. In this case immediately after the load command. Essentially putting something analogous to the MStatus back into the API 🙂

It still needs proper testing in real-world scenarios, but it looks to do the job.

Below is a simple contrived example.

 

import maya.api.OpenMaya as OpenM
batchFileList = [r"c:\path\to\file.ma", r"c:\path\to\file2.ma"]

def _test_raise_exception(fileObject, clientData):
    #pretending that a file ref failed below. Ref path validation code would go here.
    clientData['exception'] = RuntimeError('bad ref {}'.format(fileObject.expandedFullName()))
return False for file_ in batchFileList: try:
outData = {}
cId = OpenM.MSceneMessage.addCheckFileCallback(OpenM.MSceneMessage.kBeforeCreateReferenceCheck, _test_raise_exception, clientData=outData) rv = cmds.file(file_, o=1)
OpenM.MSceneMessage.removeCallback(cId) if 'exception' in outData: #check if an exception was set, if so, raise it
raise outData['exception'] except: #handle the exception here print 'load cancelled'