I am building a GUI to serve as an interactive "curve fitting tool" for Python. I've added a "logging" window in the form of a QTextEdit widget (in readonly mode) that displays relevant output from the code - things like optimized parameters, warnings, etc. This is done by redirecting sys.stdout and sys.stderr to the QTextEdit widget via something like this:
class Stream(QtCore.QObject):
newText = QtCore.pyqtSignal(str)
def write(self, text):
self.newText.emit(str(text))
class MyApp(QtWidgets.QMainWindow):
def __init__(self):
# Other stuff here...
self.outputWidget = QtWidgets.QTextEdit();
self.outputWidget.setReadOnly(True)
vLayout.addWidget(self.outputWidget)
sys.stderr = Stream(newText=self.onUpdateText)
sys.stdout = Stream(newText=self.onUpdateText)
What I want to do is after certain things are done by the user (Like changing the fit function, etc) is clear the logging window and then continue printing whatever comes up to prevent confusion from previous fits.
The immediately obvious thing is something like this:
def onUpdateText(self, text):
self.outputWidget.clear()
self.outputWidget.moveCursor(QtGui.QTextCursor.End,QtGui.QTextCursor.MoveAnchor)
self.outputWidget.insertPlainText(text)
self.outputWidget.ensureCursorVisible()
However - this just leads to the widget being cleared of all text and nothing else showing up (Or, rather, nothing ever shows up at all). Removing the clear()
call allows it to work properly, although without clearing previous output.
What is the proper way to clear the text of the widget immediately before putting additional text in?
Edit:
Here is a reproducible example:
import sys
from PyQt5 import QtGui, QtCore,QtWidgets
from PyQt5.QtCore import pyqtSlot
import PyQt5.QtCore as qtcore
class Stream(QtCore.QObject):
newText = QtCore.pyqtSignal(str)
def write(self, text):
self.newText.emit(str(text))
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
vLayout = QtWidgets.QVBoxLayout(self._main);
self.inputWidget = QtWidgets.QLineEdit();
self.inputWidget.setText("Type something here.")
self.inputWidget.textEdited.connect(self.textChanged)
vLayout.addWidget(self.inputWidget)
self.outputWidget = QtWidgets.QTextEdit();
self.outputWidget.setReadOnly(True)
vLayout.addWidget(self.outputWidget)
sys.stdout = Stream(newText=self.onUpdateText)
def __del__(self):
sys.stdout = sys.__stdout__
def onUpdateText(self, text):
self.outputWidget.clear()
self.outputWidget.insertPlainText(text)
self.outputWidget.ensureCursorVisible()
def textChanged(self,newstr):
print(newstr)
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = ApplicationWindow()
app.show()
qapp.exec_()
The problem is caused because the print()
method writes on the buffer 2 texts: the text that is passed to print and the endline ("\n"), that you can verify if you pass the bytes. Thus, the first writing deletes the previous and is added to the new text, and in the second writing the previous text is deleted and the "\n" which is not visible is caused causing the apparent non-writing.
def onUpdateText(self, text):
self.outputWidget.insertPlainText(str(text.encode()))
Output:
So depending on your real application there are several solutions:
sys.stdout.write()
instead of print()
.def onUpdateText(self, text):
self.outputWidget.clear()
self.outputWidget.insertPlainText(text)
def textChanged(self, newstr):
sys.stdout.write(newstr)
def onUpdateText(self, text):
if text != "\n":
self.outputWidget.clear()
self.outputWidget.insertPlainText(text)
def textChanged(self, newstr):
print(newstr)
Or
def onUpdateText(self, text):
if text != "\n":
self.outputWidget.clear()
self.outputWidget.insertPlainText(text)
def textChanged(self, newstr):
print(newstr)