2

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'

halfer
  • 19,824
  • 17
  • 99
  • 186
ironmantis7x
  • 807
  • 2
  • 23
  • 58
  • 1
    Why are you trying to inherit from QtCore? (or from both QMainWindow and QLineEdit for that matter) Try something like `class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow)` – Heike Apr 10 '20 at 16:29
  • Thank you @Heike. The console output is still not showing in the PlainTextEdit widget. Any ideas? I tried your suggestion. – ironmantis7x Apr 10 '20 at 16:37
  • @ironmantis7x What is the last command in `do_runstudy` supposed to do? calling `self()` doesn't make much sense... If you want to use the solution given for the link you provided, then follow it, don't try to "improvise". – musicamante Apr 10 '20 at 17:40
  • @musicamante I tried really hard not to improvise. However my application looks slightly different from the example in the link. I tried to replicate the solution in the link exactly and I kept getting errors. I fixed the errors one at a time until the resulting code above was produced. Can someone help me with actual coding steps with explained steps? I am new to this. – ironmantis7x Apr 10 '20 at 18:38
  • You're not replicating that solution as you should. That example creates a new instance of the `EmittingStream` class, but you're trying to avoid it by "calling" `self()`, which will never work: `self` is an instance object, and it's not callable. When you don't exactly understand what some code does (and let's be clear, there's nothing wrong with not understanding something) you should try to reproduce it exactly as it is before trying to change it. Start by using the EmittingStream class as the example does. – musicamante Apr 10 '20 at 18:54
  • @musicamante I have tried that. My app already has a MainWIndow class and I am trying to make this work for the application I have written. I have tried according to the example. I am truly stuck – ironmantis7x Apr 10 '20 at 20:26
  • @ironmantis7x Are you using the EmittingStream class as in the other answer? Also, you really don't need to use a QTextCursor to add text to a QPlainTextEdit, just use [`appendPlainText()`](https://doc.qt.io/qt-5/qplaintextedit.html#appendPlainText) instead. – musicamante Apr 11 '20 at 00:02
  • @musicamante Yes I did use the EmittingStream class in the other answer but it still is not working correctly. I have updated my code. – ironmantis7x Apr 11 '20 at 02:41
  • First of all, that `text_edit` at the beginning really doesn't make absolutely **ANY** sense. Remove that. Then, remove that completely unnecessary `self.normalOutputWritten(text_edit)` line. – musicamante Apr 11 '20 at 02:52
  • thanks @musicamante. I removed those two lines. COde still doesn't display the console output to the plainTextEdit widget – ironmantis7x Apr 11 '20 at 03:23
  • It's probably because you're using `os.system`. Anyway I really don't understand the need of `sys.stdout` (which could potentially send *anything* to the text edit, even completely unrelated text). If you want to read the output of a command, use QProcess. I appreciate your efforts for the objective, but you're not really helping a lot here: your code has lots of unnecessary parts (you should always keep examples [not only reproducible, but also **minimal**](https://stackoverflow.com/help/minimal-reproducible-example)) and you also seem very confused about many aspects of both PyQt and Python. – musicamante Apr 13 '20 at 23:34
  • thanks for your comment @musicamante. I will let the bounty run out and then delete my question. Thanks. – ironmantis7x Apr 14 '20 at 13:38
  • Addressing specifically to update 3 - I read the error as self is not the correct type, possibly because of the double inheritance. Try refactoring the code outside of a class. Don't get discouraged you can do it. – Christopher Hoffman Apr 17 '20 at 19:35

1 Answers1

2

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_())
  • thanks for the input @konstantine. It didn't help me though. You did provide an answer with code so I want to let you know I appreciate your effort to help. I will be deleting the question. I am quite discouraged at this point especially by some of the negative response some people gave me. – ironmantis7x Apr 20 '20 at 14:20