Search code examples
pythongrand-central-dispatchpyobjc

How to spawn threads in pyobjc


I am learning how to use pyobjc for some basic prototyping. Right now I have a main UI set up and a python script that runs the main application. The only issue is when the script runs, the script runs on the main thread thus blocking the UI.

So this is my sample code snippet in that I attempted in python using the threading import:

def someFunc(self):
    i = 0
    while i < 20:
        NSLog(u"Hello I am in someFunc")
        i = i + 1

@objc.IBAction
def buttonPress(self, sender):
    thread = threading.Thread(target=self.threadedFunc)
    thread.start()

def threadedFunc(self):
    NSLog(u"Entered threadedFunc")
    self.t = NSTimer.NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(1/150., self,self.someFunc,None, True)
    NSLog(u"Kicked off Runloop")
    NSRunLoop.currentRunLoop().addTimer_forMode_(self.t,NSDefaultRunLoopMode)

When clicking on the button, the NSLogs in threadedFunc prints out to console, but it never enters someFunc

So I decided to use NSThread to kick off a thread. On Apple's documentation the Objective-C call looks like this:

(void)detachNewThreadSelector:(SEL)aSelector
                   toTarget:(id)aTarget
                 withObject:(id)anArgument

So I translated that to what I interpreted as pyobjc rules for calling objective-c function:

detachNewThreadSelector_aSelector_aTarget_anArgument_(self.threadedFunc, self, 1)

So in context the IBAction function looks like this:

@objc.IBAction
def buttonPress(self, sender):
    detachNewThreadSelector_aSelector_aTarget_anArgument_(self.threadedFunc, self, 1)

But when the button is pressed, I get this message: global name 'detachNewThreadSelector_aSelector_aTarget_anArgument_' is not defined.

I've also tried similar attempts with grand central dispatch, but the same message kept popping up of global name some_grand_central_function is not defined

Clearly I am not understanding the nuances of python thread, or the pyobjc calling conventions, I was wondering if some one could shed some light on how to proceed.


Solution

  • So I got the result that I wanted following the structure below. Like I stated in my response to the comments: For background thread, NSThread will not allow you to perform certain tasks. (i.e update certain UI elements, prints, etc). So I used performSelectorOnMainThread_withObject_waitUntilDone_ for things that I needed to perform in between thread operations. The operations were short and not intensive so it didn't affect the performance as much. Thank you Michiel Kauw-A-Tjoe for pointing me in the right direction!

    def someFunc(self):
        i = 0
        someSelector = objc.selector(self.someSelector, signature='v@:')
        while i < 20:
            self.performSelectorOnMainThread_withObject_waitUntilDone(someSelector, None, False)
            NSLog(u"Hello I am in someFunc")
            i = i + 1
    
    @objc.IBAction
    def buttonPress(self, sender):
        NSThread.detachNewThreadSelector_toTarget_withObject_(self.threadedFunc, self, 1)
    
    def threadedFunc(self):
        NSLog(u"Entered threadedFunc")
        self.t = NSTimer.NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(1/150., self,self.someFunc,None, True)
        NSLog(u"Kicked off Runloop")
        self.t.fire()