Search code examples
pythonpyqt5displayobject

How do I direct console output to a pyqt5 plainTextEdit widget with Python?


I am trying to display console output of a python script in a QplainTextEdit widget in PyQt5.

I am getting this error:

TypeError: Error when calling the metaclass bases metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

I have defined my objects in the pyqt GUI file and I believe that I have all the imports.

Update

I have amended the code in this question:

from PyQt5.QtCore import QRectF, Qt
from PyQt5.QtWidgets import QFileDialog, QPlainTextEdit
from PyQt5 import QtCore, QtGui, QtWidgets
from PIL import Image, ImageQt, ImageEnhance
# from PyQt5.QtGui import Qt
from pyqtgraph.examples.text import text

from covid19gui_V3 import Ui_MainWindow
import os
import sys

input_img = Image.open("/home/ironmantis7x/Documents/Maverick_AI/Python/keras-covid-19/maverickAI30k.png")
text_edit = QPlainTextEdit()

class EmittingStream(QtCore.QObject):

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

class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    textWritten = QtCore.pyqtSignal(str)
    def __init__(self, parent=None, **kwargs):
        super(MainWindow, self).__init__(parent)
        self.setupUi(self)
        self.ShowIButton.clicked.connect(self.do_test)
        self.chooseStudy.clicked.connect(self.do_choosestudy)
        self.RunButton_3.clicked.connect(self.do_runstudy)
        self.scene = QtWidgets.QGraphicsScene(self)
        self.graphicsView.setScene(self.scene)
        w, h = input_img.size
        self.pixmap_item = self.scene.addPixmap(QtGui.QPixmap())
        # self.graphicsView.fitInView(QRectF(0, 0, w, h), Qt.KeepAspectRatio)
        self.graphicsView.update()
        self.plainTextEdit.update()
        self.level = 1
        self.enhancer = None
        self.timer = QtCore.QTimer(interval=500, timeout=self.on_timeout)
        sys.stdout = EmittingStream(textWritten=self.normalOutputWritten)

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

    @QtCore.pyqtSlot()
    def do_test(self):
        # input_img = Image.open("/home/ironmantis7x/Documents/Maverick_AI/Python/keras-covid-19/maverickAI30k.png")
        self.enhancer = ImageEnhance.Brightness(input_img)
        self.timer.start()
        self.ShowIButton.setDisabled(True)

    @QtCore.pyqtSlot()
    def on_timeout(self):
        if self.enhancer is not None:
            result_img = self.enhancer.enhance(self.level)
            qimage = ImageQt.ImageQt(result_img)
            self.pixmap_item.setPixmap(QtGui.QPixmap.fromImage(qimage))
        if self.level > 7:
            self.timer.stop()
            self.enhancer = None
            self.level = 0
            self.ShowIButton.setDisabled(False)
        self.level = 1
        self.ShowIButton.setDisabled(False)

    @QtCore.pyqtSlot()
    def do_choosestudy(self):
        dlg = QFileDialog()
        dlg.setFileMode(QFileDialog.AnyFile)
        if dlg.exec_():
            filenames = dlg.selectedFiles()
            f = open(filenames[0], 'r')

    @QtCore.pyqtSlot()
    def do_runstudy(self):
        os.system("df -h")
        # filetext = open('screenout.txt').read()
        # filetext.close()
        # textViewValue = self.plainTextEdit.toPlainText()
        # QPlainTextEdit.appendPlainText(self, str(textViewValue))
        # sys.stdout = self(textWritten=self.textWritten)
        self.normalOutputWritten(text_edit)

    def __del__(self):
        # Restore sys.stdout
        sys.stdout = sys.__stdout__

    def normalOutputWritten(self, text_edit):
        #cursor = self.plainTextEdit.textCursor()
        #cursor.movePosition(QtGui.QTextCursor.End)
        #cursor.insertText(text_edit)
        self.plainTextEdit.appendPlainText(text_edit)
        #self.plainTextEdit.ensureCursorVisible()

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

How can I make this work correctly?

Update 2

I indeed DID do research into the topic and this is one of the main resources I used to try to solve the issue before I posted my question: How to capture output of Python's interpreter and show in a Text widget?

Update 3

I have revised my code in the post to reflect code suggestions in the link I used to help me with my issue.

I am still unable to get this to run correctly. I now get this error:

self.plainTextEdit.appendPlainText(text_edit) TypeError: appendPlainText(self, str): argument 1 has unexpected type 'QPlainTextEdit'


Solution

  • I have a user interface, TableManagerWindow, that I've been maintaining and developing in Qt designer. After converting via pyuic to a *.py file, I was able to implement what Ferdinand Beyer had suggested in the link you provided above. Simple button to print text to terminal and it indeed does get appended to the QTextEdit widget via append(). Not sure this fits the bill for you for some reason, but I can vouch that it worked for me as well. I'm not savvy enough to get the nuance that is causing your issue, but figured I'd put this here just in case. Admins feel free to delete this if it's extraneous, but it works.

    import sys
    from PyQt5 import QtCore, QtGui, QtWidgets
    
    # Define a stream, custom class, that reports data written to it, with a Qt signal
    class EmittingStream(QtCore.QObject):
    
        textWritten = QtCore.pyqtSignal(str)
    
        def write(self, text):
            self.textWritten.emit(str(text))
    
    class Ui_TableManagerWindow(object):
        def setupUi(self, TableManagerWindow):
            #define all of my widgets, layout, etc here
            .
            .
            .
            # Install a custom output stream by connecting sys.stdout to instance of EmmittingStream.
            sys.stdout = EmittingStream(textWritten=self.output_terminal_written)
    
            # Create my signal/connections for custom method
            self.source_dir_button.clicked.connect(self.sourceDirButtonClicked)
    
            self.retranslateUi(TableManagerWindow)
            QtCore.QMetaObject.connectSlotsByName(TableManagerWindow)
    
    
        def retranslateUi(self, TableManagerWindow):
            .
            .
            .
    
        #custom method that prints to output terminal.  The point is to have this emmitted out to my QTextEdit widget.
        def sourceDirButtonClicked(self):
            for i in range(10):
                print("The Source DIR button has been clicked " + str(i) + " times")
    
        #custom method to write anything printed out to console/terminal to my QTextEdit widget via append function.
        def output_terminal_written(self, text):
            self.output_terminal_textEdit.append(text)
    
    if __name__ == "__main__":
        import sys
        app = QtWidgets.QApplication(sys.argv)
        TableManagerWindow = QtWidgets.QMainWindow()
        ui = Ui_TableManagerWindow()
        ui.setupUi(TableManagerWindow)
        TableManagerWindow.show()
        sys.exit(app.exec_())