简体   繁体   中英

Python run a function on main thread from a background thread

Created a background thread this way

 def listenReply(self):
        while self.SOCK_LISTENING:
            fromNodeRED = self.nodeRED_sock.recv(1024).decode()
            if fromNodeRED=="closeDoor":
                self.door_closed()

 ...

    self.listenThread = Thread(target=self.listenReply, daemon=True)
    self.SOCK_LISTENING = True
    self.listenThread.start()

But self.door_closed() has some UI stuffs so that's no good. How do I call self.door_closed in main thread instead?

You can use threading.Event() and set it whenever you you receive "closeDoor" from recv .

For example:

g_should_close_door = threading.Event()

def listenReply(self):
    while self.SOCK_LISTENING:
        fromNodeRED = self.nodeRED_sock.recv(1024).decode()
        if fromNodeRED=="closeDoor":
            g_should_close_door.set()

    ...

    self.listenThread = Thread(target=self.listenReply, daemon=True)
    self.SOCK_LISTENING = True
    self.listenThread.start()

    if g_should_close_door.is_set():
        door_closed() 
        g_should_close_door.clear() 

One thing to keep in mind is that each thread is a sequential execution of a single flow of code, starting from the function the thread was started on. It doesn't make much sense to simply run something on an existing thread, since that thread is already executing something, and doing so would disrupt it's current flow.

However, it's quite easy to communicate between threads and it's possible to implement a thread's code such that it simply receives functions/events from other threads which tell it what to do. This is commonly known as an event loop.

For example your main thread could look something like this

from queue import Queue

tasks = Queue()

def event_loop():
   while True:
       next_task = tasks.get()
       print('Executing function {} on main thread'.format(next_task))
       next_task()

In your other threads you could ask the main thread to run a function by simply adding it to the tasks queue:

 def listenReply(self):
    while self.SOCK_LISTENING:
        fromNodeRED = self.nodeRED_sock.recv(1024).decode()
        if fromNodeRED=="closeDoor":
            tasks.put(door_closed)

Solved it myself using signal and slots of the PyQt.

class App(QWidget):

    socketSignal = QtCore.pyqtSignal(object) #must be defined in class level

    # BG THREAD
    def listenReply(self):
        while self.SOCK_LISTENING:
            fromNodeRED = self.nodeRED_sock.recv(1024).decode()
            print(fromNodeRED)
            self.socketSignal.emit(fromNodeRED)

.... somewhere in init of Main thread:

    self.socketSignal.connect(self.executeOnMain)
    self.listenThread = Thread(target=self.listenReply, daemon=True)
    self.SOCK_LISTENING = True
    self.listenThread.start()
       

....

def executeOnMain(self, data):
     if data=="closeDoor":
            self.door_closed() # a method that changes the UI

Works great for me.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM