-1

I found this solution, but it prints after function ends. When i press button_collect_data i'm calling print_sleep function. I expected that it would print each value after 1 sec sleep but it prints all after 3 second.

Main point that i want to see function progress, so

Question: What is the way to redirect stdout to some text widget in QT app to show every print immediately?

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        ...
        self.button_collect_data = QtWidgets.QPushButton(self.centralwidget)
        self.button_collect_data.setMaximumSize(QtCore.QSize(399, 16777215))
        self.button_collect_data.setObjectName("button_collect_data")
        ...
        self.console = QtWidgets.QTextEdit(self.centralwidget)
        self.console.setReadOnly(True)
        self.console.setObjectName("console")

        sys.stdout = EmittingStream(textWritten=self.normalOutputWritten)
        self.add_functions()

    def add_functions(self):
        self.button_collect_data.clicked.connect(lambda: QtCore.QProcess(self.print_sleep()))

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

    def print_sleep(self):
        for i in range(3):
            print(i)
            time.sleep(1)


class EmittingStream(QtCore.QObject):
    textWritten = QtCore.pyqtSignal(str)

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


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())
555Russich
  • 354
  • 2
  • 10
  • In your `print_sleep()` function, try changing `print(i)` to `print(i, flush=True)`. It may do the trick. – MattDMo Sep 01 '22 at 22:24
  • @MattDMo Getting `AttributeError: 'EmittingStream' object has no attribute 'flush'` – 555Russich Sep 01 '22 at 22:26
  • 1
    code may sends text to widget but it may wait for end of your function to redraw window and display new text - this way it can redraw all changes in one moment and window will not flicker. You may have to force it to redraw window. See idea of `app.processEvents()` in question for `PyQt4 but maybe it works also for PyQt5: [python - Label in PyQt4 GUI not updating with every loop of FOR loop - Stack Overflow](https://stackoverflow.com/questions/2482437/label-in-pyqt4-gui-not-updating-with-every-loop-of-for-loop) – furas Sep 02 '22 at 10:44
  • as for error `EmittingStream' object has no attribute 'flush'` - I think it may need to create `def flush(self)` in `class EmittingStream`. And eventually you could use `app.processEvents()` in this function. – furas Sep 02 '22 at 10:46
  • @555Russich You're not using QProcess: the lambda is executed in the main thread, it calls `print_sleep()` which is *blocking* (so the whole UI will become unresponsive until it ends) and returns `None`, then creates a new instance of QProcess that is immediately garbage collected. Besides, it seems that you're trying to do something similar to python's `multiprocessing.Process`, and that's ***not*** the purpose of [QProcess](//doc.qt.io/qt-5/qprocess.html), which instead "is used to **start external programs** and to communicate with them". Please clarify what you're actually trying to do. – musicamante Sep 02 '22 at 14:27
  • @furas you are right about `app.processEvents()`. If i add it in loop, prints going step by step! But in real case i have pretty big function for scrapping from another file instead of just `print_sleep`. I mean that this scrap-func have a lot of prints inside which i want add to text widget to see progress redirecting stdout or something like that... – 555Russich Sep 03 '22 at 23:05

1 Answers1

-1

I found solution which using QThread. Described here

For me it's working (but i don't know if it's the best) because in real case function is calling after pressing button from another module and i don't want change it somehow.

btw thanks @furas for idea of app.processEvents(). It's answering question, but not working in all cases. Especially with my code which i put in other question related with same app

555Russich
  • 354
  • 2
  • 10