0

Here is the code sample:

class RunGui (QtGui.QMainWindow)

    def __init__(self, parent=None):

        ...
        QtCore.Qobject.connect(self.ui.actionNew, QtCore.SIGNAL("triggered()"), self.new_select)
        ...


    def normal_output_written(self, qprocess):
        self.ui.text_edit.append("caught outputReady signal") #works
        self.ui.text_edit.append(str(qprocess.readAllStandardOutput())) # doesn't work


    def new_select(self):
        ...
        dialog_np = NewProjectDialog()
        dialog_np.exec_()
        if dialog_np.is_OK:
            section = dialog_np.get_section()
            project = dialog_np.get_project()
            ...
            np = NewProject()
            np.outputReady.connect(lambda: self.normal_output_written(np.qprocess))
            np.errorReady.connect(lambda: self.error_output_written(np.qprocess))
            np.inputNeeded.connect(lambda: self.input_from_line_edit(np.qprocess))
            np.params = partial(np.create_new_project, section, project, otherargs)
            np.start()

class NewProject(QtCore.QThread):

    outputReady = QtCore.pyqtSignal(object)
    errorReady = QtCore.pyqtSignal(object)
    inputNeeded = QtCore.pyqtSignal(object)
    params = None
    message = ""

    def __init__(self):
        super(NewProject, self).__init__()
        self.qprocess = QtCore.QProcess()
        self.qprocess.moveToThread(self)
        self._inputQueue = Queue()

    def run(self):
        self.params()

    def create_new_project(self, section, project, otherargs):
        ...
        # PyDev for some reason skips the breakpoints inside the thread
        self.qprocess.start(command)
        self.qprocess.waitForReadyRead()
        self.outputReady.emit(self.qprocess) # works - I'm getting signal in RunGui.normal_output_written()
        print(str(self.qprocess.readAllStandardOutput())) # prints empty line
        .... # other actions inside the method requiring "command" to finish properly.

The idea is beaten to death - get the GUI to run scripts and communicate with the processes. The challenge in this particular example is that the script started in QProcess as command runs an app, that requires user input (confirmation) along the way. Therefore I have to be able to start the script, get all output and parse it, wait for the question to appear in the output and then communicate back the answer, allow it to finish and only then to proceed further with other actions inside create_new_project()

Eugene Sajine
  • 8,104
  • 3
  • 23
  • 28

1 Answers1

2

I don't know if this will fix your overall issue, but there are a few design issues I see here.

  1. You are passing around the qprocess between threads instead of just emitting your custom signals with the results of the qprocess
  2. You are using class-level attributes that should probably be instance attributes

Technically you don't even need the QProcess, since you are running it in your thread and actively using blocking calls. It could easily be a subprocess.Popen...but anyways, I might suggest changes like this:

class RunGui (QtGui.QMainWindow)
    
    ...

    def normal_output_written(self, msg):
        self.ui.text_edit.append(msg) 

    def new_select(self):
        ...
            np = NewProject()
            np.outputReady.connect(self.normal_output_written)
            np.params = partial(np.create_new_project, section, project, otherargs)
            np.start()

class NewProject(QtCore.QThread):

    outputReady = QtCore.pyqtSignal(object)
    errorReady = QtCore.pyqtSignal(object)
    inputNeeded = QtCore.pyqtSignal(object)

    def __init__(self):
        super(NewProject, self).__init__()

        self._inputQueue = Queue()
        self.params = None

    def run(self):
        self.params()

    def create_new_project(self, section, project, otherargs):
        ...
        qprocess = QtCore.QProcess()
        qprocess.start(command)
        if not qprocess.waitForStarted():
            # handle a failed command here
            return

        if not qprocess.waitForReadyRead():
            # handle a timeout or error here
            return

        msg = str(self.qprocess.readAllStandardOutput())
        self.outputReady.emit(msg) 

Don't pass around the QProcess. Just emit the data. And create it from within the threads method so that it is automatically owned by that thread. Your outside classes should really not have any knowledge of that QProcess object. It doesn't even need to be a member attribute since its only needed during the operation.

Also make sure you are properly checking that your command both successfully started, and is running and outputting data.

Update

To clarify some problems you might be having (per the comments), I wanted to suggest that QProcess might not be the best option if you need to have interactive control with processes that expect periodic user input. It should work find for running scripts that just produce output from start to finish, though really using subprocess would be much easier. For scripts that need user input over time, your best bet may be to use pexpect. It allows you to spawn a process, and then watch for various patterns that you know will indicate the need for input:

foo.py

import time

i = raw_input("Please enter something: ")
print "Output:", i
time.sleep(.1)
print "Another line"
time.sleep(.1)
print "Done"

test.py

import pexpect
import time

child = pexpect.spawn("python foo.py")
child.setecho(False)

ret = -1
while ret < 0:
    time.sleep(.05)
    ret = child.expect("Please enter something: ")

child.sendline('FOO')
while True:
    line = child.readline()
    if not line:
        break
    print line.strip()

# Output: FOO
# Another line
# Done
Community
  • 1
  • 1
jdi
  • 90,542
  • 19
  • 167
  • 203
  • i have applied your suggestions and it still doesn't work. i tried to use QProcess and use Popen. When I'm usig Qprocess i see the output of the underlying program in the console, but it is not caught in my GUI. I'm really starting to wonder if there is any bug in python2.6 that I'm currently "forced" to use. I chnaged the implementation to use Popen and it works for all the processes I'm starting that do not require user input, i.e. all that are getting finished one way or another. But when it comes to the one that does stop in the middle - nothing works – Eugene Sajine Sep 05 '12 at 13:39
  • The underlying program I'm trying to run is a startup script that runs java application. Can this be the reason of problems? – Eugene Sajine Sep 05 '12 at 13:42
  • I would wager that you are not dealing with any bugs here. Its most likely that you are just missing something in your code, as my example can only show a small section of what you are asking for in this specific question. What you might just need is a code review of the entire thing. You also might need to look at the `pexpect` module for interactively communicating with a process expecting periodic input. – jdi Sep 05 '12 at 17:21
  • See my update with a pexpect example. Just try ditching QProcess all together, and using either pexpect, or subprocess. pexpect should be able to do everything you need though. You would be doing a blocking `while` loop anyways, reading lines, and emitting your own signals with the string values. – jdi Sep 05 '12 at 17:30
  • Under normal circumstances pexpect should do the trick, so I'm accepting the answer. – Eugene Sajine Sep 05 '12 at 18:03
  • Which chat? You need to post the link. – jdi Sep 05 '12 at 18:12
  • http://chat.stackoverflow.com/rooms/15779/discussion-between-eugene-sajine-and-jdi – Eugene Sajine Sep 05 '12 at 18:19
  • this answer really contradict other stackoverflow thread. which recommend using QProcess and now you're telling me to use subprocess? – greendino Oct 19 '20 at 10:31
  • @lone_coder, 8 years ago, given the specifics of the original problem, yes I was suggesting either subprocess or pexpect for the case where you are already using threads and want to start a child process where you need to match on certain stdout before sending an interactive stdin response. If it is easier now to do that with QProcess then go for it. You could then get rid of threads and just start QProcess that emit when data is ready. But professing of the data could still block which is why a thread can be useful. – jdi Oct 20 '20 at 16:40