Using pipes/slots

edited February 2019 in Python scripting

Hi, I am trying to read from PIPE in a Python macro.
I have nice stand-alone sample (it runs "ping 127.0.0.1" and reads/prints its output):

import sys
from PyQt5.QtWidgets import QApplication
from PyQt5 import QtCore, QtGui

class PipeReader(QtCore.QObject):
    def __init__(self, parent=None):
        super(PipeReader, self).__init__(parent)
        self.process = QtCore.QProcess(self)
        self.process.started.connect(self.on_started)
        self.process.finished.connect(self.on_finished)
        self.process.readyReadStandardOutput.connect(self.on_ready_read_standard_output)
        command = "ping 127.0.0.1"
        self.process.start(command)

    def on_ready_read_standard_output(self):
        result = self.process.readAllStandardOutput()
        print("result = ", result)

    def on_started(self):
        print("started")

    def on_finished(self):
        print("finished")
        app.quit()

app = QApplication([])
reader = PipeReader()
sys.exit(app.exec_())

I tried to port it to KLayout:

import pya

class PipeReader(pya.QObject):

    def __init__(self, parent=None):
        super(PipeReader, self).__init__(parent)
        self.process = pya.QProcess(self)
        self.process.finished = self.on_finished
        self.process.readyReadStandardOutput = self.on_ready_read_standard_output
        self.process.start("ping 127.0.0.1")

    def on_ready_read_standard_output(self):
        result = self.process.readAllStandardOutput()
        print("result = ", result)

    def on_finished(self):
        print("finished")


#app = pya.QApplication()
reader = PipeReader()
#sys.exit(app.exec_())

But the only output I get is "finished". It takes some time to complete, so it really runs "ping", but no output is shown.
Maybe there is some problem in my QProcess config ?

Ps. The next code works almost perfectly (i.e. it shows "ping" output), but it locks the KLayout UI:

import subprocess
import io

process = subprocess.Popen("ping 127.0.0.1 -t", stdout=subprocess.PIPE, shell=True)
for line in io.TextIOWrapper(process.stdout):
    print(line)

What could be wrong ?

Comments

  • edited February 2019

    Hi yaroslav,

    I think Popen blocks because Qt implements it's own event loop (on Linux maybe it does a select, but I don't know if that helps keeping Popen active). There is a competition for CPU time and Popen looses.

    The QProcess approach works, but you need to carefully control the way you're doing that. You're inside a live application and how you need to do this depends on what you want to achive. In the PyQt5 example you did a handshake and used "app.quit" to terminate the loop. You will also need to control the lifetime of the PipeReader object - as soon as the function finishes, the reader object will be deleted which ends your QProcess too.

    The equivalent of your PyQt5 version above is:

    import pya
    
    class PipeReader(pya.QObject):
    
        def __init__(self, parent=None):
            super(PipeReader, self).__init__(parent)
            self.process = pya.QProcess(self)
            self.process.finished = self.on_finished
            self.process.readyReadStandardOutput = self.on_ready_read_standard_output
            self.process.start("ping 127.0.0.1 -c 4")
    
        def on_ready_read_standard_output(self):
            result = self.process.readAllStandardOutput()
            print("result = ", result)
    
        def on_finished(self):
            print("finished")
            pya.QApplication.instance().quit()
    
    reader = PipeReader()
    pya.QApplication.instance().exec_()
    

    run this with

    klayout -z -r this_script.py
    

    If you want to run the process inside a menu function for example, you'll need to keep a reference to the reader object. The easiest way is to provide a parent in the constructor:

    class PipeReader(pya.QObject):
    
        def __init__(self, parent = None):
            super(PipeReader, self).__init__(parent)
            self.process = pya.QProcess(self)
            self.process.finished = self.on_finished
            self.process.readyReadStandardOutput = self.on_ready_read_standard_output
            self.process.start("ping 127.0.0.1 -c 4")
    
        def on_ready_read_standard_output(self):
            result = self.process.readAllStandardOutput()
            print("result = ", result)
    
        def on_finished(self):
            print("finished")
            self.deleteLater()
    
    PipeReader(pya.QApplication.instance())
    

    Note the "deleteLater" in "on_finished". This will actually remove the object once the operation has finished.

    Regards,

    Matthias

  • edited February 2019

    Hi Matthias,
    thanks for the fast reply.

    I want to run the script from menu, and with your help I was able to get the right response from the ping command on Centos 7 (just running your script):

    ('result = ', 'PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.\n64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.031 ms\n')
    ('result = ', '64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.071 ms\n')
    ('result = ', '64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.083 ms\n')
    ('result = ', '64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.083 ms\n\n--- 127.0.0.1 ping statistics ---\n4 packets transmitted, 4 received, 0% packet loss, time 2999ms\nrtt min/avg/max/mdev = 0.031/0.067/0.083/0.021 ms\n')
    finished
    

    But on Windows it is silent :|
    Only "finished" is printed.
    (I only removed "-c -4" from the ping command, as on Windows it has different keys)

    Can you suggest how can I debug this ? Is it Qt or Python ?

    On Centos 7 "About" box shows Qt 4.8.7 (Klayout 0.25.7)
    And on Windows it is 5.6.2 (Klayout 0.25.7)

  • Update: for Windows case I have replaced

    self.process.readyReadStandardOutput = self.on_ready_read_standard_output
    

    by

    self.process.readyRead = self.on_ready_read_standard_output
    

    and it started to produce correct output.
    Looks like there are some problems in readyReadStandardOutput slot or stdout on Windows.

    Anyway that solves my problem.

    Thanks a lot !

Sign In or Register to comment.