August 19, 2008.
Recently, I was working on a PyObjC project and I ran into a slightly complex situation. I wanted to schedule an action and run it at a later time, unless a specific condition was satisfied, in which case I wanted to invalidate the scheduled action.
Writing in raw Python I could have used the
Timer class to accomplish this.
from random import random from threading import Timer def disp(): print "Couldn't find in time." timer = Timer(15.0,disp) timer.start() while 1: if random() > 0.9: timer.cancel() print "Found!" break
No problem, we can just translate that using our handy PyObjC rules.
Most of the arguments are self explanatory, but there was something I was unsure of: how do you create selectors in PyObjC? It turns out, pretty easily, but with a bit of magic.
import objc class VendingMachine(object): def __init__(self,products): self.products = products self.timer = None self.reset_timer() def reset_timer(self): if self.timer is not None: self.timer.invalidate() s = objc.selector(self.advertise,signature='v@:') self.timer = NSTimer.scheduleTimerWithTimeInterval_target_selector_userInfo_repeats(30.0,self,s,None,True) def advertise(self): print "Don'cha wanna buy something?" def vend(self): self.reset_timer() print "Thanks for buying."
So that all makes sense, right? Except, perhaps, for one little thing that doesn't make any sense at all? If I may be so bold, the
signature='v@:' part seems the slightest bit magical and even (dare I say) arbitrary. Fortunately, it's an easy magic trick to pick up.
The first part, the
v indicates that the method will return void--i.e. it doesn't return anything, and the second part,
@:, indicates that it is an instance method. Since my object isn't taking any parameters thats all there is to it.
But if the method was taking some parameters, thats easy too. The signature
v@:@fi indicates that the method takes three signatures.
@ represents an object,
f represents a float, and
i represents an integer. You can also use those same symbols instead of
v for specifying the type of a returned object. For example, consider this Python method:
class MyClass(object): def my_method(self,x_int,y_int,z_obj): print x_int + y_int return z_obj
Creating the selector for that method in Python would look like this:
import objc s = objc.selector(my_method,signature="@@:ii@")
Awkward, to be sure, but nothing insurmountable with our four magic symbols:
The places I scrounged for information--all of which were helpful but not quite comprehensive--were objc.selector and objc.signature at Jim Matthews Blog, some PyObjC documentation online, and the always helpful Python docstrings in the PyObjC code itself:
>>> import objc >>> print objc.selector.__doc__ selector(function, [, selector] [, signature] [, isClassMethod=0] [, returnType] [, argumentTypes] [, isRequired=True]) -> selector Return an Objective-C method from a function. The other arguments specify attributes of the Objective-C method. etc...
All three may prove fruitful for those looking for more tidbits to assemble into a somewhat complete picture of using selectors in PyObjC.
Let me know if there are any questions or comments!
Actually, there are a couple of different class methods to choose from, but I picked the easiest one to work with.↩