I have a PyQt GUI with a QTextEdit
box that displays print statements during code execution. I chose to do this by redirecting sys.stdout
, following posts like this one:
Print out python console output to Qtextedit
It works great for logging print statements so that the user can see what happened during execution, however I'd like to have it update as the code is running, rather than writing to the buffer and printing everything out at the end. In this code, for example, clicking the 'Run' button will wait 5 seconds to print both 'Running...' and 'Done.' after the function runs, whereas I'd like it to display 'Running...', wait 5 seconds, then display 'Done.'
Here are the basics of the code as I have it written:
import sys
import time
from PyQt5.QtWidgets import QMainWindow, QPushButton, QApplication, QTextEdit
from PyQt5 import QtCore, QtGui
class Stream(QtCore.QObject):
"""Redirects console output to text widget."""
newText = QtCore.pyqtSignal(str)
def write(self, text):
self.newText.emit(str(text))
class GenMast(QMainWindow):
"""Main application window."""
def __init__(self):
super().__init__()
self.initUI()
# Custom output stream.
sys.stdout = Stream(newText=self.onUpdateText)
def onUpdateText(self, text):
"""Write console output to text widget."""
cursor = self.process.textCursor()
cursor.movePosition(QtGui.QTextCursor.End)
cursor.insertText(text)
self.process.setTextCursor(cursor)
self.process.ensureCursorVisible()
def closeEvent(self, event):
"""Shuts down application on close."""
# Return stdout to defaults.
sys.stdout = sys.__stdout__
super().closeEvent(event)
def initUI(self):
"""Creates UI window on launch."""
# Button for generating the master list.
btnGenMast = QPushButton('Run', self)
btnGenMast.move(450, 100)
btnGenMast.resize(100, 100)
btnGenMast.clicked.connect(self.genMastClicked)
# Create the text output widget.
self.process = QTextEdit(self, readOnly=True)
self.process.ensureCursorVisible()
self.process.setLineWrapColumnOrWidth(500)
self.process.setLineWrapMode(QTextEdit.FixedPixelWidth)
self.process.setFixedWidth(400)
self.process.setFixedHeight(150)
self.process.move(30, 100)
# Set window size and title, then show the window.
self.setGeometry(300, 300, 600, 300)
self.setWindowTitle('Generate Master')
self.show()
def genMastClicked(self):
"""Runs the main function."""
print('Running...')
time.sleep(5)
print('Done.')
if __name__ == '__main__':
# Run the application.
app = QApplication(sys.argv)
app.aboutToQuit.connect(app.deleteLater)
gui = GenMast()
sys.exit(app.exec_())
I first tried setting flush=True
in the print statements. However, I was met with the error 'Stream' object has no attribute 'flush'
, so I moved on to defining flush
in the Stream
class I created, as follows:
class Stream(QtCore.QObject):
"""Redirects console output to text widget."""
newText = QtCore.pyqtSignal(str)
def write(self, text):
self.newText.emit(str(text))
def flush(self):
sys.stdout.flush()
Which produced the error RecursionError: maximum recursion depth exceeded
. This is where my understanding runs out, as I've tried other ways to flush the print buffer but they all seem to have this problem. Any advice on what I'm doing wrong?