Rerouting the Maya Script Editor to a terminal (and other places)

This is one of those topics that seems ridiculously simple when you know the answer but terribly complicated when you don’t. To be fair, you can’t do this one in MEL. At least not properly. There is a MEL command, scriptEditorInfo, that let’s you write the history to a file but it doesn’t allow any manipulation or redirection of the stream.

Before we get into the nitty-gritty, let’s talk about some of the reasons you’d want to reroute or manipulate the stream going into the Maya Script Editor:

  • The Script Editor is slow. Basically, if a lot of stuff gets printed to the Script Editor very quickly, it noticeably slows down Maya. Not much you can normally do about it other than keep the Script Editor closed but that’s not always an option if you’re trying to follow the progress of a script.
  • Maya keeps crashing (or needs to be killed) and the information required to track down the problem is in the Script Editor.
  • You’ve written a tool with a UI that includes a log display. The log should show some or all Maya messages while the tool is running an operation.

I’m sure there’s plenty of other reasons but those are the ones I’ve personally had to deal with.

The Basics

Alright, enough chit-chat. Let’s walk through the steps required to route the Maya history away from its Script Editor and into the terminal that spawned Maya.

First, the imports:

In case you’re not familiar with it, OpenMaya is the module used to access the Maya API from Python. We’re going to use it to register a custom function as a callback with Maya. The idea is whenever Maya wants to write a line to the Script Editor, it will call our function first and pass it that line.

The only downside to using a Maya API callback is that if we ever want to remove it, we’ll need to track its ID. For the purpose of this demo, I’ll just create a global variable to store the ID. This is not an endorsement of global variables!

Next, let’s write the function for registering the callback:

Notice that we’re passing writeToTerminal to the callback. That’s the function we’ll be writing shortly that gets called every time the callback is triggered.

Here’s the function for removing the callback:

Alright, are you ready for the actual callback function? This is it!

BAM! How easy was that?!

The arguments passed to writeToTerminal are the following:

  • msg – The actual message that Maya wants to write to the Script Editor.
  • msgType – The message type. These are enumerated values representing info, error, warning, etc.
  • filterOutput – A pointer to a boolean value that tells Maya whether the message should be filtered from the Script Editor or not.
  • clientData – Some user defined object (optionally) passed to the callback function.

Important: According to the Maya C++ API, msgType should be a pointer to a bool. However, Python doesn’t support pointers to booleans so we have to use the provided MScriptUtil class to work around that.

A bit more

Actually, if you test the code above, you’ll notice a slight glitch. Certain message types are missing a bit of formatting, including the newline (“/n“) character. This is pretty easy to fix. We’ll just modify our writeToTerminal function slightly to add the formatting back in:

Limitations

  • While any message Maya posts to the Script Editor can be diverted or filtered, it cannot be modified.
  • The callback function cannot be a class instance method. It must either be a standalone function or a class staticmethod. Doing anything else will crash Maya.
  • No print statements inside the callback function! This will cause a segmentation fault because Maya will try to send the print statement to the Script Editor, which will trigger the callback function, which will try to print again, etc, etc, etc.

    There is a workaround though! Two actually. Both follow the same idea, which is: don’t do anything that will get sent to the Script Editor.

    1. Instead of using Maya’s print statement, use sys.__stdout__.write.
    2. Reroute all Python printing to the terminal directly by adding this to the top of your script:



Leave a Reply

  • (will not be published)