Python Forum
[PyGUI] Failed when communicating between threads
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[PyGUI] Failed when communicating between threads
#1
Hi everybody,

I am trying to display an image on the window, but when I try it, I got this error:
"QPixmap: It is not safe to use pixmaps outside the GUI thread"

As you can see I am using two threads: one for the GUI and another one of the APP logic. The new image path file is generated in the app logic thread which should be sent to the GUI thread.
For this reason, I decided to use the Signal & slots communication mechanism.

I put the basic logic of my still-to-finish solution into small pieces to illustrate it, so you can run it and give me a hand if you have time.

The program basically shows an internal value for each thread every x seconds, and every time it is changed on LOGIC class it should be changed on GUI class too - just an example of what I would like to do with the final widget that can be only changed form its own thread:

import threading
from PyQt4.QtCore import QObject, SIGNAL
import time
import random



class LOGIC (threading.Thread, QObject):

        def __init__(self):
                QObject.__init__(self)
                threading.Thread.__init__(self)
                self.id = "LOG"
                self.value = 0

        def run(self):
                while True:
                        self.value = random.randint(1, 10)
                        print ("[" + self.id + "] value = " + str(self.value))
                        self.on_value_changed(self.value)
                        time.sleep(4)

        def on_value_changed(self, value):
                print ("[" + self.id + "] Running on_value_changed()")
                self.emit(SIGNAL('changeThisPlease(int)'), int(self.value))



class GUI (threading.Thread, QObject):

        def __init__(self):
                QObject.__init__(self)
                threading.Thread.__init__(self)
                self.id = "GUI"
                self.value = 0

        def run(self):
                while True:
                        print ("[" + self.id + "] value = " + str(self.value))
                        time.sleep(1)

        def on_GUI_changeValue(self, value):
                print ("[" + self.id + "] Running on_GUI_changeValue()")
                self.value = int(value)


if __name__ == '__main__':

        myLogic = LOGIC()
        myLogic.start()

        myGui = GUI()
        myGui.start()

        myLogic.connect(myLogic, SIGNAL("changeThisPlease(int)"), myGui.on_GUI_changeValue)
The GUI thread always shows the initial value, so it's never changed by the signal emitted by LOGIC, what am I doing wrong?
Could you please give me a hand?

Thanks so much for reading
Reply
#2
I don't think signals works between two threads, they only work between main GUI and other threads.

on_GUI_changeValue function is never called as thread.start() will only execute run method.

To communicate between threads you can use global variables or queues.

(Sep-11-2017, 11:10 AM)pythonisse Wrote: "QPixmap: It is not safe to use pixmaps outside the GUI thread"

What they mean by GUI thread is the main thread that is started with .show() method.
Reply
#3
Thanks for taking the time to post your answer.

Precisely I kept trying and finally I sorted it out by using Queue().

It works like a charm now!

Just a bit frustrating I couldn't make it work with signals, but maybe it is just impossible with that approach

Regards
Reply
#4
I think the move to thread approach is usually recommended for PyQt, such as:
#!/usr/bin/python3
import sys
import time
from PyQt5 import QtGui, QtWidgets
from PyQt5.QtCore import QThread, QObject, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QSystemTrayIcon

class workerExample(QObject):
    signalExample = pyqtSignal(str, int)

    def __init__(self):
        super().__init__()

    @pyqtSlot()
    def loop(self):
        while True:
            #Do your work here...
            self.signalExample.emit("leet", 1337)
            time.sleep(5)

class guiExample(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.worker = workerExample()
        self.workerThread = QThread() #move the Worker object to the Thread object
        self.workerThread.started.connect(self.worker.loop) #init worker loop() at startup
        self.worker.moveToThread(self.workerThread)
        self.worker.signalExample.connect(self.signalExample) #Connect your signals/slots
        self.workerThread.start()
        #Do your gui operations here...
        #...

    def signalExample(self, text, number):
        print(text)
        print(number)

if __name__== '__main__':
    app = QtWidgets.QApplication(sys.argv)
    Dialog = guiExample()
    sys.exit(app.exec_())
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020