【问题标题】:RealTime output from a subprogram to stdout of a pyQT Widget从子程序到 pyQT Widget 的标准输出的实时输出
【发布时间】:2014-03-30 22:19:05
【问题描述】:

您好,我已经看到关于这个问题已经有很多问题了,但是似乎没有一个回答我的问题。

根据下面的链接,我什至在使用 windows 时尝试了 winpexpect,但它似乎对我有用。 Getting realtime output from ffmpeg to be used in progress bar (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()

【问题讨论】:

  • 必须是subprocess吗?你不能用QProcess吗?
  • @Avaris 你能举例说明你在说什么吗?
  • 好的,我会写一个答案。

标签: python subprocess pyqt4


【解决方案1】:

这是对 PyQt5 接受答案的改编。

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 = "cp" + str(ctypes.cdll.kernel32.GetConsoleOutputCP())

from PyQt5 import QtCore, QtGui, QtWidgets

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() 

【讨论】:

    【解决方案2】:

    QProcesssubprocess 非常相似,但在 (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() 
    

    【讨论】:

    • .readAll() 是一个令人困惑的名称。我必须阅读 Qt 文档才能发现它只读取可用数据,即它不会阻止 dataReady() 方法。
    • @Avaris - 这正是我想要的,谢谢。仍然想知道为什么我没能早点找到它并为延迟回复感到抱歉:)
    • 注: Python3 用户:将str(self.process.readAll()) 替换为str(self.process.readAll(), 'utf-8')
    • 如果您有多个进程信号(来自代码的不同部分,或同时多个),我喜欢使用 lambda 将进程传递给 print/write 函数,而不是引用它在课堂里。因此,我将添加“process”作为 dataReady() 的参数而不是 self.process,即 dataReady(self, process)。然后信号看起来像 self.process.readyRead.connect(lambda: self.dataReady(self.process))
    猜你喜欢
    • 2023-03-24
    • 2013-06-29
    • 1970-01-01
    • 2016-12-01
    • 2011-08-29
    • 2016-12-03
    • 1970-01-01
    • 2017-12-29
    • 2010-12-09
    相关资源
    最近更新 更多