1

This is a follow-up question to a previous one (again posted by me): PyQt4 QProcess state always 0, various slots not working too

The code (modified):

Main application: qprocess_test.py

#!/usr/bin/python

import sys
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import QProcess


class Example(QtGui.QWidget):


    def __init__(self):
        super(Example, self).__init__()
        self.command = "./testCommand.py"
        self.args = [""]
        self.initUI()

    def initUI(self):               
        hbox = QtGui.QHBoxLayout()
        hbox.addStretch(1)

        qbtn = QtGui.QPushButton('Start', self)
        qbtn.clicked.connect(self.toggleProcess)
        qbtn.resize(qbtn.sizeHint())
        hbox.addWidget(qbtn)

        # This button is for testing the responsiveness of the GUI after the QProcess has been started
        qbtn2 = QtGui.QPushButton('Click me', self)
        qbtn2.setCheckable(True)
        qbtn2.toggled.connect(self.toggleButton)
        qbtn2.resize(qbtn2.sizeHint())
        hbox.addWidget(qbtn2)

        self.setLayout(hbox)
        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('QProcess controlled by a button')    
        self.show()

    def toggleProcess(self):
        res, pid = QProcess.startDetached(self.command, self.args)
            print "Starting process\n", str(res), " | pid = ", str(pid)

    def toggleButton(self, value):
        if value == True:
            print "Lalalala!"
        else:
            print "Didadida!"

def main(): 
    app = QtGui.QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

For now all the Start button does is start the detached process. The idea is for it to be toggable and stop/start the detached process whenever the users interacts with it based on the PID.

The application used for the created process: testCommand.py

#!/usr/bin/env python

while True:
    print "a"

After doing some reading and googling I found out that the startDetached() function returns 2 values in Python (in the C++ it's a boolean function, but it also sets the value of one of its arguments - the pointer pid):

  • bool: tells us if the starting of the detached process was successful or not
  • int: gives the PID of the started process if the operation was successful

Here are the sources I have used for this information:

For some reason this is not working at all. Here are the two situations that I have tested:

  • Trying to get both the bool and int values from the function:

    res, pid = self.myProcess.startDetached(self.command, self.args)
    

    I get this error:

    Traceback (most recent call last):
      File "./qprocess_test.py", line 48, in toggleProcess
        res, pid = self.myProcess.startDetached(self.command, self.args)
    TypeError: 'bool' object is not iterable
    

    This tells me that I can't iterate over the return value of startDetached() hence only a SINGLE value is actually returned and not two as I have also seen in multiple pieces of code again part of the PyQt4 documentation...

  • Checking what startDetached() actually returns: it seems it only returns an int, which I presume is the PID. However if it's the PID it is not the correct value (looking at the output of htop):

    val = QProcess.startDetached(self.command, self.args)
    # val = 0 (always!)
    

I have learned that startDetached() is a static function hence any future interaction with the created process can be done via it's pid but things like calling its state (QProcess.state()) etc. are not possible since there is no object to interact with. The way this (broken) thing works right now is indeed start a detached process but since I don't have any PID that identifies it, the only way of interaction I have is finding manually the PID of the testCommand.py and then executing the appropriate signal via kill.

Does anyone know what I'm doing wrong here? I can go to the basics and start using C/C++ with the various system calls but I would really like to actually learn how to do this with PyQt4 and Python.

Thanks!

EDIT

It seems that startDetached() indeed returns True|False. The PID however is nowhere to be found...Not what one reads in the official documentation.

Community
  • 1
  • 1
rbaleksandar
  • 8,713
  • 7
  • 76
  • 161
  • For your second situation: I think the int that `startDetached()` returns is the boolean, not the PID (0=False, 1=True) – Mel Jul 20 '15 at 14:37
  • 2
    According to the PyQt Doc, the actual function that returns the PID is `(bool, int pid) QProcess.startDetached (QString program, QStringList arguments, QString workingDirectory)`. Note the third argument. – Mel Jul 20 '15 at 14:40
  • The Qt documentation also states that in case workingDirectory is not given, the started process takes the value from the spawning process. Btw I also tried to get a tuple from this function (by adding the round brackets) - same error with bool being non-iterable. – rbaleksandar Jul 20 '15 at 14:45
  • @tmoreau I don't thinks so because 1)why return 0|1 when Python uses False|True for values of type bool (this would be a piss-poor porting mechanic which can lead to a lot of confusion) and 2)False in this case will mean that the process was not started (at least reading the documentation says so), but the process IS started and running without a problem. – rbaleksandar Jul 20 '15 at 14:47
  • There's only one way to know for sure: do `print(type(val))`. Me I have `bool`, but val print "True", so there might be something we're missing. I'm on Python3.4 and PyQt 4.11 – Mel Jul 20 '15 at 14:53
  • I just restared my code a couple of times and the function started to return True|False. LOL So yeah, the 0 and 1 are indeed True and False but the PID is still nowhere to be found. I am also using Python 2.7 and the latest PyQt4 in the Ubuntu repo. – rbaleksandar Jul 20 '15 at 14:57
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/83773/discussion-between-tmoreau-and-rbaleksandar). – Mel Jul 20 '15 at 15:33

1 Answers1

7

The function startDetached is overloaded in C++, it has several signatures. Depending on the arguments provided, it does not necesseraly return the same thing.

Even if overloading does not exist in Python, you have something similar:

import sys
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import QProcess

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)

    command = "./testCommand.py"
    args = [""]

    #"regular" startDetached : give two arguments, get a boolean
    process=QProcess()
    re=process.startDetached("ls",["."])
    print(re)
    print(type(re))

    #"overload" startDetached : give three arguments, get a tuple(boolean,PID)
    process2=QProcess()
    re,pid=process2.startDetached("ls",["."],".")
    print(re,pid)
    print(type(re),type(pid))
Community
  • 1
  • 1
Mel
  • 5,837
  • 10
  • 37
  • 42
  • Thanks a bunch again! Btw you don't need the process variable due to the nature of startDetached(). It works perfectly fine. Also I really have to start reading documentions from top to bottom - the PyQt4 documentation explicitly describes ALL 3 versions of QProcess.startDetached() and what those return... -_- – rbaleksandar Jul 20 '15 at 15:58