The observer pattern is useful whenever you want a dynamic list of objects (observers) be notified when an event occurs in an observed object (also called the subject).
In this post I am going to contribute an implementation of the observer pattern that resembles the way delegates are implemented in Cocoa. In contrast to Cocoa built-in mechanisms (like NSNotificationCenter) the presented approach enables you to use arbitrary selectors in notification messages (just as is the case with delegates). What is more you won’t have to cope with the details how notification messages are dispatched by the broadcaster.
Note: for what it’s worth, I will call the broadcaster “observable” and the listener “observer” throughout this post. This is in line with the classes I’m providing in my sample code.
Basic Idea
We would like to be able to notify multiple observers about a certain event, giving them a chance to act as appropriate. Just like with delegates, this should be possible using arbitrary selectors.
Let’s take a look at a standard delegation example in Objective-C:
- (void)performTask
{
// Check whether delegate implements -willPerformTask
if ([delegate respondsToSelector: @selector(willPerformTask)]) {
// Notify delegate that we're about to perform the task
[delegate willPerformTask];
}
}
The example above, of course, is limited to one delegate receiving notification messages from the delegator. Now, what we want in certain situations is a collection of observers (multiple “delegates”). All observers should receive the notification message.
It would be neat if the code in your observable object would look something like this:
- (void)performTask
{
// Notify observers that we're about to perform the task
[self.observers willPerformTask];
}
The idea here is that observers acts as a proxy broadcasting the willPerformTask message to all observer objects implementing the message’s selector.
This is exactly what the Observers class that I provide in the Observer sample code will do for you.
A Closer Look
Let’s take a closer look at what we’re talking about here.
The Observer sample provides two classes responsible for implementing observer registration and message broadcasting:
Observable: Abstract base class giving subclasses the ability to easily adopt the role of “generic” broadcasters. Observable maintains an Observers object and allows other objects to add and remove observers.
Observers: This class implements the actual broadcasting mechanism for Objective-C messages to arbitrary observer objects (must be Objective-C objects.) The basic idea here is that all messages sent to Observers are automatically broadcasted to all observers responding to a given selector (implementing/listing to a given notification message.)
An Example
For what follows, let’s assume the following example: we’d like to implement a simple contact manager that is capable of managing a collection of contact names. The contact manager provides two methods, -addContactWithName: and -removeContactWithName:. What is more, our contact manager should be observable, i.e. an arbitrary number of observers should be notified whenever a contact is being added or removed.
Implementing an Observable Contact Manager
Our contact manager is defined in the ContactManager class which inherits from Observable so as to provide for observation capabilities (see ContactManager.h in the sample code):
By inheriting from Observable, ContactManager is equipped with an Observers object which is capable of maintaining a collection of observers and broadcasting messages to them.
As a consequence, within ContactManager’s implementation of -addContactWithName: we can do the following (see ContactManager.m in the sample code):
Note: Observable provides two methods to add and remove observers:
-(void)addObserver:(id)observer; and
-(void)removeObserver:(id)observer;.
Since ContactManager is a Observable subclass, we can later wire observers to the contact manager by using these methods.
Implementing a Contact Manager Observer
The observer may be defined as a simple NSObject subclass providing implementations for the notification selectors it is interested in. Consider the following example (see ContactObserver.h and ContactObserver.m in the sample code):
@interface ContactObserver : NSObject
@end
@implementation ContactObserver
- (void)contactManager:(ContactManager *)manager
didAddContactWithName:(NSString *)name
{
NSLog(@"Observer got notification: contact manager added contact with name %@", name);
}
- (void)contactManager:(ContactManager*)manager
willRemoveContactWithName:(NSString *)name
{
NSLog(@"Observer got notification: contact manager is about to remove contact with name %@", name);
}
@end
Wiring Observers with Observables
Finally, what we have to do to kick things off is wire the observer with the observable. In our example this looks as follows (see main.m in the sample code):
Providing a Syntactically Correct Implementation with Protocols
While the code presented above will work, it is likely to cause compiler warnings due to the fact that observers are of type id. The compiler cannot know for sure that observers are going to respond to messages like contactManager:didAddContactWithName:, because id does not implement these methods.
The best solution here is to provide a protocol for each type of observer. This is not strictly necessary, but it tidies up your code and supports a good understanding of how things are structured.
It’s a good idea to add the protocol to the Observable’s header and name it accordingly.
Viola, by telling the compiler that observers conform to the ContactManagerObserver protocol, there’s nothing to complain about anymore.
Characteristics of the Presented Observer Mechanism
There are some key characteristics of what you can do with observers when implementing them in the way discussed here:
Arbitrary method signatures: you may use selectors with arbitrary signatures for notification messages.
No obligation to handle all observer messages: it is not necessary to implement all methods for handling messages sent by the observable object. That is, in our example an observer may implement -contactManager:didAddContactWithName: only, omitting -contactManager:didRemoveContactWithName:.
Order of notification message dispatch undefined: you should keep in mind that the order in which notifications are sent to observers is not defined with this approach. As a consequence, you should avoid making assumptions on what other observers do in response to an observable notification.
Weak references to observers: the Observers class holds weak references to observer objects to avoid retain cycles. Consequently, you must take care of the observer vs. observable lifecycle on your own.
Limitations
There are some limitations with the presented approach:
Observer method implementations cannot return values: due to the nature of the broadcaster mechanism employed here, observer methods must exhibit a void return type.
Sample code is not optimized for performance: you should not use this approach for performance critical operations. The Observers class implements broadcasting in a fairly naive way, asking all registered observer objects whether they respond to the given selector. For large numbers of observer, this may represent a major performance hit. You should consider using another method in such cases.
Behind the Scenes: How Does Message Forwarding Work?
There is a plethora of documentation on Objective-C message forwarding, and I am not going to re-iterate on that here. However, to give you a basic understanding of what the code in the Observers class does, I provide a brief explanation here. And also there’s one little thing to add that has not yet been discussed profoundly on the web yet—swallowing selectors with an unknown method signature—but more on that later.
Message Forwarding and How To Swallow Messages With Unknown Signature
At the heart of the Observers message forwarding mechanism, there are two methods: methodSignatureForSelector: and forwardInvocation:.
These methods are called by the Objective-C runtime whenever a selector is sent to an object which is not implemented inside the compiled code of the receiver’s class.
Essentially, the runtime first calls methodSignatureForSelector:. If it gets back a valid method signature, it continues with calling forwardInvocation:. This gives custom code a chance to implement message forwarding for messages not implemented directly in the receiver’s class.
Be wary, that the runtime will cause an exception (unrecognized selector sent to instance) when methodSignatureForSelector: returns nil.
Let’s have a look at Observer implementation of methodSignatureForSelector: first:
This method is called by the Objective-C runtime to request a method signature for a given selector. Observers implements this method so as to ask each registered observer whether it is capable of responding to the given selector. If so, the method signature is requested from the first observer exhibiting that capability.
If no observer capable of responding is found, we have to quietly bailout—without causing an exception.
Recall that observables should be able to simply send an arbitrary observer selector to their observers. This means we may run into a situation where no observer implements the given selector. Usually this would cause the runtime to throw an exception, because an unrecognized selector is sent to an instance.
The key to resolving this situation without running into runtime errors is to provide an arbitrary method signature. That is, we just simply ignore that we have no clue about the selector’s designated method signature by returning a default signature. This is possible, because we just want to swallow the invocation later on, i.e. there is no invocation in response to the message being sent to the observers instance.
Finally, this is what the corresponding forwardInvocation: implementation looks like:
This code checks whether there is a registered observer that responds to the given invocation’s selector. If there is one, we can be sure that methodSignatureForSelector: did successfully respond with the correct method signature for the required selector. If not, we simply do nothing. Thus, it is completely irrelevant whether the previously requested method signature was correct or not.
I found one single blog post about Elegant Delegation that explicitly discusses this issue. There is also a post by Matt Gallagher about collecting arbitrary messages using NSInvocation. While that post is great, the method provided there appears overly complex for the problem at hand in this case.
Part 2: Creating a Foundation-based CLI for Ubuntu
In Part 1: Cross Compiling Foundation I provided a step by step tutorial on how to compile the Cocotron Foundation framework for Ubuntu Linux using Xcode 4.
This post is intended to complete the tutorial by explaining how a simple command line tool can be created which will run on Ubuntu Linux using the previously compiled Foundation framework.
After completing this tutorial you will have:
a baseline project for compiling (GUI-less) Objective-C apps on Linux,
Foundation.framework installed on your Ubuntu Linux machine,
basic support for NSUserDefaults on the Linux machine.
The requirements for completing this tutorial are basically the same as those provided in the previous part. What is more, you need to complete part 1 successfully before you may start with part 2.
Step 1: Creating the Base Xcode Project
The first thing we will do is create a new command line tool Xcode project.
Open up Xcode and create a new project by selecting “File” => “New” => “New Project…” from the main menu.
Create a new command line tool project by selecting “Application” => “Command Line Tool” from the new project template wizard.
Name the project “CocotronUbuntuCLI” and choose “Foundation” from the type dropdown.
Step 2: Replacing OS X with Linux Foundation Framework
The standard project template used by Xcode will reference Apple’s Foundation framework, which of course won’t run on Linux. Hence, we will have to replace it with the Cocotron Foundation framework that you built in part 1 of this tutorial.
In the project navigator, expand the “Frameworks” item and remove “Foundation.framework” by selecting it and hitting backspace.
To add the Foundation framework for Linux, right click on the project navigator, select “Add Files to CocotronUbuntuCLI…” from the contextual menu, and locate Cocotron’s Foundation.framework that you built in the last part of this tutorial. Usually this will be located at /Developer/Cocotron/1.0/Linux/i386/Frameworks/ Foundation.framework.
Finally, click “Add”.
Step 3: Configuring the Target’s Build Settings
In order to make Xcode build the application correctly, we will have to change some build settings.
From the project navigator, click on the “CocotronUbuntuCLI” project item. This will open up project settings in the editor.
In the project settings editor, select the “CocotronUbuntuCLI” target from the tree control on the left. Then select “Build Settings” => “All”/”Combined” from the project settings editor’s tabs.
Change “Architecture” to “32-bit Intel”.
Change “Mac OS X Deployment Target” to “Compiler Default”.
Change “C/C++ Compiler Version” to “System default (LLVM GCC 4.2)”.
Change “Implicit Conversion to 32 Bit Type” to “No”.
Step 4: Configuring the Target’s Build Rules and Building the Source
Additionally, we have to add a build rule for C source files, so Xcode uses the correct compiler tool chain to build our command line tool for Linux.
In the project settings editor, select the “CocotronUbuntuCLI” target from the tree control on the left. Then select “Build Rules” => “Target” from the project settings editor’s tabs.
Click the “Add Build Rule” button on the lower right corner of the Xcode window. A new build rule is added to the target.
From the “Process” dropdown select “C source files”; from the “Using” dropdown, select “Cocotron 1.0 Linux i386 gcc default (4.3.1)”.
Finally, build the command line tool by hitting Cmd+B.
Step 5: Installing libm.so.6 on Your OS X Machine
The last step to set up the project for proper building is to get a Foundation framework dependency from your Ubuntu machine, namely libm.so.6. This library was not needed in part 1, but is required when linking against Cocotron’s Foundation.framework. In fact, libm.so.6 is a symlink pointing to libm-2.13.so.
Locate libm-2.13.so on your Ubuntu Linux machine. It is usually located at /lib/i386-linux-gnu/libm-2.13.so.
Copy libm-2.13.so to /Developer/Cocotron/1.0/Linux/i386/gcc-4.3.1/i386-ubuntu-linux/lib.
Create a symlink libm.so.6 -> libm-2.13.so in that directory.
Finally, build the command line tool by hitting Cmd+B.
Step 6: Installing and Testing the Command Line Interface on Ubuntu Linux
Locate Cocotron’s Foundation.framework directory on your Mac (see step 2.2.)
Copy the complete directory to /usr/lib on your Ubuntu Linux machine recursively. Make sure to preserve symlinks.
Within your Linux system’s /usr/lib directory, create a symlink named “libFoundation.so” to /usr/lib/Foundation.framework/libFoundation.so.
Copy the build product from step 5.4 to your user’s home directory on the Ubuntu Linux machine.
Finally, open up a terminal on your Linux and execute the command line tool by typing ./CocotronUbuntuCLI (current working directory must be your home directory.) You should see an NSLog message saying “Hello, World!” if everything worked correctly!
Beyond Porting(?)
I hope this tutorial was helpful for you, what ever you intend to do with Objective-C on Linux.
Of course, porting OS X based code over to Linux is the most obvious use case here. As outlined in my previous post I see great potential in combining both platforms in a client/server manner. Thanks to the Cocotron project, this is now perfectly possible.
However, there are still a number of things I would love to have:
a GUI library (not necessarily an exact Cocoa ripoff) on Linux – this is not currently provided by Cocotron,
the compiler tool chain for building the complete Cocotron frameworks and apps based on that on Linux,
an IDE for developing Objective-C apps on Linux,
and, finally, 64 bit support for Objective-C apps on Linux.
If there are others out there who think that these tools are really needed, please don’t hesitate to contact me. I’m willing to invest some of my free time to contribute to one or two of these projects, if there are fellow devs willing to do the same!
Everyone who knows me knows I’m an advocate of Objective-C, the Foundation framework, and Cocoa. Luckily, in recent years Objective-C has gained wide-spread popularity by virtue of the iPhone/iPad hype. However, there has always been a lack of integration with operating systems not originating from Apple. There are numerous reasons one would want to compile Objective-C based code on Linux, for instance. Imagine you are writing an iPhone multiplayer game and you need a custom server solution for that. Chances are you will want to reuse parts of your client code, but you won’t run an OS X based server farm. If so, you will quickly feel the need for porting lots of Objective-C code over to another language, say C/C++, simply because there is no (easy) way to get your original code working on the server platform.
Fortunately, I am not the only one thinking in that direction and so Christopher Lloyd came up with his great Cocotron project. Cocotron provides an open source implementation of the most important frameworks used with Objective-C on the Mac and iOS platforms. What is more, it comes with a cross compiler integration for Xcode. This allows you to build Objective-C based apps for a number of operating systems, e.g. Windows or Linux.
Both Cocotron and Apple are working constantly, so there is need for a good tutorial of how to get started with Cocotron and the newest Apple developer tools on Linux. This is what this article is all about.
Things You Will Need
A Mac with OS X 10.6.7+ and Xcode4 (latest developer tools from Apple) installed; you will use this for cross compilation
A machine with your target Linux OS installed (we will use Ubuntu 11.04 32-bit here)
The Cocotron source code (get it by typing “$ hg clone http://cocotron.googlecode.com/hg/ cocotron” on the terminal)
Cocotron’s InstallCDT provides an automatized setup and Xcode integration of tool chains for Objective-C cross compilation. Download the latest InstallCDT package and unzip it to a directory on your Mac.
Step 2: Create a Platform Interface for Cocotron Developer Tools
Installing CDT requires some files from your target platform. We are going to get these in this step. Don’t worry, this is already the most complicated part of the story.
In the PlatformInterfaces directory, create a subdirectory named i386-ubuntu-linux.
From your Ubuntu machine, copy the /usr/include directory to /Developer/Cocotron/1.0/PlatformInterfaces/i386-ubuntu-linux/include on your Mac.
Additionally, from your Ubuntu machine, copy the /usr/src/linux-headers-x.x.xx-x/arch/x86/include/asm diectory to /Developer/Cocotron/1.0/PlatformInterfaces/i386-ubuntu-linux/include/asm on your Mac.
In the i386-ubuntu-linux directory, create a subdirectory named lib.
From your Ubuntu Linux machine, copy the following files to that directory from /usr/lib/i386-linux-gnu:
Edit libc.so and libpthread.so using a text editor and replace /usr/lib/i386-linux-gnu and /lib/i386-linux-gnu paths with /Developer/Cocotron/1.0/PlatformInterfaces/i386-ubuntu-linux/lib. [Source]
Run install.sh from your InstallCDT directory as follows:
$ sudo install.sh Linux i386
Finally, change ownership of the /Developer/Cocotron directory as follows:
As an example, let’s compile the Cocotron Foundation project for Ubuntu. Open Xcode4 and load Foundation.xcodeproj from the Cocotron source code. Choose ‘Foundation-Linux-i386′ as active scheme. Build the Cocotron Foundation framework by hitting Cmd+B. If you see ‘Build succeeded’, congratulations, you have taken the first step towards Objective-C on Linux.
Note: the steps described here relate to Ubuntu Linux (11.04). Creating a CDT platform interface for a different platform may require other steps. For more information see Creating a Platform Interface for CDT.
In the next post I am going to make this tutorial complete by explaining how to set up a Cocotron based Xcode project from scratch. That tutorial will also cover installing and running your first Objective-C binary on Ubuntu Linux.
SVN did a great job for me for at least four years now. Lately, I stumbled upon Mercurial and decided to have a closer look. It turned out that Mercurial (also called “hg”) comes with some great advantages, namely:
Not a “Version Control System” (VCS), but a “Distributed Version Control System” (DVCS) enabling you to check in changes locally (offline, without an internet connection). The advantages of this one are also discussed in detail in PEP 374 (Python Foundation).
Available in pure Python, therefore installable on a standard shared web server without root access (not a virtual server, just a “web host”). Thereby cutting my costs of maintaining a root server on my own or being charged by a specialized host for an SVN/VPS plan.
This post is a small guide on how to accomplish the mission of installing Mercurial (the pure Python version) on a shared web host. In this exemplary case a “WebPack Pro” that we rent from our ISP of choice, HostEurope, is going to be used. However, what is described herein should basically work for any web hosting plan that fulfills the following requirements. This article is therefore most interesting for people who already own a shared web hosting plan and wish to use that plan for hg hosting also.
A shared web server at an arbitrary web host enabling execution of CGI scripts written in Python (HostEurope provides Python 2.4 which appears to be sufficient).
mod_python or mod_wsgi (better).
SSH access to that host. This is required to setup Mercurial on your shared web server. No need for root privileges.
Installation (Step by Step)
Unpack the Mercurial source tarball on your local system.
Create a temporary directory on your web server and transfer the extracted Mercurial source tree to that directory. You can do so via SSH or FTP.
SSH into the server (if you did not so already) and create a top-level directory named “python”. This is going to hold all the Python modules you would like to use, but which are not currently installed on the server’s root Python distribution.
Change to the Mercurial source directory and build the module in pure Python mode (instructions adopted from Mercurial Wiki) :
Likewise, install the module. Here, it is essential to enter the correct prefix path, as you want an alternate installation following the prefix scheme, not a standard installation. Please specify the path to your “python” directory, which you created in step 3 (in my case this is “../../python”):
If everything went well setup.py has installed the Mercurial module files to python/lib/python2.4/site-packages and the hg binary to python/bin.
In order to get the hg binary running via SSH you have to export your custom PYTHONPATH so Python knows where to look for the hg modules. Via SSH go to your home directory on the web host and create a .bash_profile file as follows:
$ cd ~
$ touch .bash_profile
Open the file in a text editor and add the following line to it (adjust the path so that it fits your configuration):
Logout and login again via SSH. Type hg. If you get the Mercurial Distribution SCM help screen you now have finished installation and are ready to go for setting up Mercurial web access.
Setting up hgwebdir.cgi
First of all, create a directory on your webspace for hg. This directory must be reachable via HTTP. You may also want to set up a subdomain, e.g. hg.yourdomain.com and link that to your hg web directory.
Make sure your web host is configured appropriately to handle .cgi files and index.cgi files. (You should be able to configure these settings in the scripts settings of your ISP’s administrative UI.)
Open index.cgi using your text editor of choice. It is a good idea to check the script environment statement that is located in the first line of the file. It should look like the following:
#!/usr/bin/env python
This should work on most servers. However, if your python binary is not linked (or located) in /usr/bin/python you may have to correct for this.
Find the following comment # enable importing on demand to reduce startup time. Beneath the comment line there should be two commented lines. Change these lines to something like this:
Replace the absolute path to the site-packages directory with your own one.
If everything works as intended, the script should now be callable via HTTP. Enter the address of your hg web directory into your web browser and check whether the script runs correctly.
Setting Up Your First Repository and Configuring hgweb.config
Via SSH you can setup a first test repository like this:
$ cd /path/to/www/hg
$ hg init test
hg will create a directory named test and place a .hg subdirectory in it containing repository-specific configuration and payload data.
hgwebdir.cgi has to know which repositories to serve. You tell this by creating a file named hgweb.config. Simply put the config file into the directory of hgwebdir.cgi.
Open the config file with a text editor and add the following lines:
[paths]
test=test
Note: more info on how to configure hgwebdir.cgi can be found on HgWebDirStepByStep (in the Mercurial Wiki).
Finally, the repository itself needs some configuration. In test/.hg create a new file named hgrc and add the following lines to it:
This will register a first user, allow for HTTP pushing, and allow pushing for everyone (we will take care of security later on).
That’s it. Your first repository is served via Mercurial on your shared web host.
Testing the Whole Thing
You may want to do a simple test to check whether your repository is working and you are able to pull and push changes as intended. You can do so by opening a terminal and type something like the following:
$ hg clone http://hg.yourdomain.com/index.cgi/test
$ cd test
$ touch testfile
$ echo "This is just a test" > testfile
$ hg add testfile
$ hg commit -m 'Testing...' -u username
$ hg push
If everything went right, this should add the file testfile to the repository root and push it to the web server.
For now, you hopefully have a working Mercurial repository directory service up and running on your shared web host. However, there are two major flaws. Firstly, the repository address is not as beautiful as it could be, because of the index.cgi contained in any path. Secondly, the repositories are accessible for everyone. We will fix these issues in what follows.
Beautifying hgwebdir Paths
In order to strip index.cgi from the repository URLs we will have to use mod_rewrite (and your server needs to support that apache module).
Go to your hg webdir root directory and create a file named .htaccess. Open the file in a text editor and add the following lines:
# Taken and adapted from http://www.pmwiki.org/wiki/Cookbook/CleanUrls#samedir
# Used at http://ggap.sf.net/hg/
RewriteEngine On
#write base depending on where the base url lives
RewriteBase /
RewriteRule ^$ index.cgi [L]
# Send requests for files that exist to those files.
RewriteCond %{REQUEST_FILENAME} !-f
# Send requests to hgwebdir.cgi, appending the rest of url.
RewriteRule (.*) index.cgi/$1 [QSA,L]
Note: this apache script rewrites URLs so that any directory queried in the form hg.yourdomain.com/directory is automatically rewritten to hg.yourdomain.com/index.cgi/directory internally. Consequently, users can omit disturbing repetitive typing of index.cgi in hg repository URLs. Please note that you may have to adapt RewriteBase to your specific server environment. If you do not serve hg repositories via a subdomain you should specify the relative base directory of your hg webdir here instead (this may e.g. be /hg if you serve hg repositories via www.yourdomain.com/hg). The approach is also discussed in PublishingMultipleRepositories on the Mercurial Wiki.
Securing hgwebdir
You can secure your hg web directory in multiple ways. You may want to allow pulling by anyone and pushing only to specific core developers. Detailed information about hg authentication can be found on PublishingMultipleRepositories.
However, my intention is to have private repositories, which I share only with my friends (no public pulling). Me and my friends will use HTTP authentication and I will create a user and password for everyone who is allowed to pull and push. Fortunately, this scenario is relatively simple to configure for everyone who knows about apache authentication configuration using htaccess files.
First of all, use htpasswd to create a password file for hg users. Name the file .hgusers and place it in a location that is not accessible via HTTP. For example, you may want to do it this way:
$ cd /private-path
$ htpasswd -c hgusers username
Enter password twice, etc...
Since we already have a .htaccess file, it is even more simple. Re-open the file using a text editor and add the following lines at the end of the file:
# Authentication
AuthName "Please enter your username and password."
AuthType Basic
Require valid-user
AuthUserFile /is/htdocs/wpXXXXX_XXX/.hgusers
Please change the path to the .hgusers file conforming to your server environment.
Congratulations! You now have a running, secured, beautified, pure-python hg repository server on your cheap web hosting account.
Remarks
Building Mercurial in pure Python may have drawbacks in terms of performance. This has more thoroughly been discussed on the Mercurial mailing list, see mailing list archive on pure python Mercurial.
WhyNotUnmount 0.3 is out now. Version 0.3 provides a number of stability improvements (multi-threading issues were fixed), improved unmounting of volumes, automatic list updating on device mounting / unmounting, and finally, several updated user interface details.
WhyNotUnmount works on Mac OS X 10.4 (Tiger) and 10.5 (Leopard) and comes as a Universal Binary.
A new openFrameworks-Python Binding for OS X 10.5 is finally available for download. The new binding comes with an Xcode project including the openFrameworks 005 source code and required libraries plus an example of how to use the binding. As in the previous versions, after building the binding using Xcode, you may run the test example as follows: open up a terminal, go to the build/release directory of the binding and type: ‘python animation.py’. The example script should run out of the box. If it does not please don’t hesitate to contact me.
Note: for those who want to build the binding for OS X 10.4, please see the readme file contained in the package. It contains hints on how to build the project using the 10.4 SDK.
Recently I presented the openFrameworks-Python live coding proof of concept on this blog. Unfortunately, the first version was quite unstable, because code changes introducing syntax errors would crash the running application framework.
This has now been solved as suggested by Maddi with an exception handling block. That is, as soon as draw.py has syntax errors in it, the draw code’s exception is caught by the Python code in animation.py. Rendering stops under this condition, but does not crash the framework. What is more the type of exception is outputted on stdout. As soon as the error is corrected, rendering is automatically resumed.
Unfortunately stderr is still redirected to nirvana inside the overridden functions called by the openframeworks library. I appreciate any suggestions on this issue.
Pythonware’s PIL, the Python Imaging Library, is an extension to Python enabling you to process and arrange images quickly. It supports the popular JPEG and PNG image formats and provides font rendering using the freetype2 library. Unfortunately, PIL is not included in the standard Python environment on Mac OS X 10.4/10.5, so you have to download and build it.
First of all, you might find that there is plenty of forum/blog posts, articles etc. on the web attempting to provide useful instructions on how to build and install PIL in the right fashion. However, I found that most of them are telling different things and so I didn’t really trust them. I tried a lot and basically all of them failed. I am going to explain now, why that was.
Following the README what you need in advance is: libjpeg (6b), libpng3 and freetype2. You can get these using fink or MacPorts. Since I don’t like my machine to re-compile all the stuff that is easily available in a binary form, I used fink.
After installing the required libraries via fink I tried to build PIL using the command as described in the README file:
$ python setup.py install
… and all I got was a lousy:
--- TKINTER support ok
--- JPEG support ok
--- ZLIB (PNG/ZIP) support ok
*** FREETYPE2 supported not available
It turned out that the setup.py file did for some reason not search the correct directories for the freetype2 library. Fixing this is quite easy: just set
FREETYPE_ROOT = "/sw/lib/freetype2/lib"
Note: This does only apply as long as you use fink. MacPorts directories differ!
I again built PIL using the setup.py install command. Everything seemed OK this time. So, as suggested by the installer script, I tried to run PIL’s self test now using
python selftest.py
which would result in
Failure in example: _info(Image.open("Images/lena.jpg"))
from line #24 of selftest.testimage
Exception raised:
Traceback (most recent call last):
File "./doctest.py", line 499, in _run_examples_inner
exec compile(source, "", "single") in globs
File "", line 1, in
File "./selftest.py", line 22, in _info
im.load()
File "PIL/ImageFile.py", line 180, in load
d = Image._getdecoder(self.mode, d, a, self.decoderconfig)
File "PIL/Image.py", line 375, in _getdecoder
raise IOError("decoder %s not available" % decoder_name)
IOError: decoder jpeg not available
1 items had failures:
1 of 57 in selftest.testimage
***Test Failed*** 1 failures.
*** 1 tests of 57 failed.
I googled and googled and found a lot of similar results, but no answers unfortunately. After some time I realized that PIL would run from any other directory, but only the selftest.py failed. The reason for this is that in the Imaging-1.1.6 directory there is a PIL.pth file and a PIL subdirectory. Python picks these up and tries to run PIL from there. Of course this cannot succeed, since the library is not built properly for our system there. The solution is to delete both the PIL subdirectory and the PIL.pth file and there it goes – the self test runs through without any failures.
When it gets really cold in winter and you find your hands wrapped in thick gloves, it’s not that easy to pick up an iPhone call fast enough. CHCH.cc has a great solution on MacOSXHints.com: use your nose! I have googled around a bit to find some illustration of this (see YouTube video above).
WhyNotUnmount 0.2 has been released today. A number of bugs are fixed now. WhyNotUnmount will now display all blocking processes regardless of whether they are UI or system processes.