mad5245
mad5245

Reputation: 394

How can I print to QTextEdit to mimic printing to console? (Python 3.3)

I have a piece of code that displays a Gui which has a QTextEdit Field. I would like to print to this field in real time similar to how the print function outputs to the console.

I have tried using multiple instances of the append function. Ex:

self.textEdit.append(_translate("MainWindow", ">>> Text", None))

The problem is that no matter where they are in the code, they seem to only show after the program is executed. My goal is to have them show in line like the print function does on the console.

I feel like this is an easy answer, but I have had no luck searching.. I am fairly new to Python and any help or guidance will be appreciated.

Thanks in advance!

Upvotes: 0

Views: 2722

Answers (2)

insys
insys

Reputation: 1288

Indeed, as mata mentioned the freezing comes from doing all your work in the same (main) thread, which also handles UI updates. One way to solve your responsiveness issue is indeed to frequently use QApplication.processEvents() in your blocking code. That will give the impression of a responsive GUI to the user if frequent enough.

However, using threads in Python (whether native or QThread) will not always work. That is because of the existence of the Global Interpreter Lock (GIL, the wiki has a good short intro). In short, Python does not allow more than one thread to execute code at the same time.

If your background task is light, or is based on IO, you can get around this as most IO-heavy modules for Python release the GIL while doing their job. However, if you are performing heavy computations in Python, the GIL will be locked by your processes, and as such your UI will still be unresponsive.

Consider the following example, built using PySide:

import sys, random
from threading import Thread
from time import sleep
from urllib import urlopen
from PySide import QtCore, QtGui

class Window(QtGui.QMainWindow):
    update_signal = QtCore.Signal(int)
    def __init__(self):
        QtGui.QMainWindow.__init__(self)
        self.progress_bar = QtGui.QProgressBar(self)
        self.progress_bar.setRange(0, 10)
        self.setCentralWidget(self.progress_bar)
        self.update_signal[int].connect(self.progress_bar.setValue)
        self.show()
        self.t = Thread(target=self.worker)
        self.t.start()

    def worker(self):
        while self.progress_bar.value() < 10:
            self.update_signal.emit(self.progress_bar.value()+1)
            print "Starting Sleep"
            sleep(5)
            print "End of Sleep"

if __name__ == '__main__':
    qapp = QtGui.QApplication(sys.argv)
    win = Window()
    sys.exit(qapp.exec_())

Then, try replacing the worker function to:

def worker(self):
    while self.progress_bar.value() < 10:
        self.update_signal.emit(self.progress_bar.value()+1)
        v = 0
        print "Starting Add"
        for i in xrange(5000000):
            v = v+random.uniform(0, 100)
        print "End of Add"

The first case maintains a responsive UI, as the call to sleep() releases the GIL. But the second example does not, as the computationally-intense algorithm keeps the lock.

One solution could be using the multiprocessing package. From the docs:

multiprocessing is a package that supports spawning processes using an API similar to the threading module. The multiprocessing package offers both local and remote concurrency, effectively side-stepping the Global Interpreter Lock by using subprocesses instead of threads. Due to this, the multiprocessing module allows the programmer to fully leverage multiple processors on a given machine.

A simple, illustrative example of using python multipocessing.

Also, if you have further interest, this blog post about multi-processing techniques might be of interest.

Upvotes: 2

mata
mata

Reputation: 69012

This means that you're probably doing all your work in the GUI thread. This is a common mistake, and it means that the GUI will freeze and not respond while there is something else going on.

You can add a call to QApplication.processEvents() to allow for the GUI to update after you change the text in your QTextEdit, but that will only partially solve the problem, the GUI will nevertheless freeze up between those calls.

The solution is simple: do the work in a separate thread. You should read Threading Basics from the Qt documentation, that should get you started.

Upvotes: 2

Related Questions