1""" 2Python <-> Objective-C bridge (PyObjC) 3 4This module defines the core interfaces of the Python<->Objective-C bridge. 5""" 6 7__all__ = ['IBOutlet', 'IBAction', 'accessor', 'Accessor', 'typedAccessor', 'callbackFor', 'selectorFor', 'synthesize', 'namedselector', 'typedSelector' ] 8 9from _objc import ivar, selector, _makeClosure, selector, _C_SEL, _C_ID 10import sys, textwrap 11 12# 13# Interface builder support. 14# 15def IBOutlet(name=None): 16 """ 17 Create an instance variable that can be used as an outlet in 18 Interface Builder. 19 """ 20 if name is None: 21 return ivar(isOutlet=1) 22 else: 23 return ivar(name, isOutlet=1) 24 25def IBAction(func): 26 """ 27 Return an Objective-C method object that can be used as an action 28 in Interface Builder. 29 """ 30 return selector(func, signature="v@:@") 31 32def instancemethod(func): 33 return selector(func, isClassMethod=False) 34 35def accessor(func, typeSignature='@'): 36 """ 37 Return an Objective-C method object that is conformant with key-value coding 38 and key-value observing. 39 """ 40 41 from inspect import getargspec 42 args, varargs, varkw, defaults = getargspec(func) 43 funcName = func.__name__ 44 maxArgs = len(args) 45 minArgs = maxArgs - len(defaults or ()) 46 # implicit self 47 selArgs = 1 + funcName.count('_') 48 if varargs is not None or varkw is not None: 49 raise TypeError('%s can not be an accessor because it accepts varargs or varkw' % (funcName,)) 50 51 if not (minArgs <= selArgs <= maxArgs): 52 if selArgs == 3 and (minArgs <= 2 <= maxArgs) and funcName.startswith('validate') and funcName.endswith('_error_'): 53 return selector(func, signature='Z@:N^@o^@') 54 elif minArgs == maxArgs: 55 raise TypeError('%s expected to take %d args, but must accept %d from Objective-C (implicit self plus count of underscores)' % (funcName, maxArgs, selArgs)) 56 else: 57 raise TypeError('%s expected to take between %d and %d args, but must accept %d from Objective-C (implicit self plus count of underscores)' % (funcName, minArgs, maxArgs, selArgs)) 58 59 if selArgs == 3: 60 if funcName.startswith('validate') and funcName.endswith('_error_'): 61 return selector(func, signature='Z@:N^@o^@') 62 63 if funcName.startswith('insertObject_in') and funcName.endswith('AtIndex_'): 64 return selector(func, signature='v@:@i') 65 elif funcName.startswith('replaceObjectIn') and funcName.endswith('AtIndex_withObject_'): 66 return selector(func, signature='v@:i@') 67 68 # pass through to "too many arguments" 69 70 elif selArgs == 2: 71 if funcName.startswith('objectIn') and funcName.endswith('AtIndex_'): 72 return selector(func, signature='@@:i') 73 elif funcName.startswith('removeObjectFrom') and funcName.endswith('AtIndex_'): 74 return selector(func, signature='v@:i') 75 elif funcName.startswith('get') and funcName.endswith('_range_'): 76 return selector(func, signature='@@:{_NSRange=ii}') 77 78 return selector(func, signature="v@:" + typeSignature) 79 80 elif selArgs == 1: 81 if typeSignature == '@' and func.func_name.startswith('countOf'): 82 typeSignature = 'i' 83 84 return selector(func, signature=typeSignature + "@:") 85 86 raise TypeError("%s takes too many arguments to be an accessor" % (funcName,)) 87 88 89def typedSelector(signature): 90 def _typedSelector(func): 91 return selector(func, signature=signature) 92 return _typedSelector 93 94def namedselector(name, signature=None): 95 """ 96 Python 2.4 decorator for overriding the Objective-C SEL for a method, usage: 97 98 @namedselector("foo:bar:") 99 def foobar(self, foo, bar): 100 return foo + bar 101 """ 102 if signature is not None: 103 def _namedselector(func): 104 return selector(func, selector=name, signature=signature) 105 else: 106 def _namedselector(func): 107 return selector(func, selector=name) 108 109 return _namedselector 110 111def typedAccessor(typeSignature): 112 """ 113 Python 2.4 decorator for creating a typed accessor, usage: 114 115 @typedAccessor('i') 116 def someIntegerAccessor(self): 117 return self.someInteger 118 119 @typedAccessor('i') 120 def setSomeIntegerAccessor_(self, anInteger): 121 self.someInteger = anInteger 122 """ 123 def _typedAccessor(func): 124 return accessor(func, typeSignature) 125 return _typedAccessor 126 127def Accessor(func): 128 import warnings 129 warnings.warn( 130 "Use objc.accessor instead of objc.Accessor", DeprecationWarning) 131 return accessor(func) 132 133# 134# Callback support 135# 136def callbackFor(callable, argIndex=-1): 137 """ 138 Decorator for converting a function into an object that can be used 139 as a callback function for (Objective-)C API's that take such a beast 140 as one of their arguments. 141 142 Note that using this decorator for methods is unsupported and that this 143 decorator is optional when the callback isn't stored by the called function 144 145 Usage:: 146 147 @objc.callbackFor(NSArray.sortedArrayUsingFunction_context_) 148 def compare(left, right, context): 149 return 1 150 """ 151 def addClosure(function): 152 closure = _makeClosure(function, callable, argIndex) 153 function.pyobjc_closure = closure 154 return function 155 156 return addClosure 157 158def selectorFor(callable, argIndex=-1): 159 """ 160 Decorator that makes sure that the method has the right signature to be 161 used as the selector argument to the specified method. 162 163 Usage:: 164 165 @objc.selectorFor(NSApplication.beginSheet_modalForWindow_modalDelegate_didEndSelector_contextInfo_) 166 def sheetDidEnd_returnCode_contextInfo_(self, sheet, returnCode, info): 167 pass 168 """ 169 if argIndex == -1: 170 for arg in callable.__metadata__()['arguments']: 171 if arg['type'] == _C_SEL and 'sel_of_type' in arg: 172 signature = arg['sel_of_type'] 173 break 174 else: 175 raise ValueError("No selector argument with type information") 176 177 else: 178 signature = callable.__metadata__().arguments[idx]['type_of_sel'] 179 180 def addSignature(function): 181 return selector(function, signature=signature) 182 183 return addSignature 184 185 186def synthesize(name, copy=False, readwrite=True, type=_C_ID, ivarName=None): 187 """ 188 Use this in a class dictionary to syntheze simple setting/setter methods. 189 190 Note: this is only necessary to get propper behaviour when Key-Value coding 191 is used and special features (like copying) are needed 192 193 usage:: 194 195 class MyClass (NSObject): 196 objc.synthesize('someTitle', copy=True) 197 198 """ 199 if ivarName is None: 200 ivarName = '_' + name 201 202 classDict = sys._getframe(1).f_locals 203 204 if copy: 205 setter = textwrap.dedent(''' 206 def set%(name)s_(self, value): 207 self.%(ivar)s = value.copy() 208 ''' % dict(name=name.capitalize(), ivar=ivarName)) 209 210 else: 211 setter = textwrap.dedent(''' 212 def set%(name)s_(self, value): 213 self.%(ivar)s = value 214 ''' % dict(name=name.capitalize(), ivar=ivarName)) 215 216 getter = textwrap.dedent(''' 217 def %(name)s(self): 218 return self.%(ivar)s 219 ''' % dict(name=name, ivar=ivarName)) 220 221 if readwrite: 222 exec setter in classDict 223 224 exec getter in classDict 225 226 classDict[ivarName] = ivar(type=type) 227 228 229