sundar_ima
sundar_ima

Reputation: 3890

PyQt4 unable to call function of main Gui class from QThread class

Following is the sample code structure I am intending to implementing a larger time consuming operation. For doing larger operation, I have used QThread and updating progressbar (from main class) using the emited signal. All works fine un till large time consuming operation is completed. However, I run in to problem when I call a function from main GUI class. Here is the code structure I am trying (read the comments):-

import time
from scripts.gui import Ui_Dialog
from PyQt4 import QtGui
from PyQt4 import QtCore

class AppGui(QtGui.QDialog, Ui_Dialog):
    def __init__(self):
        QtGui.QDialog.__init__(self)
    # Main code here.
    # This GUI pops up for user input and opens a main GUI.

    def main_function(self):
        # Doing some main coding here.

        self.work_thread = WorkThread()
        self.work_thread.update.connect(self.ui.progressBar.setValue)
        self.work_thread.start()

        # I wanted to continue more coding here after the thread is finished. But self.work_thread.wait() is blocking main gui.
        # Therefore, I moved the continuation code to different function --> sub_function()

    def sub_function(self):

        # Do the remaining code left over from the main_function()


class WorkThread(QtCore.QThread):


    update = QtCore.pyqtSignal(int)

    def __init__(self):
        QtCore.QThread.__init__(self)

    def __del__(self):
        self.wait()

    def run(self):

        self.thread = GenericThread(scripts.function, arg1, arg2)  # This "scripts.function()" function does long process.
        self.thread.start()

        while self.thread.isRunning():
            # Do some long process.
            time.sleep(1)
            self.update.emit(signal)

        print "Distro extraction completed..."


        if self.thread.isFinished():
            self.main_class = AppGui()
            self.main_class.sub_function()  # <-- Problematic call to main AppGui function.

        if self.isFinished():

            return


class GenericThread(QtCore.QThread):

    def __init__(self, function, *args, **kwargs):
        QtCore.QThread.__init__(self)
        self.function = function
        self.args = args
        self.kwargs = kwargs

    def __del__(self):
        self.wait()

    def run(self):
        self.function(*self.args, **self.kwargs)
        return

This is what I got after running.

What I believe is that I am wrongly calling function of main AppGui() from WorkThread() class.

QPixmap: It is not safe to use pixmaps outside the GUI thread
Larger operation is complete...
QObject::installEventFilter(): Cannot filter events for objects in a different thread.
[xcb] Unknown request in queue while dequeuing
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
python2.7: ../../src/xcb_io.c:179: dequeue_pending_request: Assertion `!xcb_xlib_unknown_req_in_deq' failed.

Any help to solve this issue is appreciated.

Upvotes: 1

Views: 1195

Answers (1)

Cui Heng
Cui Heng

Reputation: 1285

The reason is that worker thread emit a signal, this signal could not directly bind to an UI slot, but you need to bind it to a general slot, then you call the UI slot to upgrade. As I don't have all your code, so I write a similar file like this, it works fine

from PyQt4.QtCore import *
from PyQt4.QtGui import *
import time


class WorkerThread(QThread):
    updateSignal = pyqtSignal(int)

    def run(self):
        count = 0
        while True:
            time.sleep(0.1)
            self.updateSignal.emit(count)
            count += 1


class ProgressBar(QProgressBar):
    def __init__(self, parent=None):
        super(ProgressBar, self).__init__(parent)
        self.worker = WorkerThread()
        self.worker.updateSignal.connect(self.updateProgress) # here should bind to a general slot

    def startWorker(self):
        self.worker.start()

    def updateProgress(self, progress):
        self.setValue(progress)


if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    p = ProgressBar()
    p.startWorker()
    p.show()
    app.exec_()

Upvotes: 1

Related Questions