RealTime从子程序输出到pyQT Widget的stdout

paa*_*tra 7 python subprocess pyqt4

嗨,我已经看到在这个问题上已经有很多问题,但是他们似乎都没有回答我的问题.

根据下面的链接我甚至尝试了winpexpect,因为我正在使用Windows,但它似乎正在为我工​​作. 从ffmpeg获取实时输出以在进度条中使用(PyQt4,stdout)

我正在使用subprocess.Popen运行子程序,并希望在pyQt Widget中看到实时结果.目前它在pyQt小部件中显示结果,但仅在子命令完成执行后才显示.我需要知道当我们可以将子进程的输出实时输入窗口时是否有办法.请参阅下面的代码,我尝试了所有这些.

import sys
import os
from PyQt4 import QtGui,QtCore
from threading import Thread
import subprocess
#from winpexpect import winspawn



class EmittingStream(QtCore.QObject):
    textWritten = QtCore.pyqtSignal(str)

    def write(self, text):
        self.textWritten.emit(str(text))

class gui(QtGui.QMainWindow):

    def __init__(self):
    # ...
        super(gui, self).__init__()
    # Install the custom output stream
        sys.stdout = EmittingStream(textWritten=self.normalOutputWritten)
        self.initUI()

    def normalOutputWritten(self, text):
        cursor = self.textEdit.textCursor()
        cursor.movePosition(QtGui.QTextCursor.End)
        cursor.insertText(text)
        self.textEdit.ensureCursorVisible()

    def callProgram(self):

        command="ping 127.0.0.1"
        #winspawn(command)
              py=subprocess.Popen(command.split(),stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True)
        result,_=py.communicate()
        for line in result:
            print line
        print result


    def initUI(self):
        self.setGeometry(100,100,300,300)
        self.show()

        self.textEdit=QtGui.QTextEdit(self)
        self.textEdit.show()
        self.textEdit.setGeometry(20,40,200,200)

        print "changing sys.out"
        print "hello"

        thread = Thread(target = self.callProgram)
        thread.start()


#Function Main Start
def main():
    app = QtGui.QApplication(sys.argv)
    ui=gui()
    sys.exit(app.exec_())
#Function Main END

if __name__ == '__main__':
    main()
Run Code Online (Sandbox Code Playgroud)

Ava*_*ris 24

QProcess非常相似subprocess,但在(Py)Qt代码中使用它更方便.因为它利用信号/插槽.此外,它以异步方式运行该进程,因此您无法使用QThread.

我已修改(并清理)您的代码QProcess:

import sys
from PyQt4 import QtGui,QtCore

class gui(QtGui.QMainWindow):
    def __init__(self):
        super(gui, self).__init__()
        self.initUI()

    def dataReady(self):
        cursor = self.output.textCursor()
        cursor.movePosition(cursor.End)
        cursor.insertText(str(self.process.readAll()))
        self.output.ensureCursorVisible()

    def callProgram(self):
        # run the process
        # `start` takes the exec and a list of arguments
        self.process.start('ping',['127.0.0.1'])

    def initUI(self):
        # Layout are better for placing widgets
        layout = QtGui.QHBoxLayout()
        self.runButton = QtGui.QPushButton('Run')
        self.runButton.clicked.connect(self.callProgram)

        self.output = QtGui.QTextEdit()

        layout.addWidget(self.output)
        layout.addWidget(self.runButton)

        centralWidget = QtGui.QWidget()
        centralWidget.setLayout(layout)
        self.setCentralWidget(centralWidget)

        # QProcess object for external app
        self.process = QtCore.QProcess(self)
        # QProcess emits `readyRead` when there is data to be read
        self.process.readyRead.connect(self.dataReady)

        # Just to prevent accidentally running multiple times
        # Disable the button when process starts, and enable it when it finishes
        self.process.started.connect(lambda: self.runButton.setEnabled(False))
        self.process.finished.connect(lambda: self.runButton.setEnabled(True))


#Function Main Start
def main():
    app = QtGui.QApplication(sys.argv)
    ui=gui()
    ui.show()
    sys.exit(app.exec_())
#Function Main END

if __name__ == '__main__':
    main() 
Run Code Online (Sandbox Code Playgroud)

  • 如果您有多个过程信号(来自代码的不同部分,或同时有多个),我喜欢使用lambda将过程传递给print/write函数,而不是在类中引用它.因此,我将添加"process"作为dataReady()的参数,即dataReady(self,process),而不是self.process.然后信号看起来像self.process.readyRead.connect(lambda:self.dataReady(self.process)) (2认同)

Vin*_*ier 6

这是 PyQt6 和 PyQt5 已接受答案的改编。

PyQt6:

from PyQt6 import QtCore, QtWidgets
import sys

# On Windows it looks like cp850 is used for my console. We need it to decode the QByteArray correctly.
# Based on https://forum.qt.io/topic/85064/qbytearray-to-string/2
import ctypes
CP_console = f"cp{ctypes.cdll.kernel32.GetConsoleOutputCP()}"


class gui(QtWidgets.QMainWindow):
    def __init__(self):
        super(gui, self).__init__()
        self.initUI()

    def dataReady(self):
        cursor = self.output.textCursor()
        cursor.movePosition(cursor.MoveOperation.End)

        # Here we have to decode the QByteArray
        cursor.insertText(
            str(self.process.readAll().data().decode(CP_console)))
        self.output.ensureCursorVisible()

    def callProgram(self):
        # run the process
        # `start` takes the exec and a list of arguments
        self.process.start('ping', ['127.0.0.1'])

    def initUI(self):
        # Layout are better for placing widgets
        layout = QtWidgets.QVBoxLayout()
        self.runButton = QtWidgets.QPushButton('Run')
        self.runButton.clicked.connect(self.callProgram)

        self.output = QtWidgets.QTextEdit()

        layout.addWidget(self.output)
        layout.addWidget(self.runButton)

        centralWidget = QtWidgets.QWidget()
        centralWidget.setLayout(layout)
        self.setCentralWidget(centralWidget)

        # QProcess object for external app
        self.process = QtCore.QProcess(self)
        # QProcess emits `readyRead` when there is data to be read
        self.process.readyRead.connect(self.dataReady)

        # Just to prevent accidentally running multiple times
        # Disable the button when process starts, and enable it when it finishes
        self.process.started.connect(lambda: self.runButton.setEnabled(False))
        self.process.finished.connect(lambda: self.runButton.setEnabled(True))

# Function Main Start


def main():
    app = QtWidgets.QApplication(sys.argv)
    ui = gui()
    ui.show()
    sys.exit(app.exec())
# Function Main END


if __name__ == '__main__':
    main()

Run Code Online (Sandbox Code Playgroud)

PyQt5:

from PyQt5 import QtCore, QtWidgets
import sys

# On Windows it looks like cp850 is used for my console. We need it to decode the QByteArray correctly.
# Based on https://forum.qt.io/topic/85064/qbytearray-to-string/2
import ctypes
CP_console = f"cp{ctypes.cdll.kernel32.GetConsoleOutputCP()}"


class gui(QtWidgets.QMainWindow):
    def __init__(self):
        super(gui, self).__init__()
        self.initUI()

    def dataReady(self):
        cursor = self.output.textCursor()
        cursor.movePosition(cursor.End)

        # Here we have to decode the QByteArray
        cursor.insertText(
            str(self.process.readAll().data().decode(CP_console)))
        self.output.ensureCursorVisible()

    def callProgram(self):
        # run the process
        # `start` takes the exec and a list of arguments
        self.process.start('ping', ['127.0.0.1'])

    def initUI(self):
        # Layout are better for placing widgets
        layout = QtWidgets.QVBoxLayout()
        self.runButton = QtWidgets.QPushButton('Run')
        self.runButton.clicked.connect(self.callProgram)

        self.output = QtWidgets.QTextEdit()

        layout.addWidget(self.output)
        layout.addWidget(self.runButton)

        centralWidget = QtWidgets.QWidget()
        centralWidget.setLayout(layout)
        self.setCentralWidget(centralWidget)

        # QProcess object for external app
        self.process = QtCore.QProcess(self)
        # QProcess emits `readyRead` when there is data to be read
        self.process.readyRead.connect(self.dataReady)

        # Just to prevent accidentally running multiple times
        # Disable the button when process starts, and enable it when it finishes
        self.process.started.connect(lambda: self.runButton.setEnabled(False))
        self.process.finished.connect(lambda: self.runButton.setEnabled(True))

# Function Main Start


def main():
    app = QtWidgets.QApplication(sys.argv)
    ui = gui()
    ui.show()
    sys.exit(app.exec_())
# Function Main END


if __name__ == '__main__':
    main()

Run Code Online (Sandbox Code Playgroud)