Stdout stops showing in QTextEdit once another win

2019-09-01 15:22发布

问题:

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_())
"""

回答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) ---
    ...