Nikolai Golub
Nikolai Golub

Reputation: 3567

Concurrent to QLabel in PyQt

I have i python application, which uses PyQt GUI. It application has some I/O operations, which i want to run in separate threads. When each thread started, it should write messages to applications main window status bar and when last thread is completed, status bar messages should be cleared. How can i handle number of threads via QThread?

Here is example of code:

import sys, time
from PyQt4 import QtCore, QtGui
from functools import partial


def io_emulator(sleep_seconds):
    print 'We will sleep for %s seconds' % str(sleep_seconds)
    time.sleep(sleep_seconds)


class IOThread(QtCore.QThread):
    def __init__(self, func):
        QtCore.QThread.__init__(self)
        self.io_func = func

    def run(self):
        self.io_func()


class m_Window(QtGui.QWidget):
    def __init__(self):
        super(m_Window, self).__init__()

        self.initUI()

    def initUI(self):
        self.thread_button = QtGui.QPushButton("Thread", self)
        self.thread_button.move(30, 10)
        self.spinbox = QtGui.QSpinBox(self)
        self.spinbox.move(30, 50)
        self.stat_label = QtGui.QLabel("", self)
        self.stat_label.setGeometry(QtCore.QRect(200, 200, 150, 14))
        self.stat_label.move(30,90)

        self.setWindowTitle('Threads')
        self.show()

        self.thread_button.clicked.connect(self._sleeper)

    def _sleeper(self):
        seconds = int(self.spinbox.text())
        stat_str = 'Sleeping %s seconds' % str(seconds)

        io_func = partial(io_emulator, seconds)
        set_status_f = partial(self.set_status_msg, stat_str)

        self.thread =  IOThread(io_func)
        self.thread.started.connect(set_status_f)
        self.thread.finished.connect(self.clear_status_msg)

        self.thread.start()

    def set_status_msg(self, msg):
        self.stat_label.setText(msg)

    def clear_status_msg(self):
        self.stat_label.clear()


def main():
    app = QtGui.QApplication(sys.argv)
    m = m_Window()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

I want, that message is being cleared only when last thread is ended.

Upvotes: 2

Views: 638

Answers (1)

jdi
jdi

Reputation: 92569

You cannot call QWidget functions from any thread but the main thread. If you want another thread to trigger GUI operations, then they should be communicated by emitting signals that are connected to your main thread:

def someFunc():
    return "message"

class IOThread(QtCore.QThread):

    statusUpdate = QtCore.pyqtSignal(str)

    def __init__(self, func):
        QtCore.QThread.__init__(self)
        self.io_func = func

    def run(self):
        msg = self.io_func()
        self.statusUpdate.emit(msg)

Then in your main thread, connect the io thread to the status label or some intermediate handler:

io_thread = IOThread(someFunc)
io_thread.statusUpdate.connect(self.status_label.setText)

This will create a queued connection, which places the call into the event loop of the main thread for execution.

Upvotes: 1

Related Questions