[Maya][Python][PyQt] Redirecting Print, Warning, Error to a custom Window

I work with alot of artists who don’t know about the script editor, and hide the status and help lines.
as a consequence, alot of the information that our tools report back, such as warnings and errors go unnoticed.

I would like to create a simple Window that collects the Print, Warning and Error messages that are peppered through our toolset.

I’m looking for advice on how to capture output that normally goes to Maya’s Script Editor and also display it in a custom window.

Thanks.

I just started using tool-tips / annotations for this purpose. Not good for displaying LOTS of information, but I like it for a few sentences of info/errors/help where appropriate.

i.e. if they hover over a list of items to export, it’ll show where the first few will be exported.

Possibly useful, the scriptEditorInfo command allows you to write the script editor output to a text file, which you could then parse/use however you wanted.

Why not use python’s logging? You could have it print to stdout as well as to a file. Then you would have complete control over the data.

I have used logging in the past, I was looking for something that was real-time and wouldn’t have to read in a file…

Can I pipe stdout to a UI widget?

Is PyQt an option?

You could create your own class that has a pyqtSignal and a write() method that emits it’s messages through the pyqtSignal. Then just replace stdout/stderr with that object and connect it’s signal to a QTextBrowser widget or something similar. You could even have the write() method output to the original stdout/stderr as well.

Python inherent logging is good, with most of the options you’d want (filtering, etc). It’s also easy to swap in different loggers at the the high level and not have to touch the code that’s doing the logging - so if you decide, say, to log errors to a database in the future you can do it by swapping in a new logger (example: https://gist.github.com/ykessler/2662203#file_sqlite_handler.py)

Once you get it going kill all your print statements. Print is slow and it fills the artists windows with stuff they don’t know or care about making it harder for them to see things they want to see.

You can intercept gui.Excepthook and do anything you want with exceptions (and catches anything tossed up by cmds.error()) but it won’t catch warnings or prints.

The maya sys.stdout is a maya.Output object. I’m assuming you could create your own object and set the sys.stdout = YourOutputClass(). Then you could have the important functions (write()) do whatever you want.


type(sys.stdout)
# Result: <type 'maya.Output'> # 
dir(sys.stdout)
# Result: ['__class__',
 '__delattr__',
 '__doc__',
 '__format__',
 '__getattribute__',
 '__hash__',
 '__init__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 'flush',
 'softspace',
 'write',
 'writelines'] # 

For example, this quick hack writes to my file, but I could have done anything with it:


class MyStdout(object):
    def write(self, value):
        """
        """
        with open('C:/temp/testmaya.txt','a') as f:
            f.write('Rerouted: %s
' % value)

mystdout = MyStdout()
sys.stdout = mystdout

I assume you’d have to do something similar for stderr too.

Thanks for the hints, i’ll keep ya posted.

as mentionned python logging seems to be the stronghest way , but i recently tried this method (python file + scriptEditor Info) and works fine (for my problem)
If it can help here is the code : https://docs.google.com/file/d/0B5R_JI6qCZAYU1RiVURpY01VYmc/edit?usp=sharing

*Record the script editor result and store it in a file
*create a HUD in the scene that print the last line of this file.

  • regenerate the log file if the file is more than X lines

I will search around the logging module too, it seems to be a very strongh way to do.

Forgot to mention: the python default stdout is stored in sys.stdout. It writes to the maya Output window, that useless one that always pops up at the beginning of your session. If you want to log data to a window (not a file) and keep it out of the listener, sys.stdout.write will put text into the output window.

Be warned, though, that you can’t save the output window. I think you can select all and copy the contents however.

to clarify, I don’t want to suppress anything from going to the script editor, i just want another window to catch messages.

Basically just want a window that will catch the output from our custom tools, preferably without reading/writing to a file.

Thanks again for all your tips.

You could make dummy procs to replace cmds.warning and cmds.error with sys.stdout.writeline. That said, I’d look seriously at database logging as the main avenue for this : you can get good statistics and see patterns that way that are invisible when just trolling log spew.

Sorry to re-open this thread, but I’m having a similar problem. We have a lot of tools that use a mixture of “print”, “MGlobal.displayWarning”, and “MGlobal.displayError”. For most uses, this is fine. However, I am now trying to run some of these tools in Maya standalone (non-GUI) mode, and I would like to log the output to a file.

I tried something similar to what Phil suggested, assigning sys.stdout to my own custom class that makes logging calls. This worked for logging the “print” statements, but it does not intercept the calls to “MGlobal.displayWarning” or “MGlobal.displayError”. I also tried redirecting sys.stderr, sys.stdout, and sys.stderr. None of them catch the MGlobal calls.

Does anybody know how to intercept these MGlobal calls? How is it possible that they are printing to the console window when they are not going to sys.stdout, sys.stderr, sys.stdout, or sys.__stderr? I am trying to avoid putting logging code into all the tools, just because there are so many existing files that would need to be touched.

(I also tried using the scriptEditorInfo command, but it says the -historyFilenamd and -writeHistory flags are not supported in batch mode.)

I just had a thought. Maybe the reason I can’t catch the MGlobal calls is because the printing is going through the C++ API instead of the Python interpreter? Like MGlobal.displayWarning calls some C++ code under the hood, and it is that C++ code that does the actual printing. Since there is no return value from the print statement, it never goes through the Python interpreter. Does this sound possible?

That does sound like the case. MGlobal seems to do something slightly different from its counterparts in cmds: for example, MGlobal.displayError prints an error message - and doesn’t do anything else – while cmds.Error stops execution.

Over the long run it’s a good idea to standardize all of the recording calls with your own API and use it everywhere - otherwise you’ll be chasing these things for ever.

Okay. That sounds like the way to go. It will be painful going through all the modules one by one and replacing all the print/displayWarning/displayError statements, but like you said, it will be better in the long run.

I keep telling myself to put a test in my builder that rejects any code with a print statement. I never do it, of course, but I keep telling myself to.