Embedding matplotlib into a Cocoa app (was Re: cocoa backend for matplotlib?)

Hi Michael,


I have an application that is using PyObjC, matplotlib and scipy to
perform some image classification and feature extraction. I am now
attempting to use py2app to bundle it up into a single OS X .app.

So I'm wondering -- have you already got a matplotlib backend done? I've just started work on such a project and don't want to duplicate effort.

Lemme know, so I don't waste my time! (that is, if you're willing to share)


Yes, I do have this working. I can package up numpy/scipy/matplotlib into a .app using py2app, and distribute it. The resulting package is rather large and only compresses to 9 MB, but I haven't yet looked into what I can exclude from the package or using the Apple-supplied Python to reduce size.

I've attached the MatplotlibView class that I made, using code from the matplotlib Cocoa backend. (I'm not using the mouse event interaction yet, so I haven't tested it.) Just insert a NSView into your nib file and set the custom class to MatplotlibView. Then add the view as an outlet in your app delegate. (this is "imageView" in the following code.)

What I then do in my app delegate is first create the initial look. It ends up like this:

     def applicationDidFinishLaunching_(self, notification):

And then in response to user events, I have a function that updates the view:

     def showImage(self):
         axes = self.imageView.axes
         if self.displayimage is 'original':
             axes.imshow(self.fullcolourimage, extent=[0,imsizex,0,imsizey])

         axes.set_xlim(0, imsizex)
         axes.set_ylim(0, imsizey)



This will now work fine as long as your app is created using "setup.py py2app --alias".
There are a few problems when you want to distribute it and just use the standalone "setup.py py2app".

First, in your setup.py you will need to include the matplotlib data files:

import matplotlib, glob
mpldata = ''.join([matplotlib.rcParams['datapath'], r'/*'])
matplotlibdata = glob.glob(mpldata)
setup_options['data_files'].append((u'matplotlibdata', matplotlibdata))

This will include the files in your application bundle. When the mpl-data directory was moved, the code that allowed any data directory to be used was commented out. I am not sure, but I think it applied to win32 only anyway. So what you need to do next is edit the matplotlib __init__.py, find the lines below "CODE ADDED TO SUPPORT PY2EXE", and uncomment them. Remove the "if sys.platorm == 'win32'" (or similar) condition as well, to give:

     if sys.frozen: ### Remove "sys.platform == 'win32' and" from here
         path = os.path.join(os.path.split(sys.path[0])[0], 'matplotlibdata')
         if os.path.isdir(path): return path
             # Try again assuming sys.path[0] is a dir not a exe
             path = os.path.join(sys.path[0], 'matplotlibdata')
             if os.path.isdir(path): return path

There also seems to be a problem with the way numpy/scipy handles being frozen, so you need to actually expand the site-packages.zip into a site-packages directory in the same location (and then remove the zip). Eg:

unzip -q build/XXX.app/Contents/Resources/Python/site-packages.zip -d build/XXX.app/Contents/Resources/Python/site-packages
rm build/Bovillator.app/Contents/Resources/Python/site-packages.zip

And you are done! This should help with you embedding matplotlib into an application. I have also developed a mixin class that lets you easily add Cocoa bindings to Python code with only one line of code. If you want it let me know.

I am also posting this to the matplotlib user mailing list. Since these problem issues also might affect others, I will post a shorter version detailing these to matplotlib devel and numpy mailing lists.


MatplotlibView.py (3.25 KB)


On 19/02/2006, at 1:51 PM, Michael Bentley wrote: