0

I am trying to get stdout and error messages to show on my main window. The window is by pyqt and made via designer. I have a QTextEdit on it. This is where the output should show itself. Also I have a dialog box (again made via designer) where i set some settings for my program before running it. The dialog box is opened like this:

def open_settings(self):
    dialog = SettingsDialog()
    dialog.open_settings_tab()  # its on a tab widget

I already read and used the info on these links to achieve my goal:

Print out python console output to Qtextedit

How to capture output of Python's interpreter and show in a Text widget?

Both are pretty much the same with different object names. The issue I'm having is that whenever I open a dialog box and return to the main window the stdout no longer shows itself on the QTextEdit. Instead it goes back to showing itself on Sublime Editor.

I believe it has something to do with the class instancing.

Here is how the Dialog class starts:

class SettingsDialog(QDialog):
    def __init__(self, parent=None):
        super(SettingsDialog, self).__init__(parent)
        self.ui = Ui_SettingsDialog()
        self.ui.setupUi(self)

and finally here is how my main window (form) class starts:

class MyForm(QMainWindow):
    def __init__(self, parent=None):
        super(MyForm, self).__init__(parent)

        # Install the custom output stream
        sys.stdout = EmittingStream(textWritten=self.normalOutputWritten)

        self.ui = Ui_MyForm()
        self.ui.setupUi(self)

Any ideas of why the stdout stops working (in qtextedit) once i go into the dialog screen and come back?

NEW Update: The code is very long. I made a small program thats showing the issue:

PS: I found that the problem is related with this line shown below:

self.ui.pushButton_path.clicked.connect(Form(self).change_path)

if i comment it out the problem goes away.. But I need to call that function (which opens a QDialog, from the main form). What is the proper way?

main:

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QDialog
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtGui import QTextCursor

from ui_form import Ui_Form
from ui_dialog import Ui_Dialog


class EmittingStream(QObject):  # test
    textWritten = pyqtSignal(str)

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


class Form(QMainWindow):

    def __init__(self, parent=None):
        super(Form, self).__init__(parent)

        # Install the custom output stream
        sys.stdout = EmittingStream(textWritten=self.normalOutputWritten)  # test

        self.ui = Ui_Form()
        self.ui.setupUi(self)

        self.ui.pushButton_open.clicked.connect(self.open_dialog)
        self.ui.pushButton_text.clicked.connect(self.test_write)

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

    def normalOutputWritten(self, text):  # test
        """Append text to the QTextEdit."""
        # Maybe QTextEdit.append() works as well, but this is how I do it:
        # self.ui.tEdit_cli.insertPlainText(text)
        cursor = self.ui.textEdit.textCursor()
        cursor.movePosition(QTextCursor.End)
        cursor.insertText(text)
        self.ui.textEdit.setTextCursor(cursor)
        self.ui.textEdit.ensureCursorVisible()

    def open_dialog(self):
        dialog = Dialog()
        dialog.open_tab()

    def test_write(self):
        print("something written")

    def change_path(self):
        pass


class Dialog(QDialog):
    def __init__(self, parent=None):
        super(Dialog, self).__init__(parent)
        self.ui = Ui_Dialog()
        self.ui.setupUi(self)

        self.ui.pushButton_close.clicked.connect(self.close_dialog)

        self.ui.pushButton_path.clicked.connect(Form(self).change_path)  # this is what causes the issue. but i need to use it!

    def open_tab(self):
        self.ui.tabWidget.setCurrentIndex(0)
        self.exec_()

    def close_dialog(self):
        self.close()


def main():
    app = QApplication(sys.argv)
    form = Form()
    form.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

ui_dialog:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'dialog.ui'
#
# Created by: PyQt5 UI code generator 5.6
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(400, 300)
        self.horizontalLayout = QtWidgets.QHBoxLayout(Dialog)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.tabWidget = QtWidgets.QTabWidget(Dialog)
        self.tabWidget.setObjectName("tabWidget")
        self.tab = QtWidgets.QWidget()
        self.tab.setObjectName("tab")
        self.pushButton_close = QtWidgets.QPushButton(self.tab)
        self.pushButton_close.setGeometry(QtCore.QRect(100, 80, 211, 131))
        self.pushButton_close.setObjectName("pushButton_close")
        self.pushButton_path = QtWidgets.QPushButton(self.tab)
        self.pushButton_path.setGeometry(QtCore.QRect(30, 30, 75, 23))
        self.pushButton_path.setObjectName("pushButton_path")
        self.tabWidget.addTab(self.tab, "")
        self.tab_2 = QtWidgets.QWidget()
        self.tab_2.setObjectName("tab_2")
        self.tabWidget.addTab(self.tab_2, "")
        self.horizontalLayout.addWidget(self.tabWidget)

        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
        self.pushButton_close.setText(_translate("Dialog", "close"))
        self.pushButton_path.setText(_translate("Dialog", "path"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("Dialog", "Tab 1"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("Dialog", "Tab 2"))


"""
if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Dialog = QtWidgets.QDialog()
    ui = Ui_Dialog()
    ui.setupUi(Dialog)
    Dialog.show()
    sys.exit(app.exec_())
"""

ui_form:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'form.ui'
#
# Created by: PyQt5 UI code generator 5.6
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(Form)
        self.centralwidget.setObjectName("centralwidget")
        self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
        self.textEdit.setGeometry(QtCore.QRect(90, 230, 601, 271))
        self.textEdit.setObjectName("textEdit")
        self.pushButton_open = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_open.setGeometry(QtCore.QRect(140, 80, 241, 81))
        self.pushButton_open.setObjectName("pushButton_open")
        self.pushButton_text = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_text.setGeometry(QtCore.QRect(440, 80, 251, 81))
        self.pushButton_text.setObjectName("pushButton_text")
        Form.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(Form)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
        self.menubar.setObjectName("menubar")
        Form.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(Form)
        self.statusbar.setObjectName("statusbar")
        Form.setStatusBar(self.statusbar)

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "MainWindow"))
        self.pushButton_open.setText(_translate("Form", "open dialog"))
        self.pushButton_text.setText(_translate("Form", "write somthing"))


"""
if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Form = QtWidgets.QMainWindow()
    ui = Ui_Form()
    ui.setupUi(Form)
    Form.show()
    sys.exit(app.exec_())
"""
shafuq
  • 465
  • 6
  • 20
  • provide a [mcve] – eyllanesc Apr 22 '18 at 12:16
  • I added what seems to be relevant. (the code is thousands of lines) – shafuq Apr 22 '18 at 12:32
  • what is `Ui_MyForm`? Do you understand what it means verifiable? your project can have millions of lines, we are not interested in your project, we are interested in the specific problem. and for that we must be able to reproduce it. – eyllanesc Apr 22 '18 at 12:36
  • it has over 500 lines in it, all regarding the gui objects.. – shafuq Apr 22 '18 at 12:40
  • Show that code in your project, the errors can be caused by the design of the ui, take time and provide a reproducible code. – eyllanesc Apr 22 '18 at 12:40
  • **Create** a [mcve], that is, a different project to your original project that focuses on the functionality that brings you problems. – eyllanesc Apr 22 '18 at 12:41
  • I wrote a new program, using the basic components.. but its working there! I have no clue now :( – shafuq Apr 22 '18 at 13:28
  • wait a moment.. i found the line thats causing all this trouble. in the program (the dialog code) there is a line: self.ui.my_path.clicked.connect(MyForm(self).change_path) if i comment this line out it works without an issue. I use this line for calling the path from the main form. I'll try to make a copy of this on the new small program. If I can I will share it.. – shafuq Apr 22 '18 at 13:34
  • I updated the question now. The example shows where the problem is. I use the line mentioned there to open a dialog page for saving the path folder location and storing it in a global variable within the Main Form. This isn't mantioned above but might be handy to know why I need it. @eyllanesc – shafuq Apr 22 '18 at 13:48

1 Answers1

1

You must use the object to invoke a method, you must not use the class, so the instruction Form(self) is not valid.

You must make the connection where you can access the signal and the slot simultaneously, for example open_dialog would be a good place:

class Form(QMainWindow):
    ...
    def open_dialog(self):
        dialog = Dialog(self)
        dialog.ui.pushButton_path.clicked.connect(self.change_path) # +++
        dialog.open_tab()

    def test_write(self):
        print("something written")

    def change_path(self):
        pass


class Dialog(QDialog):
    def __init__(self, parent=None):
        super(Dialog, self).__init__(parent)
        self.ui = Ui_Dialog()
        self.ui.setupUi(self)
        self.ui.pushButton_close.clicked.connect(self.close_dialog)
        # self.ui.pushButton_path.clicked.connect(Form(self).change_path) ---
    ...
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Thank you @eyllanesc . I removed the line and added it under the function you mentioned. The only (minor) issue is that since my tab widget has 4 functions like that I have to add the same line over and over in each one. def open_tab1(self):, def open_tab2(self):, def open_tab3(self):, def open_tab4(self): all have that same line in them. But nevertheless your answer is great. Thanks again! – shafuq Apr 22 '18 at 14:36