Commits

Ronald Oussoren committed 075ac8f

Restructure documentation, makes it easier to generate the website. This is
still not optimal though.

Comments (0)

Files changed (60)

pyobjc-core/Doc/01intro.txt

+=========================
+An introduction to PyObjC
+=========================
+
+..	:authors: Ronald Oussoren, Bob Ippolito
+	:contact: pyobjc-dev@lists.sourceforge.net
+	:URL: http://pyobjc.sourceforge.net/
+	:copyright: 2003-2005 The PyObjC Project
+
+
+.. contents::
+
+Preface
+-------
+
+PyObjC is a bridge between Python and Objective-C.  It allows Python 
+scripts to use and extend existing Objective-C class libraries;
+most importantly the `Cocoa libraries`_ by `Apple`_.
+
+This document describes how to use Objective-C class libraries from Python
+scripts and how to interpret the documentation of those libraries from the 
+point of view of a Python programmer.
+
+.. _`Apple`: http://www.apple.com/
+
+First Steps
+-----------
+
+When dealing with the Objective-C runtime, there are certain patterns
+you need to learn when writing Python code.  If you're not already an
+Objective-C programmer, some of them will seem strange or even
+"un-pythonic" at first.  However, you will get used to it, and the way
+that PyObjC works is quite compliant with the `Zen of Python`_
+(``import this``).  In fact, Ronald is Dutch ;)
+
+.. _`Zen of Python`: http://www.python.org/peps/pep-0020.html
+
+With no further ado, here are the three most important things you
+*must* know before embarking on any PyObjC voyage:
+
+Underscores, and lots of them
+.............................
+
+Objective-C objects communicate with each other by sending messages.
+The syntax for messages is somewhere in-between Python's positional and
+keyword arguments.  Specificlaly, Objective-C message dispatch uses
+positional arguments, but parts of the message name (called "selector"
+in Objective-C terminology) are interleaved with the arguments.
+
+An Objective-C message looks like this::
+
+    [someObject doSomething:arg1 withSomethingElse:arg2];
+
+The selector (message name) for the above snippet is this (note the colons)::
+
+    doSomething:withSomethingElse:
+
+In order to have a lossless and unambiguous translation between Objective-C
+messages and Python methods, the Python method name equivalent is simply
+the selector with colons replaced by underscores.  Since each colon in an
+Objective-C selector is a placeholder for an argument, the number of
+underscores in the PyObjC-ified method name is the number of arguments
+that should be given.
+
+The PyObjC translation of the above selector is (note the underscores)::
+
+    doSomething_withSomethingElse_
+
+The message dispatch, translated to PyObjC, looks like this::
+
+    someObject.doSomething_withSomethingElse_(arg1, arg2)
+
+*Methods that take one argument will have a trailing underscore*.
+
+It may take a little while to get used to, but PyObjC does not ever
+rename selectors.  The trailing underscore will seem strange at first,
+especially for cases like this::
+
+    # note the trailing underscore
+    someObject.setValue_(aValue)
+
+There are a few additional rules regarding message dispatch, see the 
+`Overview of the bridge`_ for the complete rundown.
+
+Two-phase instantiation
+.......................
+
+Objective-C, being a low-level runtime, separates the two concepts required
+to instantiate an object.
+
+allocation:
+    Reserve a chunk of memory large enough to hold the new object, and make
+    sure that all of its declared instance variables are set to "zero"
+    (this means nil pointers to objects, 0 for integers, etc.).
+
+initialization:
+    Fill in the blank slate allocated by the allocation phase.
+
+In Objective-C, the convention is for allocation to be performed by a class
+method called ``alloc``, and initialization is done with method
+*beginning with* the word ``init``.  For example, here is the syntax for
+instantiating an ``NSObject``::
+
+    myObject = NSObject.alloc().init()
+
+And here is an example for creating an ``NSData`` instance given a few bytes::
+
+    myData = NSData.alloc().initWithBytes_length_('the bytes', 9)
+
+You must also follow this convention when subclassing Objective-C classes.
+When initializing, an object must always (directly or indirectly) call the
+designated initializer of its ``super``.  The designated initializer is the
+"most basic" initializer through which all initialization eventually ends up.
+The designated initializer for ``NSObject`` is ``init``.  To find the
+designated initializer for other classes, consult the documentation for that
+class.  Here is an example of an ``NSObject`` subclass with a customized
+initialization phase::
+
+    class MyClass(NSObject):
+
+        def init(self):
+            """
+            Designated initializer for MyClass
+            """
+            # ALWAYS call the super's designated initializer.
+            # Also, make sure to re-bind "self" just in case it
+            # returns something else, or even None!
+            self = super(MyClass, self).init()
+	    if self is None: return None
+
+            self.myVariable = 10
+
+            # Unlike Python's __init__, initializers MUST return self,
+            # because they are allowed to return any object!
+            return self
+
+    class MyOtherClass(MyClass):
+
+        def initWithOtherVariable_(self, otherVariable):
+            """
+            Designated initializer for MyOtherClass
+            """
+            self = super(MyOtherClass, self).init()
+	    if self is None: return None
+
+            self.otherVariable = otherVariable
+            return self
+
+    myInstance = MyClass.alloc().init()
+    myOtherInstance = MyOtherClass.alloc().initWithOtherVariable_(20)
+
+Many Objective-C classes provide class methods that perform two-phase
+instantiation for you in one step.  Several examples of this are::
+
+    # This is equivalent to:
+    #
+    #   myObject = NSObject.alloc().init()
+    #
+    myObject = NSObject.new()
+
+    # This is equivalent to:
+    #
+    #   myDict = NSDictionary.alloc().init()
+    #
+    myDict = NSDictionary.dictionary()
+
+    # This is equivalent to:
+    #
+    #   myString = NSString.alloc().initWithString_(u'my string')
+    #
+    myString = NSString.stringWithString_(u'my string')
+
+Objective-C uses accessors everywhere
+.....................................
+
+Unlike Python, Objective-C convention says to use accessors rather than
+directly accessing instance variables of other objects.  This means
+that in order to access an instance variable ``value`` of an object
+``valueContainer`` you will have to use the following syntax::
+
+    # Getting
+    #
+    # notice the method call
+    #
+    myValue = valueContainer.value()
+
+    # Setting
+    #
+    # notice the naming convention and trailing underscore
+    #
+    valueContainer.setValue_(myNewValue)
+
+When writing your own classes from Python, this is a bit harder since
+Python only has one namespace for all attributes, even methods.  If you
+choose to implement accessors from Python, then you will have to name
+the instance variable something else::
+
+    class MyValueHolder(NSObject):
+
+        def initWithValue_(self, value):
+            self = super(MyValueHolder, self).init()
+            # It's recommended not to use typical Python convention here,
+            # as instance variables prefixed with underscores are reserved
+            # by the Objective-C runtime.  It still works if you use
+            # underscores, however.
+            self.ivar_value = value
+            return self
+
+        def value(self):
+            return self.ivar_value
+
+        def setValue_(self, value):
+            self.ivar_value = value
+
+It's also possible to use `Key-Value Coding`_ in some cases, which eliminates
+the need for writing most accessors, but only in scenarios where the rest of
+the code is using it.
+
+Objective-C for PyObjC users
+----------------------------
+
+It is recommended that you take the time to understand a little bit about
+Objective-C before jumping into PyObjC development.  The class libraries
+that you will be using from Cocoa are not documented in Python, and their
+documentation will be confusing without a grasp on the semantics and syntax
+of Objective-C.
+
+Objective-C is an object-oriented programming language implemented as a
+superset of C that borrows heavily in concept and syntax from Smalltalk.
+of C and borrows heavily from Smalltalk.  It features single inheritance with
+dynamic dispatch and (in theory) multiple root classes.  This is basically the
+same as Python with single inheritance.
+
+An important difference between Python and Objective-C is that the latter is
+not a pure object-oriented language.  Some values are not objects, but values
+of plain C types, such as ``int`` and ``double``.  These basic C types can 
+be used as the types of arguments and the return value of methods. 
+
+Object allocation and initialization are explicit and separate actions in 
+Objective-C.  The former is done by the class-method ``alloc``, while the
+latter is done by instance methods whose name customarily starts with ``init``.
+
+Objective-C code looks just like plain C code, with some easily recognizable
+Smalltalk-like extensions for the object-oriented parts of the language.  An
+example class declaration (usually found in ``.h`` files) and implementation
+(usually found in ``.m`` files) are listed below.  Class declarations are easily
+recognized as blocks of code between ``@interface`` and ``@end``, and similarly
+the implementation is between ``@implementation`` and ``@end``.  An expression
+enclosed in brackets in Objective-C is called a message, and is the equivalent
+to an instance method invocation in Python.  For example, this Objective-C
+code::
+
+    [aMutableArray addObject:@"constant string"];
+
+Is equivalent in intent to the following in Python::
+
+    aList.append(u"constant string")
+
+Objective-C messages have three components: a target, a selector, and zero or
+more arguments.  The target, ``aMutableArray``, is the object or class
+receiving the message.  The selector, ``addObject:`` uniquely identifies the
+kind of message that is being sent.  Finally, the arguments,
+``@"constant string"`` are used by the implementation of the method upon
+receipt of the message.  The syntax of Objective-C message dispatch is
+deceptively similar to keyword arguments in Python, but they are actually
+quite different.  Objective-C messages can not have default arguments, and all
+arguments are passed in a specific order.  The components of a selector may not
+be reordered.  Syntactically, one argument must be interleaved at every colon in
+the selector.  The message::
+
+    [anArray indexOfObject:someObject inRange:someRange]
+    
+Target:
+    ``anArray``
+
+Selector:
+    ``indexOfObject:inRange:``
+    
+Arguments:
+    ``someObject``, ``someRange``
+
+As documented later, the straightforward translation of such a message to
+Python is::
+
+    anArray.indexOfObject_inRange_(someObject, someRange)
+    
+This may be awkward and "unpythonic" at first, however this syntax is necessary
+to preserve the semantics of Objective-C message dispatch.
+
+A class declaration::
+
+    @interface MyClass : MySuperClass
+    {
+        id  anInstanceVariable;
+        int anotherInstanceVariable;
+    }
+
+    // A class method that returns an initialized instance of this class.
+    // Similar to an implementation of __call__ on the metaclass.
+    +instanceWithObject:(id)anObject andInteger:(int)anInteger;
+
+    // An instance method, the designated initializer for MyClass.
+    // Similar to an implementation of __new__ on MyClass.
+    -initWithObject:(id)anObject andInteger:(int)anInteger;
+
+    // An accessor, instance variables (attributes) are in a separate
+    // namespace and are considered "private" in Objective-C.  Conventionally,
+    // there is nothing similar to this in Python.
+    -(int)anotherInstanceVariable;
+    @end
+
+A class implementation::
+
+    @implementation MyClass
+
+    // Note that a type is not declared for the return value.  Undeclared types
+    // are assumed to be "id", which means any kind of instance.
+    +instanceWithObject:(id)anObject andInteger:(int)anInteger
+    {
+        // 1. Create an instance of MyClass.
+        // 2. Initialize it with its designated initializer
+        //    "initWithObject:andInteger:".
+        // 3. Autorelease it, so that it does not leak memory.
+        // 4. Return the new instance.
+        //
+        // NOTE:
+        //   By convention,initializers (such as +new, -init, -copy)
+        //   are the only methods that should return retained objects.
+        //
+        // NOTE:
+        //   Since this is a class method, "self" refers to the class!
+        //
+        // Very roughly similar to:
+        //   return self.__new__(anObject, anInteger)
+        return [[[self alloc] initWithObject:anObject andInteger:anInteger] autorelease];
+    }
+
+    // Note that a type is not declared for the return value.  Undeclared types
+    // are assumed to be "id", which means any kind of instance.
+    -initWithObject:(id)anObject andInteger:(int)anInteger
+    {
+        // Call the designated initializer of the superclass.
+        // Similar to:
+        //     self = super(MyClass, self).__new__()
+        self = [super init];
+
+        // Bail if initialization of the superclass failed.
+        // Similar to:
+        //     if self is None:
+        //         return None
+        if (!self) {
+            return nil;
+        }
+
+        // Set the instance variable (attribute).  The argument must be
+        // retained, since it will persist as long as the instance does.
+        // Similar to:
+        //     # Reference counting is automatic in Python
+        //     self.anInstanceVariable = anObject
+        anInstanceVariable = [anObject retain];
+
+        // Set the other instance variable.  Note that since anInteger is
+        // a primitive "C" type, not an object, no reference counting takes
+        // place.
+        // Similar to:
+        //     # Everything is an object in Python
+        //     self.anotherInstanceVariable = anInteger
+        anotherInstanceVariable = anInteger;
+
+        // Like __new__ in Python, initializers in Objective-C must
+        // explicitly return self.  Note that this is different from
+        // __init__.
+        // Similar to:
+        //     return self
+        return self;
+    }
+        
+
+    // an accessor, instance variables (attributes) are in a separate
+    // namespace and are considered "private"
+    -(int)anotherInstanceVariable
+    {
+        return anotherInstanceVariable;
+    }
+
+    // Since objects were retained as instance variables on this object,
+    // they must be freed when the object is.  This is similar to an
+    // implementation of __del__ in Python.  Since Objective-C has no
+    // cyclic garbage collection, this isn't discouraged like it is in
+    // Python.
+    -(void)dealloc
+    {
+        // Very roughly similar to:
+        //     del self.instanceVariable
+        [instanceVariable release];
+
+        // Very roughly similar to:
+        //     super(MyClass, self).__del__()
+        [super dealloc];
+    }
+
+    @end
+
+Objective-C also features exceptions, but they are typically only used for
+disaster recovery, not error handling, so you will not encounter them very
+often.  Read `The Objective-C Programming Language`_ if you want to
+know more about exceptions in Objective-C. 
+
+One thing to keep in mind when translating Objective-C snippets to Python is
+that any message can be sent to ``nil``, and the return value of that message
+will be ``nil``.  PyObjC translates ``nil`` to ``None`` when crossing the
+bridge, so any such attempt will raise an ``AttributeError``.
+
+For more information about Objective-C see:
+
+* `The Objective-C Programming Language`_ at `Apple`_.
+
+.. _`The Objective-C Programming Language`: http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/index.html
+
+
+Overview of the bridge
+----------------------
+
+Classes
+.......
+
+Objective-C classes are visible as (new-style) Python classes and can be 
+subclassed just like normal Python classes.  All the usual introspection
+mechanisms work as well, as do ``__slots__`` and descriptors.  The major 
+differences between normal Python classes and Objective-C classes are the way 
+that instances are created and initialized, and the fact that Objective-C
+selectors look strange when translated to Python methods.
+
+Multiple inheritance may be used when subclassing an Objective-C class, so
+long as the Objective-C class is the first base class and there is only one
+Objective-C base class.  The Objective-C runtime does not support multiple
+inheritance.  These mix-in classes should not contain different
+implementations for Objective-C methods.  To achieve this behavior, Categories
+should be used instead.
+
+Another thing to keep in mind is that the names of Objective-C classes must
+be globally unique per process, including across Python modules.  That is,
+it is *not* possible to have two Python modules that define a class with the
+same name.  It is conventional to choose class names with a short prefix that
+uniquely identify your project or company.  For example, Apple uses ``NS``
+as the prefix for all classes in the `Cocoa libraries`_.  Note that the ``NS``
+prefix made much more sense when it was called NeXTstep, but persists to this
+day for compatibility reasons.
+
+As described in `Objective-C for PyObjC users`_ the creation of Objective-C 
+objects is a two-stage process.  To initialize objects, first call a
+class method to allocate the memory (typically ``alloc``), and then call an
+initializer (typically starts with ``init``).  Some classes have class methods
+which perform this behind the scenes, especially classes that create cached,
+immutable, or singleton instances.
+
+Messages and Functions
+......................
+
+Objective-C methods are bridged to Python methods.  Because Objective-C
+message dispatch syntax can not be translated directly to Python, a few
+simple translations must take place.  The rules for these translations are:
+
+1. Replace all colons in the selector with underscores:
+  
+    - ``someMethod:withFoo:andBar:`` translates to ``someMethod_withFoo_andBar_``
+
+2. If the result ``class`` or ``raise`` (Python keywords), append two underscores:
+  
+    - ``class`` translates to ``class__``
+    - ``raise`` translates to ``raise__``
+
+3. Use this translated selector as a normal Python method.
+   The arguments must be passed in the same order, and the number of
+   arguments passed will normally be equal to the number of underscores
+   in the method name; exceptions to this rule and the behavior of "result"
+   are mentioned below.
+
+    - ``result = [someObject someMethod:firstArg withFoo:foo andBar:bar];``
+      translates to
+      ``result = someObject.someMethod_withFoo_andBar_(firstArg, foo, bar)``
+
+Note that it is currently not possible to support methods with a variable
+number of arguments from Python.  These selectors must be wrapped by
+custom Objective-C code in order to be accessible by Python.
+
+Wrapped/bridged methods (and functions) have the same number of arguments
+as the corresponding Objective-C method or function, unless otherwise noted
+in the documentation (`Notes on supported APIs and classes on Mac OS X`_ for
+Cocoa on Mac OS X).
+
+.. _`Notes on supported APIs and classes on Mac OS X`: api-notes-macosx.html
+
+Most methods or functions that take or return pointers to values will be an
+exception to this rule if it is callable from Python at all.  In Objective-C
+terminology, there are three kinds of pointers that can be used in a method:
+
+``in``:
+    Used to pass data by reference to the function.  This is not a special
+    case from Python.
+
+    Instead of a regular value you may also pass in the value ``objc.NULL``, 
+    when you do that the Objective-C method will receive a NULL pointer instead
+    of a pointer to your value.
+
+``out``:
+    Used to pass data from the function (e.g. an additional return value).
+
+    Pass in either ``None`` or ``objc.NULL`` for output arguments.
+    to the method. If the value is ``objc.NULL`` the Objective-C code will
+    receive a NULL pointer for this argument, otherwise it will receive a 
+    valid pointer. 
+
+    You can also leave out all output arguments from the argument list, in
+    which case the values default to ``None``. This feature is deprecated
+    and wil emit a deprecation warning.
+
+``inout``:
+    A combination of in and out (a value is passed by reference, and mutated
+    upon return).  Unlike ``out``, these arguments remain in the argument list,
+    and thus do not have an effect on the number of arguments a method expects.
+    See below for notes on how ``inout`` arguments change the return value.
+
+    Instead of a regular value you may also pass in the value ``objc.NULL``, 
+    when you do that the Objective-C method will receive a NULL pointer instead
+    of a pointer to your value.
+
+In order to determine what the return value of such an exceptional message will
+look like, you must "make a list" of the return values with the following rules:
+
+1. If the return type of the method or function is not ``void``, add it to the
+   list.
+
+2. For each argument in the method or function, add it to the list if it is
+   ``out`` or ``inout``. When ``objc.NULL`` was used as the argument value it
+   will also be used as the result value.
+
+After creating this list, you will have one of three cases:
+
+Empty:
+    The return value of this call will always be ``None``.
+
+One element:
+    The return value of this call will correspond to the one element of the list.
+
+More than one element:
+    The return value of this call will be a tuple in the same order as the list.
+    
+The rules for pass by reference arguments may look quite complicated, but
+it turns out this is very straightforward when working with them.
+
+As an example of a method with two output arguments, ``NSMatrix`` implements a
+selector named ``getNumberOfRows:columns:`` with the following signature::
+
+   (void)getNumberOfRows:(int *)rowCount columns:(int *)columnCount
+
+This method is used from Python like this::
+
+   rowCount, columnCount = matrix.getNumberOfRows_columns_()
+
+Alternatively you may use::
+
+   rowCount, columnCount = matrix.getNumberOfRows_columns_(None, None)
+
+This way of calling the method is more verbose and basically only usefull when
+you want to surpress one of the arguments. This can be usefull for methods that
+have an ``NSError**`` argument: most of those allow passing in a NULL pointer
+and won't create an error object in that case which can be a significant
+speedup for tight loops.
+
+When a function or method has an array of values and the length of that array
+as arguments, ``None`` may be passed as the length to specify that the length
+of the given sequence should be used.
+
+Python's ``array.array`` type may be used to represent a C array if the
+typestr and size match what is expected by the selector.  Numeric, numarray,
+and other third party array types are not supported in PyObjC 1.3.5.
+
+When defining methods in an Objective-C subclass, the bridge must provide
+type signatures for each method to the Objective-C runtime.  The default
+type signature is for all arguments as well as the return value to be objects (just
+like with normal Python methods).  If there is no return statement in the implementation,
+then the return value will be void.  The bridge will automatically pick a better 
+signature when it has more information available.  Specifically, a method overrides 
+an existing method, the bridge will assume you want to use the same
+method signature.  Furthermore, if the method is implemented in an (informal)
+protocol known to the bridge it will use the signature from the corresponding
+method in that signature.
+
+The end result is that it is rarely necessary to explicitly add information about
+the signature of methods.  For the two most common cases where this is necessary,
+we have provided convenience decorators (used like ``staticmethod`` or
+``classmethod``):
+
+``objc.accessor``:
+    Use this to wrap a `Key-Value Coding`_ or `Key-Value Observing`_ compliant
+    accessor.
+
+``PyObjCTools.AppHelper.endSheetMethod``:
+    Use this to wrap the implementation of a sheet's "didEndSelector" callback.
+
+For complete control of the mapping to Objective-C you can use the function
+``objc.selector`` to create custom descriptors.  See the documentation of the
+``objc`` module for the arguments you can use with this function.  It is
+normally used like this::
+
+	class MyObject(NSObject):
+
+		# -(void)someMethod:(float)arg
+		def someMethod_(self, arg):
+			pass
+
+		someMethod_ = objc.selector(someMethod_, signature='v@:f')
+
+From Python 2.4, there is a decorator for this purpose::
+
+	class MyObject(NSObject):
+
+		@objc.signature('v@:f')
+		def someMethod_(self, arg):
+			pass
+
+
+Reference counting
+..................
+
+The `Cocoa libraries`_, and most (if not all) other class libraries for 
+Objective-C use explicit reference counting to manage memory.  The methods
+``retain``, ``release`` and ``autorelease`` are used to manage these 
+reference counts.  You won't have to manage reference counts in Python, the
+bridge does all that work for you (but see `Notes on supported APIs and classes 
+on Mac OS X`__ for some advanced issues).
+
+.. __: api-notes-macosx.html
+
+The only reasons reference counts are mentioned at all are to tell you about
+ignoring them, and more importantly to introduce you to some issues w.r.t. 
+reference counting.
+
+It turns out that Cocoa uses a primitive form of `weak references`_.  Those 
+are not true `weak references`_ as in Python, but use-cases where an object 
+stores a reference to another object without increasing the reference count
+for that other object.  The bridge cannot solve the issues this introduces
+for you, which means that you get hard crashes when you're not careful when
+dealing with those `weak references`_.
+
+.. _`weak references`: http://www.python.org/doc/current/lib/module-weakref.html
+
+The basic rule to deal with weak references is: make sure objects stays
+alive as long as someone might have a weak reference to them.  Due to the way
+the bridge works, this means that you must make sure that you don't create
+weak references from Objective-C to a plain Python object.  The Python
+object stays alive, but the proxy object as seen by the Objective-C code is
+actually an autoreleased object that will be cleaned up unless the Objective-C
+code increases its reference count.
+
+The document `Notes on supported APIs and classes on Mac OS X`_ contains 
+information about classes that work with weak references.  The most important
+are notification centers and ``NSOutlineView``, to be exact: the outline view
+stores weak references to the objects return by the method 
+``outlineView:child:ofItem:`` of its data source.  The easiest way to avoid
+crashes with outline views is to make sure that you model for the view uses
+subclasses of ``NSObject`` to represent the nodes in the outline view.
+
+Another gotcha is that ``obj.setDelegate_()`` often does *not* retain the
+delegate, so a reference should be maintained elsewhere.
+
+Protocols
+.........
+
+Cocoa defines a number of formal and informal protocols that specify methods
+that should be implemented by a class if it is to be used in a specific role,
+such as the data source for an ``NSTableView``.
+
+Those protocols are represented by instances of ``objc.informal_protocol``,
+and ``objc.formal_protocol``.  The only ones that have to care about these
+objects are the maintainers of wrappers around Objective-C frameworks: they
+have to keep these protocol wrappers up-to-date.
+
+PyObjC will automatically use the information in the ``informal_protocol`` 
+objects to add the right method signatures to methods, and to warn about
+classes that partially implement a protocol.
+
+See `PyObjC protocol support`__ for more information.
+
+.. __: protocols.html
+
+Cocoa Bindings
+..............
+
+In Mac OS X 10.3 Apple introduced `Cocoa Bindings`_, a method to make it easier
+to create and use *Controller* objects using `Key-Value Observing`_ and
+`Key-Value Coding`_.  In order to create accessors compatible with this, you
+must use ``objc.accessor`` to create an appropriate selector descriptor.
+
+PyObjC automaticly emits the right `Key-Value Observing`_ notifications when 
+you set attributes on an Objective-C class. This is however not supported for
+pure python objects. You should therefore use ``NSMutableArray`` instances 
+instead of Python lists for instance variables that will be observed and contain
+a sequence of values (and simularly for ``NSMutableDictionary`` instead of
+``dict``).
+
+.. _`Cocoa Bindings`: http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaBindings/
+.. _`Key-Value Coding`: http://developer.apple.com/documentation/Cocoa/Conceptual/KeyValueCoding/
+.. _`Key-Value Observing`: http://developer.apple.com/documentation/Cocoa/Conceptual/KeyValueObserving/
+
+Categories
+..........
+
+Objective-C has a mechanism for modularize a class definition, it is possible
+to add methods to an existing class in a separate compilation unit and even
+a separate library.  This mechanism is named categories and is used to enhance
+existing classes, for splitting classes in several parts and to document
+informal protocols.
+
+An example of a category definition::
+
+	@interface NSObject (MyCategory)
+	- (NSSize)objectFootprint;
+	@end
+
+This declares an additional category on ``NSObject``.  This category contains
+a single method.
+
+The function ``objc.classAddMethods`` can be used to get the same effect in
+Python::
+
+	def objectFootprint(self):
+		pass
+
+	objc.classAddMethods(NSObject, [objectFootprint])
+
+This is not very clear, PyObjC therefore also provides the following 
+mechanism, implemented on top of ``objc.classAddMethods``::
+
+	class NSObject(objc.Category(NSObject)):
+		def objectFootprint(self):
+			pass
+
+To make it clear that ``objc.Category`` performs a special task the name in
+the class definition must be the same as the ``__name__`` of the argument
+to ``objc.Category``.
+
+Accessing Python objects from Objective-C
+-----------------------------------------
+
+All Python objects can be accessed from Objective-C through proxy objects.
+Whenever a Python object crosses the line from Python to Objective-C a proxy
+object is created (of class ``OC_PythonObject``, a subclass of ``NSProxy``).
+This proxy object will forward all method calls from Objective-C to Python, and
+will return the results back to Objective-C.
+
+See the section 'Method protocol' for a description of how PyObjC translates
+between Python and Objective-C method calls.
+
+A number of Python types/classes are treated specially:
+
+- Python numbers (``int``, ``float``, ``long``) are translated into
+  ``NSNumber`` instances.  Their identity is not preserved across the bridge.
+
+- Python ``str`` is proxied using ``OC_PythonString``, a subclass of
+  ``NSString``.  A Python ``str`` may be used anywhere a ``NSString`` is
+  expected, but ``unicode`` should be used whenever possible.
+  ``OC_PythonString`` will use the default encoding of ``NSString``, which is
+  normally MacRoman but could be something else.
+
+- Python ``unicode`` is proxied using ``OC_PythonUnicode``, a subclass of
+  ``NSString``.  A Python ``unicode`` may be used anywhere a ``NSString``
+  is expected.
+
+- Python ``dict`` is proxied using ``OC_PythonDictionary``, a subclass of
+  ``NSMutableDictionary``.  A Python ``dict`` may be used anywhere
+  an ``NSDictionary`` is expected.
+
+- Python ``list`` and ``tuple`` are proxied using ``OC_PythonArray``, a
+  subclass of ``NSMutableArray``.  Python ``list`` or ``tuple`` objects
+  may be used anywhere an ``NSArray`` is expected.
+
+- Python objects that implement the Python buffer API, except for ``str``
+  and ``unicode``, are proxied using ``OC_PythonData``, a ``NSData`` subclass.
+  Objects that implement the Python buffer API such as ``buffer``,
+  ``array.array``, ``mmap.mmap``, etc. may be used anywhere a ``NSData`` is
+  expected.
+
+These special cases allow for more transparent bridging between Python and
+Objective-C.
+
+Cocoa for Python programmers
+----------------------------
+
+Cocoa frameworks are mapped onto Python packages with the same name; that is
+the classes, constants and functions from the AppKit framework are available
+after you import ``AppKit`` in your Python script. 
+
+These helper modules contain *only* functions, constants and classes that 
+wrap items in the corresponding framework.  All utility functions and classes 
+are located in the ``PyObjCTools`` package and ``objc`` module.  Note that it
+is possible to use ``pydoc`` (or the ``help()``) function with the framework
+wrappers, but that this is not very useful for the entire module due to the
+size of these modules.
+
+This makes it easier to find documentation for an item: if you import it 
+from the wrapper module for an Objective-C framework the documentation for
+that item can be found in the documentation for the framework; otherwise the
+item is documented in the PyObjC documentation.
+
+The module ``PyObjCTools.NibClassBuilder`` can be used to make working with 
+NIB files more convenient.  This module can be used to extract information 
+about classes from NIB files, both as a standalone tool generating source code 
+and during runtime.  See the online documentation for this module for more
+information.
+
+PyObjC includes a number of examples that show how to use Cocoa from
+Python.  The `PyObjC Example index`_ contains an overview of those examples.
+
+More information on Cocoa programming can be found at:
+
+* `Cocoa documentation at the Apple developer website`_
+
+* `Cocoa examples at the Apple developer website`_
+
+* `stepwise.com`_
+
+* Your local bookstore or library
+
+.. _`PyObjC Example index`: ../Examples/00ReadMe.html
+
+..  _`Cocoa libraries`: http://developer.apple.com/referencelibrary/API_Fundamentals/Cocoa-api-date.html
+
+..  _`Cocoa documentation at the Apple developer website`: http://developer.apple.com/documentation/Cocoa/Cocoa.html
+
+.. _`Cocoa examples at the Apple developer website`: http://developer.apple.com/samplecode/Cocoa/index.html
+
+.. _`stepwise.com`: http://www.stepwise.com/
+
+Notes on specific tasks
+-----------------------
+
+Working with threads
+....................
+
+Most of Cocoa, and thus PyObjC, requires an ``NSAutoreleasePool`` in order to function
+properly.  PyObjC does this automatically on the first thread it is imported from,
+but other threads will require explicit ``NSAutoreleasePool`` management.  The following
+practice for working with ``NSAutoreleasePool`` is recommended::
+
+	pool = NSAutoreleasePool.alloc().init()
+	...
+	del pool
+
+Typically this will be done at the beginning and end of the thread.  It is important
+to use ``del`` before rebinding the ``pool`` local to another ``NSAutoreleasePool``
+instance, otherwise it will not have the intended effect.
+
+For long running threads and tight loops, it can also be useful to use this pattern
+in the body of the loop in order to optimize memory usage.  For example, ``NSRunLoop``
+will be create a new ``NSAutoreleasePool`` at the beginning of each run loop iteration
+and release it at the end.
+
+Finalizers
+..........
+
+In normal Python, there are two methods for writing finalizers: implementing
+``__del__``, and using ``weakref.ref`` callbacks.  Generally, ``__del___`` is
+discouraged as it does not allow the object to participate in cyclic garbage
+collection and create uncollectible garbage if not implemented properly.
+``weakref.ref`` callbacks avoid this restriction as they do not provide a real
+reference to the object.
+
+In Objective-C, there is no cyclic garbage collection, so all Objective-C
+objects (including subclasses from Python) are already subject to these
+restrictions.  When subclassing an Objective-C class, you may implement
+``dealloc`` or ``__del__``.  If you implement ``dealloc``, ensure that
+you call the super ``dealloc`` at the end.  If you implement both 
+``__del__`` and ``dealloc``, the order in which they are called is
+undefined.
+
+It is not currently possible to create a ``weakref.ref`` for any Objective-C
+object.  It is probably technically possible to do, but tricky, so it
+may eventually be implemented in a future version of PyObjC (especially
+if a future Objective-C runtime supports it).
+
+Copying
+.......
+
+It is possible for a Python subclass of an Objective-C class to implement
+the ``NSCopying`` protocol.  Some care must be taken when the superclass
+already implements the protocol. 
+
+Some ``NSCopying`` compliant Objective-C classes copy the template object
+manually.  In those cases the Python subclass must also copy the additional
+ivars manually.
+
+Other ``NSCopying`` compliant Objective-C classes use a convenience function
+that creates a shallow copy of the object and all of its ivars.  In those
+cases the Python subclass will not have to explicitly copy all of the ivars.
+However, the ivars in the copy will refer to the same objects as the original,
+and will thus share some state.  As with shallow copies in Python, if any of
+the ivars refer to mutable objects (``list``, ``dict``, etc.) it may be
+desirable to explicitly make shallow or deep copies of the mutable ivars.
+
+NOTE: PyObjC might introduce a helper class when you inherit from a class
+that implements ``NSCopying`` as an internal implementation detail.
+External code should not rely on the existance of this class.
+
+NOTE2: ``SomeClass.copyWithZone_`` should not be implemented unless a
+superclass already implements ``copyWithZone:``, or else the behavior
+will be undefined (memory corruption, crashes, etc.).
+
+Building applications
+---------------------
+
+There are two different recommended ways to build applications with PyObjC.
+
+"py2app" :  setup.py
+....................
+
+The PyObjC installer includes a copy of the ``py2app`` package.  This package
+offers a way to build distutils scripts for building (standalone)
+applications and plugin bundles.
+
+An example ``setup.py`` script::
+
+    from distutils.core import setup
+    import py2app
+
+    setup(
+        app = ["iClass.py"],
+        data_files = ["English.lproj"],
+    )
+	
+During development you typically invoke it from the command line like this::
+
+	python setup.py py2app -A
+
+This will build an application bundle in a folder named ``dist`` in the
+current folder. The ``-A`` option tells ``py2app`` to add symbolic
+links for data folders and files and an Alias to your main script,
+allowing you quickly rebuild the application without doing a full dependency
+scan, with the additional bonus that you can edit them without rebuild. To
+build a standalone application, simply do not use the ``-A`` option.
+Note that if you are using a version of Python shipped with your operating
+system, it will not be included in the application.  Otherwise, your
+application will include stripped down version of the Python runtime that
+you ran setup.py with.
+
+For more information about ``py2app`` usage, read through some of the
+``setup.py`` scripts used by the examples in the `Examples`__ folder.
+On any ``setup.py`` script that imports ``py2app``, you can use the
+following command to see the list of options::
+
+    python setup.py py2app --help
+
+.. __: ../Examples/00ReadMe.txt
+
+
+"IDE approach" : Xcode
+......................
+
+PyObjC includes a number of Xcode templates that can be used to 
+develop applications, using the same underlying functionality that
+is in py2app.  These templates are used like any other Xcode template,
+but there are some organizational rules about the template.
+
+See `the documentation for the templates`__ for more details.
+
+.. __: Xcode-Templates.html

pyobjc-core/Doc/06tutorial/index.txt

+=======================================
+Creating your first PyObjC application.
+=======================================
+
+In this tutorial you will learn how to create your first Python Cocoa
+application: a simple dialog that allows you to convert amounts of money from
+one currency to another.  Definitely easier to do with a calculator, but in the
+process of following the tutorial you will learn which bits of Apple's Cocoa
+documentation apply to PyObjC and which bits are different, and how to adapt
+the different bits to PyObjC from Objective-C.
+
+To follow the tutorial you need:
+
+ * PyObjC 1.3.1
+ * py2app 0.2 or later (included in the binary installer for PyObjC)
+ * Python 2.3 or later (note: PyObjC is NOT compatible with MacPython-OS9)
+ * Mac OS X 10.2 or later
+ * Xcode Tools (was Developer Tools for Mac OS X 10.2)
+
+If you do not have a ``/Developer`` folder, then you do not have Xcode Tools
+installed.  See `Getting the Xcode Tools`_.
+
+.. _`Getting the Xcode Tools`: http://developer.apple.com/tools/download/
+
+Getting Started
+---------------
+
+1. Create a work directory ``src``.  Check which Python you have installed
+   PyObjC for, by running ``python`` and checking that ``import Foundation``
+   works.  If it does not work it could be that you have installed PyObjC for
+   ``/usr/local/bin/python`` but Apple's ``/usr/bin/python`` comes first in
+   your ``$PATH``.  Make sure you use the right python whereever it says
+   ``python`` in this tutorial.
+   
+2. Start Interface Builder, select *Cocoa Application*
+   in the new file dialog, save this file as ``src/MainMenu.nib``.
+   
+3. Proceed with the instructions as lined out in Apple's
+   `Developing Cocoa Objective-C Applications: a Tutorial`_, `chapter 3`_,
+   just after the section "*Creating the Currency Converter Interface*".
+   Work through "Defining the Classes of Currency Converter", "Connecting
+   ConverterController to the Interface", and stop at
+   "*Implementing the Classes of Currency Converter*", as we are going to do
+   this in Python, not Objective-C.  Your nib file should now be the same as
+   step3-MainMenu.nib_.
+   
+.. _`Developing Cocoa Objective-C Applications: a Tutorial`: http://developer.apple.com/documentation/Cocoa/Conceptual/ObjCTutorial/index.html
+.. _`chapter 3`: http://developer.apple.com/documentation/Cocoa/Conceptual/ObjCTutorial/index.html?http://developer.apple.com/documentation/Cocoa/Conceptual/ObjCTutorial/chapter03/chapter_3_section_1.html
+.. _step3-MainMenu.nib: step3-MainMenu.nib.zip
+   
+4. Create the skeleton Python script by running the ``nibclassbuilder`` script.
+   ``nibclassbuilder`` will parse the NIB file and create a skeleton module for
+   you.  Invoke it as follows (from the ``src`` directory)::
+   
+       $ python -c "import PyObjCScripts.nibclassbuilder" MainMenu.nib > CurrencyConverter.py
+               
+   Depending on your installation, the ``nibclassbuilder`` script may be on your ``$PATH``.
+   If so, it can be invoked as such::
+
+       $ nibclassbuilder MainMenu.nib > CurrencyConverter.py
+   
+   The result of this can be seen in step4-CurrencyConverter.py_.
+
+.. _step4-CurrencyConverter.py: step4-CurrencyConverter.py.html
+               
+Testing the user interface
+--------------------------
+
+5. Now we need to create an build script for CurrencyConverter.  To do this,
+   create a file named ``setup.py`` with the following contents::
+   
+        from distutils.core import setup
+        import py2app
+
+        setup(
+            app=['CurrencyConverter.py'],
+            data_files=['MainMenu.nib'],
+        )
+
+   The result of this can be seen in step5-setup.py_.
+
+.. _step5-setup.py: step5-setup.py.html
+   
+6. Run the setup script to create a temporary application bundle for
+   development::
+
+        $ python setup.py py2app -A
+      
+   Note that we use the ``-A`` argument to create an alias bundle at 
+   ``dist/CurrencyConverter.app``.  Alias bundles contain an alias to the
+   main script (``CurrencyConverter.py``) and symlinks to the data files
+   (``MainMenu.nib``), rather than including them and their dependencies
+   into a standalone application bundle.  This allows us to keep working on
+   the source files without having to rebuild the application.  This alias
+   bundle is similar to a ZeroLink executable for Xcode - it is for
+   DEVELOPMENT ONLY, and will not work on other machines.
+   
+7. Run the program.  This can be done in three ways:
+
+   - double-click ``dist/CurrencyConverter`` from the Finder
+     (you won't see the .app extension)
+
+   - open it from the terminal with::
+   
+       $ open dist/CurrencyConverter.app
+       
+   - run it directly from the Terminal, as::
+   
+       $ ./dist/CurrencyConverter.app/Contents/MacOS/CurrencyConverter
+       
+   The last method is typically the best to use for development: it leaves
+   stdout and stderr connected to your terminal session so you can see what
+   is going on if there are errors, and it allows you to interact with ``pdb``
+   if you are using it to debug your application.  Note that your application
+   will likely appear in the background, so you will have to cmd-tab or click
+   on its dock icon to see its user interface.
+   
+   The other methods cause stdout and stderr to go to the Console log, which
+   can be viewed with ``/Applications/Utilities/Console.app``.
+   
+   When you run your script as it is now it should behave identically as when
+   you tested your interface in Interface Builder in step 3, only now the
+   skeleton is in Python, not Objective-C.
+   
+
+Writing the code
+----------------
+
+8.  Time to actually write some code.  Open ``CurrencyConverter.py`` in your
+    favorite text editor.  Follow Apple's documentation again, chapter 3,
+    section "Implementing Currency Converter's Classes".  To translate this
+    Objective C code to Python syntax, we will need to do some name mangling of
+    the selectors.  See *An introduction to PyObjC* for the details, but the
+    short is that::
+
+        [anObject modifyArg: arg1 andAnother: arg2]
+
+   translates into the following Python code, by replacing the colons in the
+   selector with underscores, and passing the arguments as you would with a
+   normal Python method call::
+
+        anObject.modifyArg_andAnother_(arg1, arg2)
+   
+   Note that we don't do this mangling for ``Converter.convertAmount()``: this
+   method is only called by other Python code, so there is no need to go
+   through the name mangling.  Also, if we would want to make this method
+   callable from ObjC code we may have to tell the PyObjC runtime system about
+   the types of the arguments, so it could do the conversion.  This is beyond
+   the scope of this first tutorial, *An introduction to PyObjC* has a little
+   more detail on this.
+   
+   The application should now be fully functional, try it.  The results of what
+   we have up to now can be seen in step8-CurrencyConverter.py_.
+   
+.. _step8-CurrencyConverter.py: step8-CurrencyConverter.py.html
+   
+Extending the functionality
+---------------------------
+
+9.  We are going to add one more goodie, just to show how you edit an existing
+    application.  The main problem, which may be obvious, is that we cannot run
+    ``nibclassbuilder`` again because we would destroy all the code we wrote in
+    steps 5 and 8, so we do this by hand.  What we are going to do is add an
+    "invert rate" command, because I always get this wrong: instead of typing
+    in the exchange rate from dollars to euros I type in the rate to convert
+    from euros to dollars.
+   
+    Open ``MainMenu.nib`` in Interface Builder.  Select the *Classes* view and
+    then select the ``ConverterController`` class.  In the info panel select
+    the *Attributes* from the popup.  Select the *Actions* tab, and add an
+    action ``invertRate:``.  You have now told Interface Builder that instances
+    of the ``ConverterController`` class have grown a new method
+    ``invertRate_()``.
+   
+    In the ``MainMenu.nib main`` window open the *MainMenu* menubar.  Select
+    the ``Edit`` menu.  Make sure the *Menus* palette is open and selected,
+    drag a separator to the ``Edit`` menu and then drag an ``Item`` there.
+    Double-click the item and set the text to ``Invert Exchange Rate``.
+   
+    Make the connection by control-dragging from the new
+    ``Invert Exchange Rate`` menu item to the ``ConverterController`` instance
+    in the Instances tab in the ``MainMenu.nib`` main window.
+
+    *NOTE:* you drag to the *instance* of ``ConverterController``, not to the
+    class.
+
+    In the *Info* panel, *Connections* section, select ``invertRate:`` and
+    press *Connect*. 
+   
+10. We know our program can't invert rates yet, because we haven't actually
+    written the code to do it, but we are going to try it anyway, just to see
+    what sort of spectacular crash we get.  Alas, nothing spectacular about it:
+    when the NIB is loaded the Cocoa runtime system tries to make the
+    connection, notices that we have no ``invertRate_()`` method in our
+    ``ConverterController`` class and it gives an error message::
+       
+       $ ./dist/CurrencyConverter.app/Contents/MacOS/CurrencyConverter 
+       2004-12-09 03:29:09.957 CurrencyConverter[4454] Could not connect the action 
+       invertRate: to target of class ConverterController
+   
+    Moreover, it has disabled the ``Invert Exchange Rate`` menu command and
+    continues, so the program works as it did before, only with one more
+    (disabled) menu item.
+   
+Debugging
+---------
+
+11. Writing the code is easy: add a method ``invertRate_(self, sender)`` that
+    gets the float value of ``rateField``, inverts it and puts it back.  We
+    deliberately forget to test for divide by zero.  We run the program again,
+    and now the menu entry is enabled.  After trying it with a couple of
+    non-zero exchange rates we try it with an exchange rate of zero (or empty,
+    which is the same).  We get a dialog box giving the Python exception, and
+    offering the choice of continuing or quitting. 
+    
+    To debug this application with pdb, start the application with the
+    following command line::
+
+        $ env USE_PDB=1 ./dist/CurrencyConverter.app/Contents/MacOS/CurrencyConverter
+
+    When running in this mode, we will get a ``pdb.post_mortem(...)`` console
+    in the terminal instead of the alert panel.  You can see this in action if
+    you try and invert an exchange rate of ``0``.
+      
+12. Fix the final bug by testing for ``rate == 0.0`` in ``invertRate_()``.
+    The result is in the step12-src_ directory.
+    
+.. _step12-src: step12-src.zip
+   
+Creating a redistributable application
+--------------------------------------
+
+Your application is finished, and you want to run it on other computers, or
+simply just move it to the ``Applications`` folder (or anywhere else) and
+insulate it from the original source code.
+
+This can be done with the following steps from the ``src`` directory::
+
+    $ rm -rf dist
+    $ python setup.py py2app
+
+Now the application bundle located at ``dist/CurrencyConverter.app`` is a fully
+standalone application that should run on any computer running the same major
+version of Mac OS X or later.  This means that applications built on
+Mac OS X 10.2 are compatible with Mac OS X 10.3, but NOT vice versa.  If you
+are not using an Apple-supplied version of Python, a subset of your Python
+installation will be included in this application.
+
+For more complicated examples of py2app usage to do things such as change the
+application's icon, see the Examples or the py2app documentation.

pyobjc-core/Doc/06tutorial/step12-src/CurrencyConverter.py

+from Foundation import NSObject, NSObject
+from PyObjCTools import NibClassBuilder, AppHelper
+
+NibClassBuilder.extractClasses("MainMenu")
+
+
+# class defined in MainMenu.nib
+class Converter(NibClassBuilder.AutoBaseClass):
+    # the actual base class is NSObject
+
+    def convertAmount(self, amt, rate):
+        return amt * rate
+
+# class defined in MainMenu.nib
+class ConverterController(NibClassBuilder.AutoBaseClass):
+    # the actual base class is NSObject
+    # The following outlets are added to the class:
+    # converter
+    # dollarField
+    # rateField
+    # totalField
+
+    def convert_(self, sender):
+        amt = self.dollarField.floatValue()
+        rate = self.rateField.floatValue()
+
+        total = self.converter.convertAmount(rate, amt)
+        self.totalField.setFloatValue_(total)
+        self.rateField.selectText_(self)
+
+    def awakeFromNib(self):
+        self.rateField.window().makeKeyAndOrderFront_(self)
+        self.rateField.selectText_(self)
+
+    def invertRate_(self, sender):
+        rate = self.rateField.floatValue()
+        if rate != 0.0:
+            rate = 1.0 / rate
+        self.rateField.setFloatValue_(rate)
+
+if __name__ == "__main__":
+    AppHelper.runEventLoop()

pyobjc-core/Doc/06tutorial/step12-src/English.lproj/MainMenu.nib/classes.nib

+{
+    IBClasses = (
+        {CLASS = Converter; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, 
+        {
+            ACTIONS = {convert = id; invertRate = id; }; 
+            CLASS = ConverterController; 
+            LANGUAGE = ObjC; 
+            OUTLETS = {
+                converter = id; 
+                dollarField = NSTextField; 
+                rateField = NSTextField; 
+                totalField = NSTextField; 
+            }; 
+            SUPERCLASS = NSObject; 
+        }, 
+        {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }
+    ); 
+    IBVersion = 1; 
+}

pyobjc-core/Doc/06tutorial/step12-src/English.lproj/MainMenu.nib/info.nib

+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IBDocumentLocation</key>
+	<string>485 378 356 240 0 0 1280 1002 </string>
+	<key>IBEditorPositions</key>
+	<dict>
+		<key>29</key>
+		<string>52 213 318 44 0 0 1280 1002 </string>
+	</dict>
+	<key>IBFramework Version</key>
+	<string>286.0</string>
+	<key>IBOpenObjects</key>
+	<array>
+		<integer>29</integer>
+		<integer>21</integer>
+	</array>
+	<key>IBSystem Version</key>
+	<string>6I32</string>
+</dict>
+</plist>
Add a comment to this file

pyobjc-core/Doc/06tutorial/step12-src/English.lproj/MainMenu.nib/objects.nib

Binary file added.

pyobjc-core/Doc/06tutorial/step12-src/setup.py

+from distutils.core import setup
+import py2app
+
+setup(
+    app=['CurrencyConverter.py'],
+    data_files=['English.lproj'],
+)

pyobjc-core/Doc/06tutorial/step3-MainMenu.nib/classes.nib

+{
+    IBClasses = (
+        {CLASS = Converter; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, 
+        {
+            ACTIONS = {convert = id; }; 
+            CLASS = ConverterController; 
+            LANGUAGE = ObjC; 
+            OUTLETS = {
+                converter = id; 
+                dollarField = NSTextField; 
+                rateField = NSTextField; 
+                totalField = NSTextField; 
+            }; 
+            SUPERCLASS = NSObject; 
+        }, 
+        {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }
+    ); 
+    IBVersion = 1; 
+}

pyobjc-core/Doc/06tutorial/step3-MainMenu.nib/info.nib

+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IBDocumentLocation</key>
+	<string>363 297 356 240 0 0 1680 1028 </string>
+	<key>IBEditorPositions</key>
+	<dict>
+		<key>29</key>
+		<string>74 219 318 44 0 0 1680 1028 </string>
+	</dict>
+	<key>IBFramework Version</key>
+	<string>349.0</string>
+	<key>IBOpenObjects</key>
+	<array>
+		<integer>21</integer>
+		<integer>29</integer>
+	</array>
+	<key>IBSystem Version</key>
+	<string>7F44</string>
+</dict>
+</plist>
Add a comment to this file

pyobjc-core/Doc/06tutorial/step3-MainMenu.nib/objects.nib

Binary file added.

pyobjc-core/Doc/06tutorial/step4-CurrencyConverter.py

+import objc
+from Foundation import *
+from AppKit import *
+from PyObjCTools import NibClassBuilder, AppHelper
+
+
+NibClassBuilder.extractClasses("MainMenu")
+
+
+# class defined in MainMenu.nib
+class Converter(NibClassBuilder.AutoBaseClass):
+    # the actual base class is NSObject
+    pass
+
+
+# class defined in MainMenu.nib
+class ConverterController(NibClassBuilder.AutoBaseClass):
+    # the actual base class is NSObject
+    # The following outlets are added to the class:
+    # converter
+    # dollarField
+    # rateField
+    # totalField
+
+    def convert_(self, sender):
+        pass
+
+
+
+if __name__ == "__main__":
+    AppHelper.runEventLoop()

pyobjc-core/Doc/06tutorial/step5-setup.py

+from distutils.core import setup
+import py2app
+
+setup(
+    app=['CurrencyConverter.py'],
+    data_files=['MainMenu.nib'],
+)

pyobjc-core/Doc/06tutorial/step8-CurrencyConverter.py

+import objc
+from Foundation import *
+from AppKit import *
+from PyObjCTools import NibClassBuilder, AppHelper
+
+NibClassBuilder.extractClasses("MainMenu")
+
+
+# class defined in MainMenu.nib
+class Converter(NibClassBuilder.AutoBaseClass):
+    # the actual base class is NSObject
+
+    def convertAmount(self, amt, rate):
+        return amt * rate
+
+# class defined in MainMenu.nib
+class ConverterController(NibClassBuilder.AutoBaseClass):
+    # the actual base class is NSObject
+    # The following outlets are added to the class:
+    # converter
+    # dollarField
+    # rateField
+    # totalField
+
+    def convert_(self, sender):
+        amt = self.dollarField.floatValue()
+        rate = self.rateField.floatValue()
+
+        total = self.converter.convertAmount(rate, amt)
+        self.totalField.setFloatValue_(total)
+        self.rateField.selectText_(self)
+
+    def awakeFromNib(self):
+        self.rateField.window().makeKeyAndOrderFront_(self)
+        self.rateField.selectText_(self)
+
+if __name__ == "__main__":
+    AppHelper.runEventLoop()

pyobjc-core/Doc/07tutorial_embed/index.txt

+=============================================================
+Tutorial - Adding Python code to an existing ObjC application
+=============================================================
+
+In this tutorial we are going to take an existing ObjC application and
+add Python and PyObjC to it.  One of the reasons why you may want to do
+this is because some things are much simpler in Python than in ObjC, mainly
+due to the rich library Python has.
+
+To follow the tutorial you need:
+
+ * PyObjC 1.3.1
+ * py2app 0.2 or later (included in the binary installer for PyObjC)
+ * Python 2.3 or later (note: PyObjC is NOT compatible with MacPython-OS9)
+ * Mac OS X 10.3 or later
+ * Xcode Tools
+
+If you do not have a ``/Developer`` folder, then you do not have Xcode Tools
+installed.  See `Getting the Xcode Tools`_.
+
+.. _`Getting the Xcode Tools`: http://developer.apple.com/tools/download/
+
+The application we are going to modify is Apple's SimpleComboBox example.
+This example shows you how to use combo boxes, but that is not what interests
+us right now: the application pretends to be a database application that allows
+you to keep notes (such as track list) for your CD collection.  With such an
+application it feels silly that even though you want to type notes on
+the CD you are currently playing in iTunes you still have to retype
+album title, artist and genre.  This is what we are going to fix: we
+are going to add a button "ask iTunes", which will use Python's
+AppleScript support to ask iTunes about the currently playing track
+and fill in the fields for you.  
+
+Follow these steps:
+
+1. Make a copy of ``/Developer/Examples/AppKit/SimpleComboBox`` to work on.
+   Let's call this ``SimpleComboBoxPlus``::
+   
+    $ cp -R /Developer/Examples/AppKit/SimpleComboBox SimpleComboBoxPlus
+
+  From this point on, all shell commands take place from this
+  ``SimpleComboBoxPlus`` folder.
+    
+2. Open it in Xcode, build it, and see what it does.
+
+3. Open ``CDInfoDocument.nib``.  Select the Class View, ``NSObject``, subclass
+   as ``ITunesCommunication``.  Give the class an ``askITunes:`` action.
+   Instantiate the class as object ``ITunesCommunication``.  This wll be the
+   class that we write in Python.
+   
+4. Go to the object view again, open the Window.
+
+5. Move the text box down a bit to make space, add a button "ask iTunes".
+
+6. Connect this button to the ``askITunes:`` action of the
+   ``ITunesCommunication`` object.
+    
+7. We now need to write the code implementing the ``ITunesCommunication``
+    class.  As this tutorial is about using PyObjC in existing ObjC programs
+    and not about PyObjC itself, we are going to skip writing the code and
+    simply copy ``ITunesCommunication_1.py`` to ``ITunesCommunication.py``.
+
+8. Now we need to create the build script for our plugin, create a file named
+   ``setup.py`` with the following contents::
+
+        from distutils.core import setup
+        import py2app
+
+        setup(
+            plugin = ['ITunesCommunication.py']
+        )   
+
+   You may also copy this file from ``setup.py``. 
+
+9. Run the setup script to create a temporary plugin bundle for development::
+
+        $ python setup.py py2app -A
+
+   Note that we use the ``-A`` argument to create an alias plugin bundle at
+   ``dist/ITunesCommunication.py``.  Alias bundles contain an alias to the
+   main script (``ITunesCommunication.py``) and symlinks to the data files
+   (none in this case).  This allows us to keep working on the source files
+   without having to rebuild the application.  This alias bundle is similar
+   to a ZeroLink executable in Xcode - it is for DEVELOPMENT ONLY, and will
+   not work on other machines.
+
+10. Add ``dist/ITunesCommunication.plugin`` to the Resources folder in your
+    Xcode project.  You can do this by ctrl-clicking the Resources folder
+    and choosing "Add Existing Files...".  Make sure to choose
+    "Create Folder References for any added folders".
+
+11. Open ``main.m``, it is in the "Other Sources" folder in your Xcode
+    project, and change the main(...) function to the following::
+
+        int main(int argc, const char *argv[]) {
+            NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+            NSString *pluginPath = [[NSBundle mainBundle]
+                                        pathForResource:@"ITunesCommunication"
+                                                 ofType:@"plugin"];
+            NSBundle *pluginBundle = [NSBundle bundleWithPath:pluginPath];
+            [pluginBundle load];
+            [pool release];
+            return NSApplicationMain(argc, argv);
+        }
+
+
+    You may also copy a full main.m from ``main.m``.  This code ensures
+    that our ITunesCommunication plugin is loaded before the nib
+    files.
+
+12. Build and run.  When you press the "Ask iTunes" the "CD Title" and
+    "Band Name" fields will be filled with one of the best albums of the last
+    few years :-)
+    
+13. Now we need to make the program talk to iTunes.  The current MacPython
+    interface to the Open Scripting Architecture requires an extra step when
+    compared to AppleScript: you need to manually generate a Python package
+    that wraps all the AppleScript terminology for an application.  To make
+    matters more complicated iTunes is one of those special cases where the
+    standard way to generate this package (start the application, ask it for
+    its terminology) does not work, so we have to actually look into the
+    bowels of ``iTunes.app``.  This leads to the following hefty command line
+    which you should run in the ``SimpleComboBoxPlus`` directory::
+    
+        $ cd SimpleComboBoxPlus
+        $ pythonw -c "from gensuitemodule import main;main()" \
+            --output iTunes --creator hook --resource \
+            /Applications/iTunes.app/Contents/Resources/iTunes.rsrc
+    
+14. Finally, add the code to ``ITunesCommunication.py`` to actually communicate
+    with iTunes.  We cop out and copy it from ``ITunesCommunication_2.py``.
+    
+15. Build and run.  If you press the button when iTunes is playing the Title
+    and Band names will be filled, otherwise they will be cleared.  In a real
+    application you would disable the "Ask iTunes" button unless iTunes was
+    active.  All that is left as an exercise to the reader.
+
+16. To make this application redistributable, perform the following commands
+    to make the plugin redistributable::
+
+        $ rm -rf dist
+        $ python setup.py py2app
+
+    Then, from Xcode, clean your project (shift-cmd-K), switch to Deployment
+    mode, and rebuild.
+ 
+A minor variation
+-----------------
+
+There a several projects that improve upon the built-in AppleScript support
+(or to be more precise "application scripting support").  One of those is
+`AppScript`_.
+
+.. _`AppScript`: http://freespace.virgin.net/hamish.sanderson/appscript.html
+
+When you have this module installed you can replace the contents of
+``ITunesCommuncation.py`` with ``ITunesCommunication_AppScript.py``,
+and you can skip step 13 entirely.

pyobjc-core/Doc/07tutorial_embed/src/ITunesCommunication_1.py

+#
+#  ITunesCommunication.py
+#  PyObjC2Test
+#
+#  Created by Jack Jansen on Tue Jun 17 2003.
+#  Copyright (c) 2003 __MyCompanyName__. All rights reserved.
+#
+
+import objc
+from Foundation import *
+from AppKit import *
+
+from PyObjCTools import NibClassBuilder
+
+# We tell NibClassBuilder to examine and remember all
+# classes from the CDInfoDocument NIB file. This way,
+# we can subclass our ITunesCommunication from AutoBaseClass
+# later on, and its actual baseclass will be ITunesCommunication
+# from the NIB file.
+# Since the NIB files are in the application, NOT the plugin, we
+# need to specify this explicitly.  Typicaly, NIB files would be in the
+# plugins.
+NibClassBuilder.extractClasses("CDInfoDocument", bundle=NSBundle.mainBundle())
+
+class ITunesCommunication(NibClassBuilder.AutoBaseClass):
+    def init(self):
+        self = super(ITunesCommunication, self).init()
+        if self is None:
+            return None
+        # subclass specific initialization here
+        # nib not loaded yet
+        return self
+
+    def askITunes_(self, obj):
+        # obj is the button the user pressed. We can go from here
+        # to the document (an instance of CDInfoDocument)
+        document = obj.window().windowController().document()
+        document.setBandName_(u"Uit de Sloot")
+        document.setCDTitle_(u"En Snel Een Beetje")
+        document.setMusicGenre_(u"Punkrock")

pyobjc-core/Doc/07tutorial_embed/src/ITunesCommunication_2.py

+#
+#  ITunesCommunication.py
+#  PyObjC2Test
+#
+#  Created by Jack Jansen on Tue Jun 17 2003.
+#  Copyright (c) 2003 __MyCompanyName__. All rights reserved.
+#
+
+import objc
+from Foundation import *
+from AppKit import *
+
+from PyObjCTools import NibClassBuilder
+
+import iTunes
+
+# We tell NibClassBuilder to examine and remember all
+# classes from the CDInfoDocument NIB file. This way,
+# we can subclass our ITunesCommunication from AutoBaseClass
+# later on, and its actual baseclass will be ITunesCommunication
+# from the NIB file.
+# Since the NIB files are in the application, NOT the plugin, we
+# need to specify this explicitly.  Typicaly, NIB files would be in the
+# plugins.
+NibClassBuilder.extractClasses("CDInfoDocument", bundle=NSBundle.mainBundle())
+
+class ITunesCommunication(NibClassBuilder.AutoBaseClass):
+    def init(self):
+        self = super(ITunesCommunication, self).init()
+        if self is None:
+            return None
+        # subclass specific initialization here
+        # nib not loaded yet
+        self.itunes = iTunes.iTunes()
+        return self
+
+    def getITunesInfo(self):
+        curtrk = self.itunes.current_track
+        try:
+            current_track = self.itunes.get(curtrk)
+        except iTunes.Error:
+            NSRunAlertPanel(
+                u'iTunes Communication Error',
+                u'iTunes failed to return the current track',
+                None,
+                None,
+                None)
+            return None
+        album = self.itunes.get(current_track.album)
+        artist = self.itunes.get(current_track.artist)
+        genre = self.itunes.get(current_track.genre)
+        return album, artist, genre
+
+    def askITunes_(self, obj):
+        # obj is the button the user pressed. We can go from here
+        # to the document (an instance of CDInfoDocument)
+        document = obj.window().windowController().document()
+        # Try to get the iTunes info
+        info = self.getITunesInfo()
+        if info is None:
+            return
+        album, artist, genre = info
+        document.setCDTitle_(album)
+        document.setBandName_(artist)
+        document.setMusicGenre_(genre)

pyobjc-core/Doc/07tutorial_embed/src/ITunesCommunication_AppScript.py

+#######
+
+#
+#  ITunesCommunication.py
+#  PyObjC2Test
+#
+#
+# This is a variation on the ITunesCommuncation that uses a the 'appscript'
+# module for the inter-application communication.
+
+import objc
+from Foundation import *
+from AppKit import *
+
+from PyObjCTools import NibClassBuilder
+
+from appscript import *
+
+# We tell NibClassBuilder to examine and remember all
+# classes from the CDInfoDocument NIB file. This way,
+# we can subclass our ITunesCommunication from AutoBaseClass
+# later on, and its actual baseclass will be ITunesCommunication
+# from the NIB file.
+# Since the NIB files are in the application, NOT the plugin, we
+# need to specify this explicitly.  Typicaly, NIB files would be in the
+# plugins.
+NibClassBuilder.extractClasses("CDInfoDocument", bundle=NSBundle.mainBundle())
+
+class ITunesCommunication(NibClassBuilder.AutoBaseClass):
+    def init(self):
+        self = super(ITunesCommunication, self).init()
+        if self is None:
+            return None
+        return self
+
+    def getITunesInfo(self):
+        try:
+            track = app('iTunes.app').current_track.get()
+        except Exception:
+            NSRunAlertPanel(
+                u'iTunes Communication Error',
+                u'iTunes failed to return the current track',
+                None,
+                None,
+                None)
+        else:
+            return track.album.get(), track.artist.get(), track.genre.get()
+
+    def askITunes_(self, obj):
+        # obj is the button the user pressed. We can go from here
+        # to the document (an instance of CDInfoDocument)
+        document = obj.window().windowController().document()
+        # Try to get the iTunes info
+        info = self.getITunesInfo()
+        if info is None:
+            return
+        album, artist, genre = info
+        document.setCDTitle_(album)
+        document.setBandName_(artist)
+        document.setMusicGenre_(genre)

pyobjc-core/Doc/07tutorial_embed/src/main.m

+#import <AppKit/AppKit.h>
+
+int main(int argc, const char *argv[]) {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSString *pluginPath = [[NSBundle mainBundle] pathForResource:@"ITunesCommunication" ofType:@"plugin"];
+    NSBundle *pluginBundle = [NSBundle bundleWithPath:pluginPath];
+    [pluginBundle load];
+    [pool release];
+    return NSApplicationMain(argc, argv);
+}

pyobjc-core/Doc/07tutorial_embed/src/setup.py

+from distutils.core import setup
+import py2app
+
+setup(
+    plugin = ['ITunesCommunication.py']
+)

pyobjc-core/Doc/10PyObjCTools.txt

+===============================
+PyObjCTools: The PyObjC Toolbox
+===============================
+
+Introduction
+------------
+
+The package ``PyObjCTools`` contains a number of (basically unrelated) modules
+with useful functionality. These have been placed inside a module to avoid
+cluttering the global namespace.
+
+The rest of this document provides documentation for these modules, but lets
+start with a short overview.
+
+* ``PyObjCTools.AppHelper``
+
+Utility functions for use with the ``AppKit`` module.
+
+* ``PyObjCTools.Conversion``
+
+Functions for converting between Cocoa and pure Python data structures.
+
+* ``PyObjCTools.KeyValueCoding``
+
+A Python API for working with `Key-Value Coding`__. 
+
+.. __: http://developer.apple.com/documentation/Cocoa/Conceptual/KeyValueCoding/
+
+* ``PyObjCTools.NibClassBuilder``
+
+Module containing a magic super-class that can read information about the
+actual super-class and implemented actions and outlets from a NIB file.
+
+* ``PyObjCTools.MachSignals``
+
+Module to make it possible to integrate signal handling into the main
+runloop.
+
+* ``PyObjCTools.Debugging``
+
+Allows logging of NSException stack traces.  This module should only be used
+during development.
+
+* ``PyObjCTools.Signals``
+
+Module that tries to print useful information when the program gets a fatal
+exception. This module should only be used during development.
+
+* ``PyObjCTools.XcodeSupport``
+
+Used by the PyObjC Xcode templates to derive py2app options from an Xcode
+project file.
+
+
+``PyObjCTools.AppHelper``
+.........................
+
+This module exports functions that are useful when working with the
+``AppKit`` framework (or more generally, run loops).
+
+* ``callAfter(func, *args, **kwargs) -> None``
+
+  Call a function on the main thread.  Returns immediately.
+
+* ``callLater(delay, func, *args, **kwargs) -> None``
+
+  Call a function on the main thread after a delay.  Returns immediately.
+
+* ``endSheetMethod(method) -> selector``
+
+  Convert a method to a form that is suitable to use as the delegate callback
+  for sheet methods.
+
+* ``stopEventLoop() -> None``
+
+  Stops the event loop (if started by ``runConsoleEventLoop``) or sends the
+  ``NSApplication`` a ``terminate:`` message.
+
+* ``runConsoleEventLoop(argv=None, installInterrupt=False, mode=NSDefaultRunLoopMode) -> None``
+
+  Run a ``NSRunLoop`` in a stoppable way (with ``stopEventLoop``).
+
+* ``runEventLoop(argv=None, unexpectedErrorAlert=unexpectedErrorAlert, installInterrupt=None, pdb=None, main=NSApplicationMain) -> None``
+
+  Run the event loop using ``NSApplicationMain`` and ask the user if we should
+  continue if an exception is caught.
+
+  This function doesn't return unless it throws an exception.
+
+
+``PyObjCTools.Conversion``
+.............................
+
+Functions for converting between Cocoa and pure Python data structures.
+
+* ``propertyListFromPythonCollection(pyCol, conversionHelper=None) -> ocCol``
+
+  Convert a Python collection (dictionary, array, tuple, string) into an 
+  Objective-C collection.
+
+  If conversionHelper is defined, it must be a callable.  It will be called 
+  for any object encountered for which ``propertyListFromPythonCollection()``
+  cannot automatically convert the object.   The supplied helper function 
+  should convert the object and return the converted form.  If the conversion 
+  helper cannot convert the type, it should raise an exception or return None.
+
+* ``pythonCollectionFromPropertyList(ocCol, conversionHelper=None) -> pyCol``
+
+  Converts a Foundation based collection-- a property list-- into a Python 
+  collection.  Like ``propertyListFromPythonCollection()``, ``conversionHelper``
+  is an optional callable that will be invoked any time an encountered object 
+  cannot be converted.
+
+``PyObjCTools.KeyValueCoding``
+..............................
+
+A module for working with Key-Value Coding in Python. Key-Value Coding is
+explained `on the Apple website`__
+
+.. __: http://developer.apple.com/documentation/Cocoa/Conceptual/KeyValueCoding/
+
+This module provides a Python interface to some of that functionality. The
+interface is modeled on the ``getattr`` and ``setattr`` functions.
+
+* ``getKey(object, key) -> value``
+
+    Get the attribute referenced by 'key'. The key is used
+    to build the name of an attribute, or attribute accessor method.
+
+    The following attributes and accesors are tried (in this order):
+
+    - Accessor 'getKey'
+    - Accesoor 'get_key'
+    - Accessor or attribute 'key'
+    - Accessor or attribute 'isKey'
+    - Attribute '_key'
+
+    If none of these exist, raise KeyError
+
+* ``getKeyPath(object, keypath) -> value``
+
+  Like ``getKey`` but using a key path. The ``keypath`` is a sequence of keys
+  separated by dots. It calls ``getKey`` to follow the path and returns the
+  final value.
+
+* ``setKey(object, key, value) -> None``
+
+  Set the value of ``key`` to ``value``.
+
+  The following values are used for setting the value for a key named ``key``
+  (first match wins):
+
+  - Call ``object.set_key(value)``
+
+  - Call ``object.setKey(value)``
+
+  - Call ``object._set_key(value)``
+
+  - Call ``object._setKey(value)``
+
+  - Check if ``_key`` is an attribute and if so, set its value
+
+  - Try to set the attribute ``key``.
+
+  Raises ``KeyError`` if the key cannot be changed.
+
+* ``setKeyPath(object, keypath, value) -> None``
+
+  The same as ``setKey``, but now using a key path. A key path is a sequence