1

I want to redirect the console output of python program in real time in a gui using PyQt5

I mean that in every output the python program do, the gui display this output instantly

the code I used only display the logs after finishing the whole program and the real time issue is not solved

can you help me

below my code:

from PyQt5 import QtGui,QtCore
from PyQt5 import uic
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5 import QtGui
from PyQt5.QtGui import QPixmap

class gui(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('python',['Robot.py'])

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

        self.output =  QTextEdit()
        self.setGeometry(100, 60, 1000, 800)
        layout.addWidget(self.output)
        layout.addWidget(self.runButton)

        centralWidget =  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 =QApplication(sys.argv)
    ui=gui()
    ui.show()
    sys.exit(app.exec_())
#Function Main END

if __name__ == '__main__':
    main() ```
Ahmed
  • 13
  • 6
  • You could launch a thread to handle the updating in a time loop or use an interrupt using something like signal which allows you to handle asynchronous events. – nerak99 Apr 28 '21 at 09:54

2 Answers2

0

When you redirect python output to file or pipe it is buffered for perfomance reasons. You need to run script with -u cli argument or flush every print output like this: print(something, flush=True). See this question for details.

self.process.start('python',['-u','Robot.py'])
mugiseyebrows
  • 4,138
  • 1
  • 14
  • 15
  • thank you. self.process.start('python',['-u','Robot.py']) worked for me ... but every line is colled with the next line with'\n'. do you know any solution to have independant lines? – Ahmed Apr 28 '21 at 13:52
  • `strip()` text before inserting? – mugiseyebrows Apr 28 '21 at 14:01
  • I didn't understand, I want to every output to be in an independant line I mean i replace '\n' with an effective return to next line – Ahmed Apr 28 '21 at 14:07
  • 1
    @Patrick use `cursor.insertText(self.process.readAll().data().decode())` – eyllanesc Apr 28 '21 at 15:15
0

the full solution here, thanks to @mugiseybrows and @eyllanesc

it resolves two problem: the first is redirecting output of python pragram in a gui and the second it displays every output in an independant line

import sys
from PyQt5 import QtGui,QtCore
from PyQt5 import uic
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5 import QtGui
from PyQt5.QtGui import QPixmap

class gui(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()))
        cursor.insertText(self.process.readAll().data().decode())
        self.output.ensureCursorVisible()

    def callProgram(self):
        # run the process
        # `start` takes the exec and a list of arguments
        self.process.start('python',['-u','Robot.py'])

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

        self.output =  QTextEdit()
        self.setGeometry(100, 60, 1000, 800)
        layout.addWidget(self.output)
        layout.addWidget(self.runButton)

        centralWidget =  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 =QApplication(sys.argv)
    ui=gui()
    ui.show()
    sys.exit(app.exec_())
#Function Main END

if __name__ == '__main__':
    main() 
Ahmed
  • 13
  • 6