Search code examples
pythonmultiprocessingpython-multithreadingpyobjc

Is it possible for Python threads to cause their parent/main process to execute code?


Background: I've written a simple RUMPS/PyObjC app that at various points makes HTTP requests and pops system alert dialogs. Most of the HTTP requests are threaded to avoid blocking the main execution.

Query: In PyObjC you're not supposed to create alert dialogs in background threads (only the main process is safe to do this) so I was wondering if there was a sensible pattern I could use to allow child threads to communicate back to their parent to notify it that an alert dialog should be created.

As an example:

  1. Main program has a popAlert() method defined, which call PyObjC functions to create alert dialog.
  2. Main program launches a thread that executes an HTTP request and wants to pop an alert when done.
  3. Child thread, when HTTP request completes, somehow calls back to the main process to trigger popAlert().

At the moment I have the child thread creating the alert dialog which apparently isn't safe in PyObjC.

I'm fairly new to threading/multiprocessing and the inter-thread communication examples I've seen so far seem to mostly involve child threads receiving messages from a queue-like mechanism, with the parent optionally blocking until the queue is empty. I haven't yet seen anything that indicates if it's possible to have the parent respond to a message from a child thread in the way I describe.

If doing this requires re-architecting, that's fine too – I'm not wedded to any particular multiprocessing approach.

Any advice would be much appreciated. Please let me know if I can provide more detail. Many thanks for reading.


Solution

  • Take a look at pyobjc_performSelectorOnMainThread. It might be what you need. Here's a contrived usage example:

      class MyClass (NSObject):
    
         def divideByZero_(self, arg):
              return arg/0
    
         def doit(self):
             try:
                 result = self.performSelectorOnMainThread_withObject_(
                      'divideByZero:', 55)
                 print result
    
             except:
                 print "Division failed"
    

    You can find it being used in a more concretely in this PyObjC example app. Check out WSTConnectionWindowControllerClass.py in particular.