0

I'm having this issue executing a python3 script from QProcess. The python script is printing the time from second to second and it is working fine from command line. In Qt, the signal readyReadStandardOutput() is connected to a slot where readAllStandardOutput() is called to read the standard output from the script. The problem is that the slot is called only once! It prints the time once and then no more. The state of QProcess remains in "running" state. readyReadStandardError() and error(QProcess::ProcessError) signals are never called.

Why is the slot called only once when it has to be called every second? Thanks

Python script:

import time, threading
def foo():
    print(time.ctime())
    threading.Timer(1, foo).start()

foo()

Qt:

MClass::MClass(QObject* parent)
{
    m_process = new QProcess(this);
    connect(m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(onTest()));
    connect(m_process, SIGNAL(readyReadStandardError()), this, SLOT(onTestErr()));
    connect(m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(onTestErr()));
    connect(m_process, SIGNAL(stateChanged(QProcess::ProcessState)), this, SLOT(onTestState()));

    startProcess();
}

void MClass::startProcess()
{
        QString script= "../../python/test.py";

        QString pythonCommand = "python3 " + script;

        printf("PyCommand: %s\n", pythonCommand.toStdString().c_str());
        m_process->start(pythonCommand);

//        m_process->start("python3", QStringList()<<script);
    }
}

void MClass::onTest()
{
    qDebug()<<"------------------ON TEST";
    while(m_process->canReadLine())
    {
        qDebug()<<m_process->readAllStandardOutput();
    }
}

void MClass::onTestErr()
{
    qDebug()<<"------------------ON ERR: " << m_process->errorString();
}

void MClass::onTestState()
{
    qDebug()<<"------------------ STATE CHANGED: " << m_process->state();
}
p-a-o-l-o
  • 9,807
  • 2
  • 22
  • 35
Ispas Claudiu
  • 1,890
  • 2
  • 28
  • 54
  • Can we see your python script? Is it an interactive script, i.e do you read from standard input? It may explains why the process stays running. – epsilon Apr 20 '18 at 08:26
  • @jbh the python script is above the qt code – Ispas Claudiu Apr 20 '18 at 08:27
  • @IspasClaudiu What if you just call the readAllStandardOutput, whitout the while loop and without testing the canReadLine() ? – Sergio Monteleone Apr 20 '18 at 08:28
  • 1
    @S.Monteleone it doesn't make any difference – Ispas Claudiu Apr 20 '18 at 08:29
  • 2
    It seems to me that the cause of this behavior is that only the first print is executed in the main thread, and the others in the other threads. – eyllanesc Apr 20 '18 at 08:31
  • @eyllanesc maybe you're right. I put more logs before the thread and those are printed however after the thread I don't receive logs anymore. Why it is happening this? The print is suppose to print on the standard output no matter it is called from main thread or another thread, no? – Ispas Claudiu Apr 20 '18 at 08:42
  • When a thread is created a new process is created, and QProcess probably does not access the child process, I am looking for a way to track the new processes. – eyllanesc Apr 20 '18 at 08:44
  • You might consider `QProcess::ForwardedChannels` option to forward streams from threads to the main stream. – vahancho Apr 20 '18 at 08:48
  • Yes, I just checked with the help of `htop`, it is creating new processes – eyllanesc Apr 20 '18 at 08:50
  • @vahancho QProcess::ForwardedChannels does not solve the issue – Ispas Claudiu Apr 20 '18 at 08:54

1 Answers1

2

I think the problem lies in python handling of standard output. I tried this script:

def foo():
    #print(time.ctime())
    sys.stdout.write(time.ctime()) 
    sys.stdout.write("\n")
    sys.stdout.flush()
    threading.Timer(1, foo).start()

where I substituted print with sys.stdout.write and it worked. I think what's really doing the difference is the call to flush.

In other words, your script runs forever, so the standard output never gets flushed. If you try this other script, which ends after five iterations:

def bar():    
    x = 0      
    while x < 5:
        print(time.ctime())
        time.sleep(1)
        x = x + 1

in the Qt app you'll see the whole output after five seconds.

p-a-o-l-o
  • 9,807
  • 2
  • 22
  • 35
  • Yes, it was a flushing problem. Your code works however also printing with the flash flag on true does the job: `print("msg: ", end='\n', file=sys.stdout, flush=True)` – Ispas Claudiu Apr 20 '18 at 11:01