QProcess in Maya - simple working example?

454 views
Skip to first unread message

Michael Boon

unread,
Jan 12, 2014, 11:44:43 PM1/12/14
to python_in...@googlegroups.com
Hi all,

I'm trying to use QProcess in Maya, and I must have some fundamental misunderstanding because I can't make it work.

Here's the simplest thing I've tried (in the Script editor, Maya 2013)

from PyQt4 import QtCore
class processTest(QtCore.QObject):
    def __init__(self):
        super(processTest, self).__init__()
        process = QtCore.QProcess(parent=self)
        process.setProcessChannelMode(process.ForwardedChannels)
        process.start('python', ['-c', 'print("finished qprocess")'])
myTest = processTest()

My expectation was that that would print "finished qprocess" to the script editor, but it doesn't do anything.

I've tried a bunch of other things, including writing to files and using signals, and with some combinations I get an error in Eclipse, "'Match job' has encountered a problem," but I haven't managed to get anything desirable.

As an aside, I'm trying to use the Perforce command line client and give the user a working Cancel button in case there's a delay due to network or server problems. Is QProcess the right choice for that?

Thanks,

Boon.

Justin Israel

unread,
Jan 13, 2014, 2:54:16 AM1/13/14
to python_in...@googlegroups.com
I think the reason you aren't seeing anything in the script editor is because Maya sort-of monkey patches the sys.stdout/sys.stderr to point at the script editor. But when you use a QProcess (or subprocess) it forks and execs a new process that doesn't really have access to the script editor via file descriptors. Its basically inheriting the main stdout/stderror of Maya (that is, I see the output in the Console in osx).

If you want to work with a subprocess asynchronously, then QProcess is definitely a useful tool to do it, since it comes with all the signals. It's actually really easy to fix the issue of getting the output into the script editor, by reacting to the signals and forwarding them to the script editor:

from PyQt4 import QtCore

class ProcessTest(QtCore.QObject):

    def __init__(self):
        super(ProcessTest, self).__init__()
        self.process = QtCore.QProcess(parent=self)
        self.process.setProcessChannelMode(process.MergedChannels)

        self.process.readyReadStandardOutput.connect(self.handle_output)

    def run(self):
        self.process.start('python', ['-c', 'print("finished qprocess")'])

    def handle_output(self):
    sys.stdout.write(self.process.readAllStandardOutput())

myTest = ProcessTest()
myTest.run()

I'm using MergedChannels instead, here, to combine stdout/stderror into a channel that we have to read. Then I have the QProcess notify my handler when data is ready. We read it and reprint it. 
If you don't need all the fancy async signal/slot stuff, then the stdlib subprocess module should be good enough.



--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/94e71427-c5d8-4c18-8c85-d17cb99a74a2%40googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Christopher Stewart

unread,
Jan 13, 2014, 9:34:45 AM1/13/14
to python_in...@googlegroups.com

I SO tried to get this working a year ago ("to no avail, that's why I'm telling you this sorry tale" :-).

Thanks Justin!

Ævar Guðmundsson

unread,
Jan 13, 2014, 11:44:43 AM1/13/14
to python_in...@googlegroups.com
If you run into further troubles, try explicitly directing your output the the pipe buffer you want ( Maya uses both )

print >> sys.stdout, "finished qprocess"
print >> sys.__stdout__, "finished qprocess"

  Justin's method is better, just leaving this one as a note for further experimentation, hope it helps and all that.

Michael Boon

unread,
Jan 13, 2014, 6:46:20 PM1/13/14
to python_in...@googlegroups.com
Thanks all.

In addition to your solutions, the reason none of my tests worked is that I had somehow messed up my Python installation and python.exe was missing. (No, I have no idea how either!) I finally tried a test using subprocess.POpen(['python', ...) and got an error. Reinstalled python 2.6 and all is good now, except for an error involving pydevd that may be unavoidable. I am confused as to why QProcess didn't produce any stderror output about my missing python.exe.

The same code saved to a .py file and imported to the Script Editor didn't produce any output from the QProcess. I guess that's because the QProcess was a child of the main module, and the module was destroyed after the line myTest.run()

I also tried running Justin's blocking.py in Maya, and it instantly crashed Maya, so I moved its guts into a simple example Maya window I found, and it works!

So, here's the simplest example of QProcess that I got working within Maya:
(Apologies if this is not the appropriate way to post code here...please let me know if I should do it differently in any way. I haven't looked into gist.github yet.)

import subprocess
import sip
import maya.OpenMayaUI as mui
from PyQt4 import QtCore, QtGui


def getMainWindow():
    pointer
= mui.MQtUtil.mainWindow()
   
return sip.wrapinstance(long(pointer), QtCore.QObject)

class BlockingTestWin(QtGui.QMainWindow):
   
def __init__(self, parent=None):
       
super(BlockingTestWin, self).__init__(parent)
               
       
self.mainWidget = QtGui.QWidget()
       
self.setCentralWidget(self.mainWidget)
       
       
#Main layout
       
self.layout = QtGui.QVBoxLayout()
       
self.mainWidget.setLayout(self.layout)
       
       
self.blocking_button = QtGui.QPushButton("Block")
       
self.blocking_button.clicked.connect(self.blocking)
       
self.layout.addWidget(self.blocking_button)
 
       
self.signal_button = QtGui.QPushButton("Signals")
       
self.signal_button.clicked.connect(self.non_blocking)
       
self.layout.addWidget(self.signal_button)
 
       
self.output = QtGui.QLineEdit()
       
self.layout.addWidget(self.output)
       
       
self.clear_button = QtGui.QPushButton("Clear")
       
self.clear_button.clicked.connect(self.output.clear)
       
self.layout.addWidget(self.clear_button)
 
   
def finished(self, returnCode):
       
self.setMessage("Return code: %s" % returnCode)
 
   
def setMessage(self, msg):
       
print msg
       
self.output.setText(msg)        
 
   
def blocking(self):
       
self.setMessage("Starting blocking sleep")
        process
= subprocess.Popen(['python', '-c', 'import time\ntime.sleep(5)'])
        ret
= process.wait()
       
self.finished(ret)
 
   
def non_blocking(self):
       
self.setMessage("Starting non blocking sleep")
        process
= QtCore.QProcess(parent=self)
        process
.setProcessChannelMode(process.ForwardedChannels)
        process
.finished.connect(self.finished)
        process
.start('python', ['-c', 'import time\ntime.sleep(5)'])
       
def run():
    app
= BlockingTestWin(getMainWindow())
    app
.show()

Justin Israel

unread,
Jan 13, 2014, 7:06:50 PM1/13/14
to python_in...@googlegroups.com
Yea that example was written to be a standalone script. It creates a QApplication and whatnot.
Your script formatting looks nice here, although if its long examples its generally nicer to either use pastebin or gist with a link. 


--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages