MAYA MEL wait for idle events/evalDeferred to finish

A trick to pause the script until evalDeferred / scriptJob / idle are executed.

I discovered this trick because sometimes maya UI elements won't refresh even using the MEL command refresh -force. Sometimes creating or modifying or loading UI elements in Maya will spawn idle events that only get executed when your own script finishes:

// The 2 lines below will display the 
// "Tool settings" pannel in Maya interface:
toolPropertyWindow -inMainWindow true;
updateMainWindowComponentState();
// Unfortunately the User interface will be updated only when 
// idle events are processed and so this line:
button -visible false "someButtonInTheToolSettingsWindow"
// won't work because the button was not created yet
// Ui gets only updated when we leave the script...

Fortunately in python you can wait for idle commands to be processed with maya.utils.processIdleEvents() (see Maya documentation). There is no Mel version to my knowledge but you can call python commands in MEL with: python("maya.utils.processIdleEvents()") So here the final solution to wait for idle UI events to be executed:

global proc mel_process_idle_events()
{
    // '$list' contains all the current events/commands in the idle queue
    string $list[] = `evalDeferred -list`;
    // The timer is here for safety 
    // you could use a counter or the mel command 'progressWindow'
    timer -startTimer; 
    while( (size($list) > 0) )
    {
        // Idle events might spawn more idle events so we need to
        // loop until the list is empty
        python("maya.utils.processIdleEvents()");
        $list = `evalDeferred -list`;
        // Stop processing idle events after 15 seconds
        if( `timer -lap` > 15)
            break;
    }
    timer -endTimer;
}

// display the "Tool settings" pannel:
toolPropertyWindow -inMainWindow true;
updateMainWindowComponentState();
// Wait for UI to refresh:
mel_process_idle_events();
// safely access UI elements:
button -visible false "someButtonInTheToolSettingsWindow"

Here come the bonus, I'm not sure how this can be useful but you can also spawn as many evalDeferred and wait for them to get executed:

print("Main 1\n");
evalDeferred("print(\"EVAL 1\\n\")");
print("Main 2\n");
evalDeferred("print(\"EVAL 2\\n\")");
print("Main 3\n");
evalDeferred("print(\"EVAL 3\\n\")");
print("Main 4\n");
mel_process_idle_events();
print("Main END\n");
/* 
OUTPUT:
   Main 1
   Main 2
   Main 3
   Main 4
   EVAL 1
   EVAL 2
   EVAL 3
   Main END
*/

Lastly you can wait for a specific MEL command to end using scriptJob:

print("START\n");
// Spawn a script job that run only once when maya is idle:
int $id = `scriptJob -runOnce true -ie ("print(\"I WILL RUN\\n\")")`;
int $acc = 0; // Counter for safety
print("After scriptJob\n");
while(`scriptJob -exists $id` && ($acc < 10)){    
    python("maya.utils.processIdleEvents()");    
    $acc += 1;
}
print("Stop waiting\n");
/* 
OUTPUT:
   START
   After scriptJob
   I WILL RUN
   Stop waiting
*/

Final note: this is not multi-threading and your script commands as well as evalDeferred or other idle commands will be executed sequentially in the same thread (as fare as my test goes). This means Maya will temporarily freeze while waiting (well we are not actually waiting but executing the idle event in our own script's thread). Therefore the user won't have any control during "wait time". For real multi-threading your best bet is probably to use python but be aware that maya UI and related Mel commands are not thread safe...

Use this trick as a ultimate way to refresh Maya after calling some obscure MEL procedures.

Related threads:

No comments

(optional field, I won't disclose or spam but it's necessary to notify you if I respond to your comment)
All html tags except <b> and <i> will be removed from your comment. You can make links by just typing the url or mail-address.
Anti-spam question: