Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Getting error messages from cmd window to plainTextEdit in QT



  • I would like to display error messages in cmd window to the plainTextEdit in QT. I am not sure on how to pass this error messages to plainTextEdit. I was ablt to pipe standard output to text window in GUI but unable to pipe error to the text window.

    Below you can find some modified snippet of the code

    from PyQt5 import QtCore, QtGui, QtWidgets
    from PyQt5.QtCore import QCoreApplication
    
    import sys
    import os
    
    # Piping the standard output (print/stdout command) to the GUI text window
    class StandardLog:
        def __init__(self, edit):
            self.out = sys.stdout
            self.textEdit = edit
    
        def write(self, message):
            # self.out.write(message)
            # self.textEdit.insertPlainText(message)
    
            self.textEdit.appendPlainText(message)
    
            self.textEdit.ensureCursorVisible()
    
            QCoreApplication.processEvents()
    
        def flush(self):
            self.out.flush()
    
    # Piping the standard error (stderr) to the GUI text window
    class ErrorLog:
        def __init__(self, edit):
            self.out = sys.stderr
            self.textEdit = edit
    
        def write(self, message):
            # self.out.write(message)
            # self.textEdit.insertPlainText(message)
            self.textEdit.appendPlainText(message)
            self.textEdit.ensureCursorVisible()
            QCoreApplication.processEvents()
    
        def flush(self):
            self.out.flush()
    
    class Ui_MainWindow(object):
        def setupUi(self, MainWindow):
            MainWindow.setObjectName("MainWindow")
            MainWindow.resize(888, 797)
            self.centralwidget = QtWidgets.QWidget(MainWindow)
            self.centralwidget.setObjectName("centralwidget")
            self.spinBox = QtWidgets.QSpinBox(self.centralwidget)
            self.spinBox.setGeometry(QtCore.QRect(60, 210, 42, 22))
            self.spinBox.setMaximum(10000)
            self.spinBox.setObjectName("spinBox")
            self.spinBox_2 = QtWidgets.QSpinBox(self.centralwidget)
            self.spinBox_2.setGeometry(QtCore.QRect(240, 210, 61, 22))
            self.spinBox_2.setMaximum(10000)
            self.spinBox_2.setObjectName("spinBox_2")
            self.checkBox = QtWidgets.QCheckBox(self.centralwidget)
            self.checkBox.setGeometry(QtCore.QRect(460, 220, 70, 17))
            self.checkBox.setObjectName("checkBox")
            self.checkBox_2 = QtWidgets.QCheckBox(self.centralwidget)
            self.checkBox_2.setGeometry(QtCore.QRect(670, 220, 70, 17))
            self.checkBox_2.setObjectName("checkBox_2")
            self.radioButton = QtWidgets.QRadioButton(self.centralwidget)
            self.radioButton.setGeometry(QtCore.QRect(400, 300, 82, 17))
            self.radioButton.setObjectName("radioButton")
            self.label = QtWidgets.QLabel(self.centralwidget)
            self.label.setGeometry(QtCore.QRect(60, 170, 151, 16))
            self.label.setObjectName("label")
            self.label_2 = QtWidgets.QLabel(self.centralwidget)
            self.label_2.setGeometry(QtCore.QRect(230, 150, 121, 71))
            self.label_2.setObjectName("label_2")
            self.label_3 = QtWidgets.QLabel(self.centralwidget)
            self.label_3.setGeometry(QtCore.QRect(460, 180, 91, 16))
            self.label_3.setObjectName("label_3")
            self.label_4 = QtWidgets.QLabel(self.centralwidget)
            self.label_4.setGeometry(QtCore.QRect(650, 180, 121, 16))
            self.label_4.setObjectName("label_4")
            self.textEdit = QtWidgets.QPlainTextEdit(self.centralwidget)
            self.textEdit.setGeometry(QtCore.QRect(30, 380, 811, 351))
            self.textEdit.setObjectName("textEdit")
            self.lineEdit_2 = QtWidgets.QLineEdit(self.centralwidget)
            self.lineEdit_2.setGeometry(QtCore.QRect(50, 80, 231, 51))
            self.lineEdit_2.setObjectName("lineEdit_2")
            self.lineEdit_3 = QtWidgets.QLineEdit(self.centralwidget)
            self.lineEdit_3.setGeometry(QtCore.QRect(382, 79, 201, 51))
            self.lineEdit_3.setObjectName("lineEdit_3")
            self.spinBox_3 = QtWidgets.QSpinBox(self.centralwidget)
            self.spinBox_3.setGeometry(QtCore.QRect(700, 80, 42, 22))
            self.spinBox_3.setObjectName("spinBox_3")
            self.label_5 = QtWidgets.QLabel(self.centralwidget)
            self.label_5.setGeometry(QtCore.QRect(70, 40, 47, 13))
            self.label_5.setObjectName("label_5")
            self.label_6 = QtWidgets.QLabel(self.centralwidget)
            self.label_6.setGeometry(QtCore.QRect(380, 30, 121, 16))
            self.label_6.setObjectName("label_6")
            self.label_7 = QtWidgets.QLabel(self.centralwidget)
            self.label_7.setGeometry(QtCore.QRect(690, 30, 47, 13))
            self.label_7.setObjectName("label_7")
            MainWindow.setCentralWidget(self.centralwidget)
            self.menubar = QtWidgets.QMenuBar(MainWindow)
            self.menubar.setGeometry(QtCore.QRect(0, 0, 888, 21))
            self.menubar.setObjectName("menubar")
            MainWindow.setMenuBar(self.menubar)
            self.statusbar = QtWidgets.QStatusBar(MainWindow)
            self.statusbar.setObjectName("statusbar")
            MainWindow.setStatusBar(self.statusbar)
            self.output = StandardLog(self.textEdit)
            self.error = ErrorLog(self.plainTextEdit)
    
            self.retranslateUi(MainWindow)
            QtCore.QMetaObject.connectSlotsByName(MainWindow)
    
        def retranslateUi(self, MainWindow):
            _translate = QtCore.QCoreApplication.translate
            MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
            self.checkBox.setText(_translate("MainWindow", "CheckBox"))
            self.checkBox_2.setText(_translate("MainWindow", "CheckBox"))
            self.radioButton.setText(_translate("MainWindow", "process"))
            self.label.setText(_translate("MainWindow", "Iterations for xxxxx"))
            self.label_2.setText(_translate("MainWindow", "Iteration for yyyyy"))
            self.label_3.setText(_translate("MainWindow", "Enable Output"))
            self.label_4.setText(_translate("MainWindow", "Enable zzzzz ))
            self.label_5.setText(_translate("MainWindow", "ip"))
            self.label_6.setText(_translate("MainWindow", "ip2"))
            self.label_7.setText(_translate("MainWindow", "idx"))
    
        def spin(self):
            #print("Selected Number of Iterations")
            self.output.write(str(self.spinBox.value()))
            print(self.spinBox.value())
    
            
        def spin2(self):
            self.output.write(str(self.spinBox_2.value()))
            print(self.spinBox_2.value())
            
        def printVal(self):
            self.output.write(str(self.checkBox.checkStateSet()))
            print(self.checkBox.checkStateSet())
            
        def printVal2(self):
            self.output.write(str(self.checkBox_2.checkStateSet()))
            print(self.checkBox_2.checkStateSet())
            
        def printip(self):
            self.output.write(str(self.lineEdit_2.text()))
            #print(self.lineEdit_2.text())
    
        def printip_1(self):
            self.output.write(str(self.lineEdit_3.text()))
            #print(self.lineEdit_2.text())
            
        def run_calib(self):
            #print("Process Started")
            os.system("process.exe 172.188.1.10 172.168.1.8 1")
            
            
    window = Ui_MainWindow()
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    window.setupUi(MainWindow)
    MainWindow.show()
    
    window.spinBox.valueChanged.connect(window.spin)
    window.spinBox_2.valueChanged.connect(window.spin2)
    window.checkBox.stateChanged.connect(window.printVal)
    window.checkBox_2.stateChanged.connect(window.printVal2)
    window.lineEdit_2.editingFinished.connect(window.printip)
    window.lineEdit_3.editingFinished.connect(window.printip_1)
    
       
    sys.exit(app.exec_())
    

    Please ignore namings as i purposefully changed all the names. The main idea is here i have one executable which i made by using c++ however i wanted to add few user friendly modifications for example no of iterations and so on.. so i thought i will create one simple gui and was able to do most part of it but stuck at this piping Stderr messages to qt.

    However, I am bit confused to get this error messages into the plainTextEdit box in GUI. For example if there is no file named process.exe it throws process.exe is not there in cmd but i would like to throw the same message in my GUI.

    Thanks in Advance :-)

    P.S I already tried using ErrorLog class above inside the run_calib function but it doesnt do what i was expecting. This part is not reflected in the code



  • @sm2770s
    You are getting confused/trying to do things which won't work. Like

    self.out = sys.stderr
    self.textEdit = edit
    

    If you want to stick with Python os.system() to run external commands then it's a Python question and you'd have to look up how to capture its output. [I think you would need to look at subprocess module.]

    My recommendation/suggestion would be to do OS commands from a Qt UI app via Qt's QProcess module. Then you can use its way of accessing stdout/stderr, as well as signals/slots/asynchronicity, and still be able to ask about it in this forum. You will have to read through that topic and look at examples, because it's a bit much to explain.



  • @JonB May be you are right! I will go through QProcess Module and try to access stdout and stderr and maybe others too.



  • @sm2770s
    I can't give you all my code from an old Python (PyQt5) program I wrote because it's too large. It runs an arbitrary sub-process from a dialog, capturing all its output and displaying to user as it goes along. But here's an extract which is the guts of it, that may help you get going. If you can understand this and fill in the missing bits you will be good.

        def runProcessShowOutput(self, program: str, args: str, extraEnv: [(str, str)]=None):
            # initialise the exit code
            self.exitCode = -1
            # create the process object
            # start the process
            # display the modal dialog
            # program: string for the executable to run (*not* quoted)
            # args: string of all command-line arguments (should be already quoted as required)
            # extraEnv: optional array of [name, value] for any extra environment variables to be passed to process
            self.process = QProcess()
            # Set process that anything which goes to stderr is merged into stdout
            self.process.setProcessChannelMode(QProcess.MergedChannels)
            # Connect process signals
            try:
                # Qt 5.6+
                self.process.errorOccurred.connect(lambda error: self.processErrorOccurred(error))
            except AttributeError:
                pass
            self.process.started.connect(self.processStarted)
            self.process.finished.connect(lambda exitCode, exitStatus: self.processFinished(exitCode, exitStatus))
            self.process.readyReadStandardOutput.connect(self.processReadyReadStandardOutput)
            # Add any additional environment variables to process
            if extraEnv and len(extraEnv):
                env = self.process.processEnvironment()
                for (name, value) in extraEnv:
                    env.insert(name, value)
                self.process.setProcessEnvironment(env)
            # Start the process
            self.process.start("\"{}\" {}".format(program, args))
            # **NOTE**
            # Don't do it via a single string of arguments, already quoted, as it was at the time I wrote this on the line above
            # Pass into this method a *list* of (unquoted) arguments now
            # and use the `self.process.start(command, arguments)` overload
    
            # Show the dialog modal
            self.exec()
    

    Don't worry about the "environment" stuff or the modal dialog.

    And here are a couple of the support methods:

        def processReadyReadStandardOutput(self):
            # read all output available at this point
            byteArray = self.process.readAllStandardOutput()
            # convert QByteArray to str
            # Linux: the decoder should always be "utf-8"
            # Windows: after *enormous* investigations "utf-8" *mostly* works
            #          but if the output contains a "funny" character like "£"
            #          it will cause a conversion error
            #          then the correct decoder to use is what the command "chcp" says
            #          (e.g. "cp850" for Code Page 850 in UK) to avoid the error *and* correctly display the £
            text = ""
            try:
                try:
                    text = byteArray.data().decode('utf-8')
                except UnicodeDecodeError:
                    from common import osfunctions
                    if osfunctions.isWindows():
                        text = byteArray.data().decode('cp850')
            except:
                # if all this fails for whatever reason (just in case)
                # just output a load of "?"s the length of the output
                # anything is better than throwing an exception here
                text = "?" * len(byteArray)
            self.appendInformativeText(text)
    
        def processFinished(self, exitCode: int, exitStatus: QProcess.ExitStatus):
            self.process = None
            output = "\nCommand exit code: {}\n".format(exitCode)
            self.appendInformativeText(output)
            self.exitCode = exitCode
            if exitCode == 0:
                text = "Command ran successfully"
            else:
                text = "Command ran, but with non-zero exit code"
            self.setText(text)
            self.disableTerminate()
    


  • @JonB Thanks Jon. SubProcess Module helped me to fix that. Thanks for the Quick help. For anyone who need in future.

    By way of a fairly minimal demo, here's a quick terminal emulator that takes a command, executes it and writes the stdout and stderr to a QTextEdit in green and red respectively:
    Special Mention to vrrorx from reddit. This part is copied from her code from reddit.

    import sys
    import subprocess as sp
    import PyQt5.QtWidgets as qtw
    import PyQt5.QtCore as qtc
    import PyQt5.QtGui as qtg
    
    
    class Demo(qtw.QWidget):
        def __init__(self):
            super().__init__()
            self.ui()
            self.show()
    
        def ui(self):
            layout = qtw.QVBoxLayout()
            self.setLayout(layout)
    
            self.text = qtw.QTextEdit()
            self.text.setReadOnly(True)
            layout.addWidget(self.text)
    
            self.command = qtw.QLineEdit()
            self.command.setPlaceholderText("Enter command...")
            layout.addWidget(self.command)
    
            self.button = qtw.QPushButton("Execute")
            self.button.clicked.connect(self.execute)
            layout.addWidget(self.button)
    
        def execute(self):
            command = self.command.text()
            self.command.clear()
            self.text.setTextColor(qtg.QColor(0, 0, 0))  # Black
            self.text.append(f"$ {command}")
    
            p = sp.run(command, capture_output=True, text=True, shell=True)
    
            if p.stdout:
                self.text.setTextColor(qtg.QColor(0, 128, 0))  # Green
                self.text.append(p.stdout)
    
            if p.stderr:
                self.text.setTextColor(qtg.QColor(255, 0, 0))  # Red
                self.text.append(p.stderr)
    
    
    if __name__ == "__main__":
        app = qtw.QApplication(sys.argv)
        demo = Demo()
        sys.exit(app.exec_())
    

  • Lifetime Qt Champion

    Hi,

    @sm2770s said in Getting error messages from cmd window to plainTextEdit in QT:

    Special Mention to vrrorx from reddit. This part is copied from her code from reddit.

    In that case it would be nice to provide a link to the relevant material.




Log in to reply