0

I am creating a user interface and there are actions which take some time to complete.

The process fetches some data. I want to use that data in the Ui class.

To prevent the interface freezing I use have a worker object which I put in a separate thread and control using signals.

To get access to the data retrieved by the worker I pass the Ui object and have the worker update the content of the Ui classes data variable.

If I try to pass var directly and modify it in the worker thread, the content within the Ui class does not change. Why is this?

Code Example (This works):

from PyQt5 import QtCore
QtCore.Signal = QtCore.pyqtSignal

class threadController(QtCore.QObject):

    fire = QtCore.Signal()

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

class worker(QtCore.QObject):

    finished = QtCore.Signal()

    def __init__(self,ob):
        super(worker,self).__init__()
        self.ob = ob

    def workerfunction(self):
        self.ob.var = [1,2,3] #modify the variable
        self.finished.emit()


class Ui():

    def __init__(self):
        self.thread1 = QtCore.QThread()
        self.thread1.start()
        self.var = [] #Create an empty variable
        self.workerobj = worker(self)
        self.workerobj.moveToThread(self.thread1)
        self.threadControllerObj = threadController()
        self.threadControllerObj.fire.connect(self.workerobj.workerfunction)

    def startThreadProcess(self):
        self.workerobj.finished.connect(self.check) #Need to make sure the thread has finished
        self.threadControllerObj.fire.emit() #fire the worker function


    def check(self):
        print(self.var) #this runs when the worker function in the thread has finished

if __name__ == '__main__':

    app = QtCore.QCoreApplication([])
    UiObj = Ui()
    UiObj.startThreadProcess()
    QtCore.QTimer.singleShot(1000, app.quit)
    app.exec_()

In addition to this, is this the correct way to go about gaining access to data produced in a worker function which is running in a separate thread?

Code Example (This does not work)

from PyQt5 import QtCore
QtCore.Signal = QtCore.pyqtSignal

class threadController(QtCore.QObject):

    fire = QtCore.Signal()

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

class worker(QtCore.QObject):

    finished = QtCore.Signal()

    def __init__(self,var):
        super(worker,self).__init__()
        self.var = var

    def workerfunction(self):
        self.var = [1,2,3]
        self.finished.emit()


class container():

    def __init__(self):
        self.thread1 = QtCore.QThread()
        self.thread1.start()
        self.var = []
        self.workerobj = worker(self.var)
        self.workerobj.moveToThread(self.thread1)
        self.threadControllerObj = threadController()
        self.threadControllerObj.fire.connect(self.workerobj.workerfunction)

    def startThreadProcess(self):
        self.workerobj.finished.connect(self.check) #Need to make sure the thread has finished
        self.threadControllerObj.fire.emit() #fire the worker function


    def check(self):
        print(self.var) #this runs when the worker function in the thread has finished

if __name__ == '__main__':

    app = QtCore.QCoreApplication([])
    contain = container()
    contain.startThreadProcess()
    QtCore.QTimer.singleShot(1000, app.quit)
    app.exec_()
Tim M
  • 479
  • 1
  • 8
  • 21
  • It doesn't look like you're directly passing in `var` in the example above, is it your fixed code? – Peter Nov 23 '18 at 14:28
  • I'm not, that is a working example of passing the object, but If I try to pass just the var it doesn't work. I'll update the question to show these changes. – Tim M Nov 23 '18 at 14:58
  • This is not modifying the object - it's assigning a new object to the name `var` in a `worker` instance. – BartoszKP Nov 23 '18 at 15:10
  • Ok, but when check runs the value of var is correct in the first example, but not in the second example. – Tim M Nov 23 '18 at 15:12
  • @TimMottram change `self.var = [1,2,3]` to `self.var += [1,2,3]` – eyllanesc Nov 23 '18 at 15:13
  • Ok, that works fine, why? is it do do with assignment vs modification? – Tim M Nov 23 '18 at 15:14
  • in your case you are creating another list and therefore the reference is different so the reference of that variable in workerfunction is changing, you can check it by printing the id `def workerfunction(self): print(id(self.var)) self.var += [1,2,3] print(id(self.var)) self.finished.emit()` – eyllanesc Nov 23 '18 at 15:15
  • @TimMottram read [Are lists thread-safe?](https://stackoverflow.com/questions/6319207/are-lists-thread-safe) – eyllanesc Nov 23 '18 at 15:17
  • @TimMottram on the other hand I recommend you not to modify data in another thread without the mutex, in your example it is not necessary, but in more complex examples it is necessary. – eyllanesc Nov 23 '18 at 15:19
  • Ok, thanks very much for the information. I appreciate it :) – Tim M Nov 23 '18 at 15:34
  • So out of interest, why was is working in the first case, I create another list when I do self.var = [1,2,3] - why was Ui.var updated? – Tim M Nov 23 '18 at 15:40
  • 1
    the instance saves the reference to the property, and this property stores the reference of the list, so if the list is replaced the data that stores the property, ie the reference to the list, will be updated with the new reference, it does not lose the reference, but in the second case it does not. – eyllanesc Nov 23 '18 at 19:49
  • Ok, that's really interesting thanks for the help. – Tim M Nov 26 '18 at 14:17

0 Answers0