Anonymous avatar Anonymous committed 3f54ac0

First stab at a tutorial on extending ObjC programs with Python/PyObjC.

Comments (0)

Files changed (7)

pyobjc/Doc/tutorial_embed/extending_objc_with_python.html

+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.2.8: http://docutils.sourceforge.net/" />
+<title>Tutorial - Adding Python code to an existing ObjC application</title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document" id="tutorial-adding-python-code-to-an-existing-objc-application">
+<h1 class="title">Tutorial - Adding Python code to an existing ObjC application</h1>
+<p>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.</p>
+<p>At the time of this writing this tutorial only works with a framework-based
+python 2.3 (also known as MacPython-2.3), it does not work with Apple's
+<tt class="literal"><span class="pre">/usr/bin/python</span></tt> 2.2. The reason for this is that Apple forgot to ship
+the Python dynamic library, so you cannot embed this Python in your
+application.</p>
+<p>You will also need the Apple Developer Kit, and it is expected you are familiar
+with using Project Builder, Interface Builder and Terminal. MacPython is expected in the
+standard location (<tt class="literal"><span class="pre">/Library/Frameworks</span></tt>, with auxiliary executables
+in <tt class="literal"><span class="pre">/usr/local/bin</span></tt> and on your <tt class="literal"><span class="pre">$PATH</span></tt>).</p>
+<p>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 &quot;ask iTunes&quot;, which will use Python's
+AppleScript support to ask iTunes about the currently playing track
+and fill in the fields for you.</p>
+<p>Follow these steps:</p>
+<ol class="arabic">
+<li><p class="first">Make a copy of <tt class="literal"><span class="pre">/Developer/Examples/AppKit/SimpleComboBox</span></tt> to work on.
+Let's call this <tt class="literal"><span class="pre">SimpleComboBoxPlus</span></tt>:</p>
+<pre class="literal-block">
+% cp -R /Developer/Examples/AppKit/SimpleComboBox SimpleComboBoxPlus
+</pre>
+</li>
+<li><p class="first">Open it in Project Builder, build it, and see what it does.</p>
+</li>
+<li><p class="first">Copy <tt class="literal"><span class="pre">PythonGlue.h</span></tt>, <tt class="literal"><span class="pre">PythonGlue.m</span></tt> and <tt class="literal"><span class="pre">PythonGlue.py</span></tt> from <tt class="literal"><span class="pre">src</span></tt>
+into the <tt class="literal"><span class="pre">SimpleComboBoxPlus</span></tt> folder.</p>
+</li>
+<li><p class="first">Add <tt class="literal"><span class="pre">PythonGlue.h</span></tt> and <tt class="literal"><span class="pre">PythonGlue.m</span></tt> to the &quot;Classes&quot; group.</p>
+<blockquote>
+<p>These files contain a class <tt class="literal"><span class="pre">PythonGlue</span></tt> that does nothing
+visibly useful, but it has interesting side effects: when the first
+class instance is initialized it will initialize the Python interpreter,
+add the Resource folder of your application to the Python search path
+and import any modules from that Resources folder. The first bit of
+this is done by the ObjC code, the latter two by the Python code
+in <tt class="literal"><span class="pre">PythonGlue.py</span></tt>.</p>
+</blockquote>
+</li>
+<li><p class="first">Add <tt class="literal"><span class="pre">PythonGlue.py</span></tt> to the &quot;Resources&quot; group.</p>
+</li>
+<li><p class="first">&quot;Add Framework...&quot;, select <tt class="literal"><span class="pre">/Library/Frameworks/Python.framework</span></tt>.</p>
+</li>
+<li><p class="first">Now we need to arrange to have a <tt class="literal"><span class="pre">PythonGlue</span></tt> object be instantiated
+being called early during startup. A good place to do this for the
+current application is when <tt class="literal"><span class="pre">MainMenu.nib</span></tt> is loaded, as it does not
+contain any Python dependencies itself. Subclass <tt class="literal"><span class="pre">NSObject</span></tt> and call
+the new class PythonGlue. Instantiate it. Use this instance as the
+application delegate for the File Owner.</p>
+<blockquote>
+<p>I think this method will not work if you want to use Python-based
+classes in your main NIB file. Suggestions for how to make sure
+a PythonGlue object is instantiated before our main NIB file is read are
+hereby requested.</p>
+</blockquote>
+</li>
+<li><p class="first">Now compile, build and run. You will get a message printed to
+standard output (&quot;PythonGlue: Warning: no Python modules found&quot;)
+but all else should be well.</p>
+</li>
+<li><p class="first">Open <tt class="literal"><span class="pre">CDInfoDocument.nib</span></tt>. Select the Class View, <tt class="literal"><span class="pre">NSObject</span></tt>, subclass
+as <tt class="literal"><span class="pre">ITunesCommunication</span></tt>. Give the class an <tt class="literal"><span class="pre">askITunes:</span></tt> action.</p>
+</li>
+<li><p class="first">Go to the object view again, open the Window.</p>
+</li>
+<li><p class="first">Move the text box down a bit to make space, add a button &quot;ask iTunes&quot;.</p>
+</li>
+<li><dl class="first">
+<dt>Connect this button to the <tt class="literal"><span class="pre">askITunes:</span></tt> action of the <tt class="literal"><span class="pre">ITunesCommunication</span></tt></dt>
+<dd><p class="first last">object.</p>
+</dd>
+</dl>
+</li>
+<li><p class="first">We now need to write the code implementing the <tt class="literal"><span class="pre">ITunesCommunication</span></tt> class.
+Create a file <tt class="literal"><span class="pre">ITunesCommunication.py</span></tt> in the Resources group. As this tutorial
+is about using PyObjC in existing ObjC programs and not about PyObjC itself
+we are going to skip the code itself and simply copy it from <tt class="literal"><span class="pre">src/ITunesCommunication_1.py</span></tt>.
+Note that this is not the final code yet, it is a debug version that does not
+yet talk to iTunes.</p>
+</li>
+<li><p class="first">Build and run. When you press the &quot;Ask iTunes&quot; the &quot;CD Title&quot; and &quot;Band Name&quot;
+fields will be filled with one of the best albums of the last few years:-)</p>
+</li>
+<li><p class="first">Now we need to make the program talk to iTunes. The MacPython implementation
+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 <tt class="literal"><span class="pre">iTunes.app</span></tt>. This leads to
+the following hefty command line which you should run in the
+<tt class="literal"><span class="pre">SimpleComboBoxPlus</span></tt> directory:</p>
+<pre class="literal-block">
+% setenv FWPYTHON /Library/Frameworks/Python.framework/Versions/Current
+% pythonw $FWPYTHON/lib/python2.3/plat-mac/gensuitemodule.py \
+        --output iTunes --resource --creator hook \
+        /Applications/iTunes.app/Contents/Resources/iTunes.rsrc
+</pre>
+<p>This assumes MacPython is installed in the standard place and <tt class="literal"><span class="pre">pythonw</span></tt>
+is on your $PATH.</p>
+</li>
+<li><p class="first">Add the generated <tt class="literal"><span class="pre">iTunes</span></tt> package to your project: select the &quot;Resources&quot;,
+and add <tt class="literal"><span class="pre">iTunes</span></tt>. Add it as a folder reference, not as a recursive group.</p>
+</li>
+<li><p class="first">Finally, add the code to <tt class="literal"><span class="pre">ITunesCommunication.py</span></tt> to actually communicate
+with iTunes. We cop out and copy it from <tt class="literal"><span class="pre">src/ITunesCommunication_2.py</span></tt>.</p>
+</li>
+<li><p class="first">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 probably put up a dialog in this case. Actually, in a real application
+you would disable the &quot;Ask iTunes&quot; button unless iTunes was active. All that
+is left as an exercise to the reader.</p>
+</li>
+</ol>
+</div>
+</body>
+</html>

pyobjc/Doc/tutorial_embed/extending_objc_with_python.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.
+
+At the time of this writing this tutorial only works with a framework-based
+python 2.3 (also known as MacPython-2.3), it does not work with Apple's
+``/usr/bin/python`` 2.2. The reason for this is that Apple forgot to ship
+the Python dynamic library, so you cannot embed this Python in your
+application.
+
+You will also need the Apple Developer Kit, and it is expected you are familiar
+with using Project Builder, Interface Builder and Terminal. MacPython is expected in the
+standard location (``/Library/Frameworks``, with auxiliary executables
+in ``/usr/local/bin`` and on your ``$PATH``).
+
+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
+	
+2. Open it in Project Builder, build it, and see what it does.
+
+3. Copy ``PythonGlue.h``, ``PythonGlue.m`` and ``PythonGlue.py`` from ``src``
+   into the ``SimpleComboBoxPlus`` folder.
+   
+4. Add ``PythonGlue.h`` and ``PythonGlue.m`` to the "Classes" group.
+
+	These files contain a class ``PythonGlue`` that does nothing
+	visibly useful, but it has interesting side effects: when the first
+	class instance is initialized it will initialize the Python interpreter,
+	add the Resource folder of your application to the Python search path
+	and import any modules from that Resources folder. The first bit of
+	this is done by the ObjC code, the latter two by the Python code
+	in ``PythonGlue.py``.
+
+5. Add ``PythonGlue.py`` to the "Resources" group.
+
+6. "Add Framework...", select ``/Library/Frameworks/Python.framework``.
+
+7. Now we need to arrange to have a ``PythonGlue`` object be instantiated
+   being called early during startup. A good place to do this for the
+   current application is when ``MainMenu.nib`` is loaded, as it does not
+   contain any Python dependencies itself. Subclass ``NSObject`` and call
+   the new class PythonGlue. Instantiate it. Use this instance as the
+   application delegate for the File Owner.
+   
+	I think this method will not work if you want to use Python-based
+	classes in your main NIB file. Suggestions for how to make sure
+	a PythonGlue object is instantiated before our main NIB file is read are
+	hereby requested.
+   
+8. Now compile, build and run. You will get a message printed to
+   standard output ("PythonGlue: Warning: no Python modules found")
+   but all else should be well.
+   
+9. Open ``CDInfoDocument.nib``. Select the Class View, ``NSObject``, subclass
+   as ``ITunesCommunication``. Give the class an ``askITunes:`` action.
+   
+10. Go to the object view again, open the Window.
+
+11. Move the text box down a bit to make space, add a button "ask iTunes".
+
+12. Connect this button to the ``askITunes:`` action of the ``ITunesCommunication``
+	object.
+	
+13. We now need to write the code implementing the ``ITunesCommunication`` class.
+    Create a file ``ITunesCommunication.py`` in the Resources group. As this tutorial
+    is about using PyObjC in existing ObjC programs and not about PyObjC itself
+    we are going to skip the code itself and simply copy it from ``src/ITunesCommunication_1.py``.
+    Note that this is not the final code yet, it is a debug version that does not
+    yet talk to iTunes.
+
+14. 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:-)
+    
+15. Now we need to make the program talk to iTunes. The MacPython implementation
+    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::
+    
+    	% setenv FWPYTHON /Library/Frameworks/Python.framework/Versions/Current
+    	% pythonw $FWPYTHON/lib/python2.3/plat-mac/gensuitemodule.py \
+    		--output iTunes --resource --creator hook \
+    		/Applications/iTunes.app/Contents/Resources/iTunes.rsrc
+    		
+    This assumes MacPython is installed in the standard place and ``pythonw``
+    is on your $PATH.
+    
+16. Add the generated ``iTunes`` package to your project: select the "Resources",
+    and add ``iTunes``. Add it as a folder reference, not as a recursive group.
+    
+17. Finally, add the code to ``ITunesCommunication.py`` to actually communicate
+    with iTunes. We cop out and copy it from ``src/ITunesCommunication_2.py``.
+    
+18. 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 probably put up a dialog in this case. Actually, 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.
+   

pyobjc/Doc/tutorial_embed/src/ITunesCommunication_1.py

+#
+#  ITunesCommunication.py
+#  PyObjC2Test
+#
+#  Created by Jack Jansen on Tue Jun 17 2003.
+#  Copyright (c) 2003 __MyCompanyName__. All rights reserved.
+#
+
+from objc import YES, NO
+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.
+NibClassBuilder.extractClasses("CDInfoDocument")
+
+class ITunesCommunication(NibClassBuilder.AutoBaseClass):
+    def init(self):
+        self = super(MyDocument, self).init()
+        if self:
+            # subclass specific initialization here
+            # nib not loaded yet
+            pass
+        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_("Uit de Sloot")
+        document.setCDTitle_("En Snel Een Beetje")
+        document.setMusicGenre_("Punkrock")
+

pyobjc/Doc/tutorial_embed/src/ITunesCommunication_2.py

+#
+#  ITunesCommunication.py
+#  PyObjC2Test
+#
+#  Created by Jack Jansen on Tue Jun 17 2003.
+#  Copyright (c) 2003 __MyCompanyName__. All rights reserved.
+#
+
+from objc import YES, NO
+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.
+NibClassBuilder.extractClasses("CDInfoDocument")
+
+class ITunesCommunication(NibClassBuilder.AutoBaseClass):
+    def init(self):
+        self = super(ITunesCommunication, self).init()
+        if self:
+            # 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:
+            print "iTunes failed to return current track"
+            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:
+            album, artist, genre = info
+        else:
+            album = ""
+            artist = ""
+            genre = ""
+        document.setCDTitle_(album)
+        document.setBandName_(artist)
+        document.setMusicGenre_(genre)
+

pyobjc/Doc/tutorial_embed/src/PythonGlue.h

+#import <Cocoa/Cocoa.h>
+
+@interface PythonGlue : NSObject
+{
+}
+
+- init;
+@end

pyobjc/Doc/tutorial_embed/src/PythonGlue.m

+#import <Foundation/Foundation.h>
+#import "PythonGlue.h"
+#import <Python/Python.h>
+#import <stdio.h>
+
+@implementation PythonGlue
+
+- init
+{
+    static id _singleton;
+    NSString *path;
+    const char *c_path;
+    FILE *fp;
+    
+    if (_singleton) return _singleton;
+    _singleton = self;
+    path = [[[NSBundle mainBundle] resourcePath] 
+              stringByAppendingPathComponent: @"PythonGlue.py"];
+    c_path = [path cString];
+    if ((fp=fopen(c_path, "r")) == NULL) {
+        perror(c_path);
+        return self;
+    }
+    Py_Initialize();
+    PyRun_SimpleFile(fp, c_path);
+    return self;
+}
+
+@end

pyobjc/Doc/tutorial_embed/src/PythonGlue.py

+# Skeleton Python source for embedding Python into ObjC programs.
+import os
+import sys
+
+DEBUG = 0
+
+def main():
+    # First find the Resource folder of the current application
+    resource_folder, ourname = os.path.split(__file__)
+    if DEBUG:
+        print "PythonGlue: resource folder:", resource_folder
+    
+    # Add this folder and the PyObjC subfolder to sys.path
+    sys.path.append(resource_folder)
+    sys.path.append(os.path.join(resource_folder, "PyObjC"))
+    
+    # Now import all modules from the resource folder
+    count = 0
+    for filename in os.listdir(resource_folder):
+        if filename[-3:] == ".py" and filename != ourname:
+            module_name = filename[:-3]
+            if DEBUG:
+                print "PythonGlue: import", module_name
+            __import__(filename[:-3])
+            count = count + 1
+    if count == 0:
+        print "PythonGlue: Warning: no Python modules found"
+            
+if __name__ == '__main__':
+    main()
+    
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.