Adding Views to Table Views with Core Data

I’ve been working on a new Cocoa app for Scientists for a few months now. It’s brand new software, so I’ve used the opportunity to learn Core Data (CD). Recently, I discovered an interesting use for CD: adding non-control NSViews to an NSTableView.

The view I was working with was a spinning NSProgressIndicator. I basically wanted to reproduce the sort of behavior you would see in the Mail app, where the indicator would spin during a refresh. I figured this must be a pretty standard problem, and that a solution would only be as far away as my Google search field in Safari, but that wasn’t altogether true…

The problem is that NSProgressIndicator is not an NSControl, and does not have an associated NSCell, which is required by NSTableView. I did find solutions to this online, it’s just that they weren’t straightforward. For example, I found a discussion on CocoaDev that was very helpful in understanding the issues, but didn’t provide any elegant or complete solutions. The CocoaDev page did provide me with a link to a more complete example by Joar Wingfors. The code in question worked a charm, but it was far from simple, coming in at several hundred lines. Joar’s code did inspire me though, and I have now come up with a solution using Core Data that puts a spinning indicator in my table view with less than 50 lines. Here’s how.

The trick — and this is the part that doesn’t sit completely right with me — is to add an attribute for the NSView to the Core Data entity represented in the table view. My entity was called Host, and it had an boolean attribute called isRefreshing which was set according to whether data for the entity was in the process of updating. To this entity, I added a second attribute calledrefreshProgressIndictor. Importantly, this attribute was made transient, with undefined type, so that Core Data would not attempt to save the NSProgressIndicator to file.

The refreshProgressIndicator attribute gets initialized to a newly created progress indicator in one of the awake... methods of Host:


-(void)commonAwake {
    NSProgressIndicator *indicator =
        [[[NSProgressIndicator alloc] initWithFrame:NSMakeRect(0,0,16,16)]
            autorelease];
    [indicator setStyle:NSProgressIndicatorSpinningStyle];
    [indicator setDisplayedWhenStopped:NO];
    [indicator animate:self];
    [indicator bind:@"animate" toObject:self
        withKeyPath:@"isRefreshing" options:nil];
    [self setValue:indicator forKey:@"refreshProgressIndicator"];
}
	
-(void)awakeFromInsert {
    [super awakeFromInsert];
    [self commonAwake];
}
	
-(void)awakeFromFetch {
    [super awakeFromFetch];
    [self commonAwake];
}

Note also that the animate binding of the progress indicator gets bound to the isRefreshing attribute of the Host. That way, whenever the isRefreshing attribute changes value, the progress indicator will immediately be informed by KVO, and start/stop spinning as appropriate.

I don’t feel good about adding a view like NSProgressIndicator to a model class; it messes with MVC, and disturbs me somewhat. But the solution it leads to is elegant, and given that the attribute is transient, I am able to live with it. How about you?

We now have our progress indicators, one for each Host. The next question is: How will they get displayed in the table view? Not surprisingly, we need some sort of cell. The code for this is extremely minimal. Here is the interface


@interface ViewCell : NSCell {
    NSView *view;
}
	
@end

and here the implementation


@implementation ViewCell
	
-(void)setObjectValue:(id )object {
    view = (id)object;
}
	
-(void)drawInteriorWithFrame:(NSRect)cellFrame
    inView:(NSView *)controlView {
    if( [view superview] != controlView ) {
        [controlView addSubview:view];
    }
    NSSize viewSize = [view frame].size;
    float dx = 0.5f * (cellFrame.size.width - viewSize.width);
    float dy = 0.5f * (cellFrame.size.height - viewSize.height);
    NSRect viewFrame = NSInsetRect(cellFrame, dx, dy);
    [view setFrame:viewFrame];
}
	
@end

The ViewCell assumes that the object passed to it will be the view that will appear in the table view. As the name suggests, it is not specialized to progress indicators, but should work with any NSView. When the table view draws a cell, it first calls setObjectValue. The ViewCell stores the object passed in the instance variable view for use later in the drawing methods.

The drawInteriorWithFrame:inView: method is called to actually draw the cells contents. In this case, rather than do that, the view is added as a subview to the control view passed in. That way, when the progress indicator is redrawn, the control view will also redisplay, and we will end up with an animated progress indicator, rather than a static image.

The ViewCell does not resize the view passed in drawInteriorWithFrame:inView:, but does position it in the center of the cell frame. Variations on this approach are possible, of course, and will depend on your objectives.

The ViewCell is typically created and added to the table view in the awakeFromNib method of anNSArrayController class.


-(void)awakeFromNib {
    [super awakeFromNib];
    NSTableColumn *col =
        [hostsTableView tableColumnWithIdentifier:@"refreshProgress"];
    [col setDataCell:[[[ViewCell alloc] init] autorelease]];
}

All that’s left is to bind the table column in IB to the refreshProgressIndictor attribute of the Hostentity via the arrangedObjects property of the NSArrayController. Once that’s done, theNSProgressIndicator views will be delivered to the setObjectValue: of the ViewCell, and we can conclude that some progress has been made.

Leave a Comment

Filed under Cocoa

Joining the MacResearch Team

I’ve been a regular visitor to the www.macresearch.org web site pretty much since its inception, so I was very pleased to be able to accept an invitation to join the Executive Committee.

MacResearch is a web site that targets the Mac-using Scientist. It provides a wide range of services, including news feeds, software reviews, how-to articles, forums, a script repository, and — most recently — access to a 4-node Xserve computational cluster.

But one of the more important roles that MacResearch has taken on is that of mediator: Polls are held regularly, and the results summarized in a report which is communicated directly to Apple, and released to the community at large. If you want to know more, either visit the site, or check out the new web cast, in which Ivan Judson and Joel Dudley explain it all much more eloquently than I ever could.

What will my role be at MacResearch? To be honest, it’s a bit too early to say. I will certainly contribute content, most probably related to scientific software development in Cocoa, Python, C++, and Fortran. I also have some ideas for applications of Xgrid, but I can’t say much more than that until I find out what the existing MacResearch team have in mind. Whatever happens, I’m sure it will be an interesting ride…

Leave a Comment

Filed under C++, Cocoa, Fortran, Mac, Personal, Python, Scientific Programming

Dustup on Your Desktop

A while back I blogged here about how you can use launchd to regularly run a script to move old files from your Desktop to the Trash. I’ve been getting by with this solution for a while, but decided that I needed an easy way to enable and disable the garbage collection. The Desktop Dustup Widget was the solution I came up with.

Desktop Dustup does pretty much what the launchd script did, except that you can turn it off and on, and it is a hell of a lot better looking. The scheduling of the garbage collection is now taken care of by Javascript, rather than launchd, but the cleanup itself is still handled by a UNIX findcommand.

The design of the icon is due to Marcello Luppi. Marcello is a scientific colleague of mine, and a talented artist. Lately I’ve been encouraging him to get into software-related design like icons and widgets, and Desktop Dustup is his first public exhibition in that vane. (Look out for more substantial ‘fruits’ in the coming months…)

If you have desktop icons piled two or more layers deep, you need Desktop Dustup. And even if you can still make out the labels on the icons on your desktop, Desktop Dustup could save you a few trips to the Trash. So give it a try, and may your Desktop remain eternally uncluttered.

Leave a Comment

Filed under Mac

The Dashcode Mystery

Last week it became apparent that the rumors of Apple working on a Dashboard widget development tool were right on the money, when Dashcode started shipping with new MacBooks. But Apple itself has not made any mention of the fact, and the tool is not present in the recently released update to the Xcode suite. Did Apple deliberately ship Dashcode, or was it an accident?

Well, curiosity may have killed the cat, but it never did me much harm, so I downloaded Dashcode to see what the fuss was all about. At first sight, it looks like an elegant and polished tool, but after actually trying to create a widget with it, I can say unequivocally that Apple did not intend to release it at this juncture in time. Either that, or Apple’s QA department is in dire straights.

The Dashcode that I tested would have to be the buggiest Apple release in history, even surpassing the already legendary iWeb 1.0! Simple things like changing fonts or resizing buttons leave the UI in an invalid state, with drawing flaws that can only be cured by deleting the afflicted element. Undo/Redo also does not seem to work properly in many cases. I can’t believe that such fundamental flaws would have been left unchecked.

My view is that development on Dashcode 1.0 is nearing completion, but that it was not intended to be released just yet. The overwhelming silence coming from Apple would seem to support this. When was the last time Apple released something and didn’t make a song and dance about it? Even a simple home speaker system gets a ticker tape parade.

It’s more logical to assume Dashcode is destined to be officially released in Leopard. We may well hear the real story from Steve Jobs himself at the WWDC in August. Either way, Dashcode looks like a nice addition to Apple’s development tools, and I look forward to the official 1.0 release.

Leave a Comment

Filed under Mac

launchd and Growl for a Cleaner Desktop

I am one of those people that lets things accumulate on their desktop until there is literally no space left, before resigning to a clean up. (This applies equally to my physical desktop as my virtual one.) I finally decided I needed to do something about it, and resolved to leverage two technologies that are relatively new to me: launchd and Growl.

I have known about launchd since its introduction in Tiger, but haven’t had an excuse to use it yet. My plan was to use launchd in its cron-like mode to run a cleanup script once a day. The cleanup script would search for old files and directories on the desktop, and simply move them to the Trash. As a nice touch, I decided to use Growl to notify me when the script had run, with a message detailing how many files and directories had been moved. If you aren’t familiar with Growl, I have just one piece of advice: get familiar. It is a very cool user notification system.

I began with the script, which I called cleandesktop, and added to my ~/bin directory. This is what it looks like:


#!/bin/sh
	
numFiles=`find ~/Desktop -fstype local -type f -maxdepth 1 \
  -ctime +3 | wc | awk '{print $1}' 2>&1`
find ~/Desktop -fstype local -type f -maxdepth 1 \
  -ctime +3 -exec mv -- {} ~/.Trash \; >/dev/null 2>&1
	
numDirs=`find ~/Desktop -fstype local ! -name . -type d -maxdepth 1 \
  -mtime +3 | wc | awk '{print $1}' 2>&1`
find ~/Desktop -fstype local ! -name . -type d -maxdepth 1 -mtime +3 \
  -exec mv -- {} ~/.Trash \;  >/dev/null 2>&1
	
/usr/local/bin/growlnotify "Desktop Cleanup" <<eor
$numFiles files and $numDirs directories were moved to the trash.
eor

This script is basically a number of find commands. Each command has many options, which I based on commands in Apple’s /etc/daily script that is used to clean the /tmp directory. Two of the commands are there just to count files and directories, and the other two do the actual moving. Files and directories are moved to trash after not being accessed for three days; I figure this gives me enough time to move anything I want to keep to a safe place.

Growl comes into it in the last few lines. The growlnotify command allows you to produce notifications from the command line. The title of the notification is given as an argument, and standard input gives a detailed description. In this case, I have reported the number of files and directories moved in the description.

To make the picture complete, I added the following content to the file~/Library/LaunchAgents/DesktopCleanup:


<?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>Label</key>
	<string>net.macanics.desktopcleanup</string>
	<key>ProgramArguments</key>
	<array>
		<string>/Users/drew/bin/cleandesktop</string>
	</array>
	<key>LowPriorityIO</key>
	<true/>
	<key>Nice</key>
	<integer>1</integer>
	<key>StartCalendarInterval</key>
	<dict>
		<key>Hour</key>
		<integer>20</integer>
		<key>Minute</key>
		<integer>15</integer>
	</dict>
</dict>
</plist>

This causes the cleandesktop script to be invoked a 20.15 each day. To load it the first time, without logging out, I used this command:


launchctl load ~/Library/LaunchAgents/DesktopCleanup
launchctl start net.macanics.desktopcleanup

And with that, I may finally be able to make out the Tiger on my desktop … or is it a Panther? Puma? It’s been a while…

Update

After more testing of this approach, it seems that using the -atime option in the find commands leaves a lot of files on the desktop that should be removed. I have now removed these above and in my own scripts, leaving only the -ctime option. This seems to work a lot better.

Leave a Comment

Filed under Cocoa, Mac

Psychic Mac

I just had one of those spooky moments. You know the ones: you are debugging, and find something totally unexpected…something that shouldn’t even be possible.

What I was doing was running a subprocess from within a Cocoa app I am developing. I was usingNSTask to start a script, and retrieving the output with an NSPipe. Nothing complicated about that.

Because I am still in the early stages of development, the script I was using was just a stand-in, to make sure everything was working to plan. It simply wrote a property list to standard output, with a few static values in it. I was planning to rewrite this script later such that it invoked the UNIX command ps, to get information about tasks running on the computer.

To my utter surprise, when I ran the application and examined the script output in the debugger, I saw this:


  PID  TT  STAT      TIME COMMAND
  711  p1  S+     0:00.20 -bash

Hmm, that looks nothing like my property list, and, what’s more, it looks awfully like the output format of the ps command! A quick search of my project revealed no reference whatsoever to ps. What was going on? Was my Mac psychic? Did it know what I was going to do next?

As with many of these ‘How could it be so?’ debugging moments, the answer turned out to be relatively simple, but it had me spooked for a while. When I initially created the script, I hadinserted a ps command into it, but had quickly forgotten that I had done this, because I then changed the script to print the property list. The script file resides in a directory that is copied into the Resources folder of the application bundle when the app is built. The problem seems to have been that Xcode did not recognize that the script needed to be recopied into the application when it was modified. A clean build fixed the problem.

The moral of the story: when confronted with something you don’t understand, your first instinct is to ascribe it to some supernatural power, when the more likely explanation is just that Xcode is buggy.

Leave a Comment

Filed under Cocoa, Mac, Scientific Programming

More Experiences of “The Life”

Luis de la Rosa is blogging about his first encounter with “The Life”, programming full-time for himself. It is a nice follow-up to my blog on “The Life” from a while back.

Leave a Comment

Filed under Cocoa, Mac

Eclipse and PyDev are Worth the Entrance Fee

I’ve been doing some work on Forpedo the last few days, which is a preprocessor for Fortran written in Python. Forpedo currently enables you to use basic generic programming techniques in Fortran programs; I’m now adding options for run-time polymorphism, as described here.

I didn’t want to talk about Forpedo today though, but the IDE Eclipse, and the PyDev plugin in particular. A friend of mine pointed it out to me, and I thought I would give it a try for Python development. In the past, I have tried developing Fortran with the Photran Eclipse plugin, but found it a bit difficult to configure for my Fortran compiler and build system. In the end, I gave up.

My experience with PyDev was very different though. I came across a few bugs, but in general it works as advertised, and was very easy to configure. The editor and code completion are powerful, and it has an outline view that allows you to easily navigate to any class, method, or function in a file. Best of all, it has a graphical debugger, which sure beats debugging from the command line, or dropping print statements into the code to locate problems. Another time saver are the links in the call stack dump that PyDev adds to allow you to jump to a problem spot when a script crashes.

All of these features are to be found in Xcode too, but Xcode only really works well for a handful of languages, and Python isn’t one of them. Working with Objective-C in Xcode has spoiled me, and I always dread having to go back to vi or TextMate — which is a great text editor, by the way — when I have to develop in Fortran or Python. Eclipse seems to be offering me a way out, at least for Python.

The Eclipse IDE itself is actually a very well written cross-platform application. ‘Cross-Platform’ usually equates to ‘Dodgy as Sh.t’, but you would never guess Eclipse was written in Java, and runs equally well on Linux and Windows as the Mac. The secret seems to be the API used to develop the user interface: The Standard Widget Toolkit (SWT). Unlike Java’s other UI libraries, AWT and Swing, SWT utilizes native widgets on each platform. On the Mac, it wraps around Carbon calls, so the windows and buttons you see on the screen are the real McCoy. It makes a world of difference to the look-and-feel of an App.

So it looks like Eclipse might become a permanent addition to my Dock. If you regularly develop in Python, why not take PyDev for a run — it’s the only Python IDE worth the time of day, in my view.

Leave a Comment

Filed under Fortran, Mac, Python, Scientific Programming

Bruce Eckel on Ruby, Python, Java, etc

Bruce Eckel is one of the better authors of programming books around. He is famous for his ‘Thinking in …’ books on Java and C++, and also has an appreciation of — and enthusiasm for — dynamic scripting languages like Python, which is quite unusual for those developing in statically-typed languages.

Bruce has just written an interesting blog entry on Ruby on the Rails, with comparisons to Java and Python. Interesting stuff. Check it out.

Leave a Comment

Filed under C++, Python

Not livin’ “The Life”, and lovin’ it!

Dan Wood has written an interesting entry in his blog about how the logo for his company Karelia came to be. He uses this to dive into a history of Watson — probably the first Mac app to bring web services to the desktop — and his latest project, a WYSIWYG web site editor called ‘Sandvox’.

The Sandvox Story

The main motivation for the story was a rumor: someone spotted a link to a yet undisclosed piece of software on Apple’s web site called ‘iWeb’. Apparently it was posted by accident, and quickly pulled, but not before the damage was done. Dan is clearly worried that iWeb will be a direct competitor to Sandvox, and I’m afraid he is probably right.

As Dan explains, if iWeb is indeed a competitor to Sandvox, it is the second time he has been struck by Apple-flavored lightening: After establishing the market, Watson soon found itself in the ring with Sherlock 3, which bore an uncanny resemblance to Karelia’s application. Watson survived for a while longer, but eventually went the way of the dodo.

Karelia’s logo shows a small human-powered railway car; it was motivated, according to Dan, by a conversation he had with Steve Jobs just before Sherlock 3 was released. Jobs apparently told Dan that Karelia was a hand-powered rail car, and Apple was the locomotive that owned the track, and was bearing down fast. It’s a very amusing story.

Dan has since moved on, and is in the last stages of development on Sandvox. (Starting today you can download a public beta.) So the news that Apple may again be encroaching on his territory is undoubtedly a bit of shock.

Today I downloaded the Sandvox public beta, and played with it for a little while. It is a very nicely written piece of Cocoa software, and I have no doubt that it would do extremely well … if Apple were not to enter the race. If iWeb is similar in functionality — and worse still — bundled with iLife, Sandvox could be dead in the water before it even gets started.

To be honest, I don’t know how much it should really come as a shock. Sandvox reminds me a lot of Apple’s Pages and Keynote software, with the primary difference being that Sandvox is targeted at web site development. It shouldn’t come as too much of a surprise that Apple might consider developing a Pages-like app for creating web sites. Yes, Karelia may be about to be struck by lightening a second time, but that could have to do with the fact that they appear to be leaning against a flagpole in the middle of a golf course.

The Development Tightrope

If you are going to play with the big boys, there is always the risk of getting burned. It’s happened time and time again. The payoffs of writing mainstream apps are great, but the competition is high. Even if you have a unique idea, and have the market to yourself, its success may ultimately be its downfall, because it won’t go unnoticed. You had better make your money fast, because it won’t be long before you have company.

If you are really serious about making money writing Cocoa apps, I think you can learn a lot from guys like Wil Shipley. Delicious Library is mainstream enough to make a heap of cash for Mr Shipley, but perhaps not mainstream enough that it would be of interest to Apple. Apple are in the business of very high-volume software — stuff that everyone needs. Browsers, photo management software, digital duke boxes, etc, etc. Delicious Library is useful to many people, but not everyone. I don’t think it would make business sense for a company the size of Apple, but for a small company like Delicious Monster, it’s a cash cow.

The Life

Developers like Wil Shipley, Dan Wood, and Brent Simmons (of NetNewsWire fame) are highly respected members of the Cocoa development world. Cinderella stories of programmers that have developed killer apps in their spare time, quit the day job, and started living ‘The Life’, always make a great read for aspiring developers. In the beginning, I was no different, but my outlook has changed. I actually don’t want my software to be too successful.

See, the thing is, I like my day job. I don’t want to quit it, even if it meant I could develop Cocoa apps all day. I like working with Cocoa as a hobby, but I don’t think I would like it nearly as much if it dominated my every waking hour.

Not only that, but as your sales increase, the amount of time you have to spend on the business side of things increases. Eventually, you have to take on staff to help with administration and/or support, and that comes out of your income. With limited sales, you can do everything yourself, and the revenue goes straight into your personal bank account.

Niche Markets

The point is, there is a third way: writing niche software. It won’t make you rich, but you can make some nice pocket money without the stress of running a business. If you actually like your day job, this might be the way to go. Just make sure you don’t write anything that becomes too popular.

Trade Strategist is the Cocoa software that I develop and sell. It is financial modeling software for the stock market, and the potential market is actually very small. Nonetheless, I do make some reasonable pocket money out of it, ranging between $500 and $1500 a month, depending on the release schedule. I won’t be getting rich any time soon, but the $10000 or so I earn per year from Trade Strategist does come in handy. It’s just paid for my new bathroom, for example.

There is more than one way to skin a cat: You can take the high-risk, high-rewards route of Karelia, or you can just play in the minor leagues. You won’t ever be Donald Trump, but it can still be very rewarding, and you’ll even be able to sleep at night. Here’s to hoping Dan can get some sleep before tomorrow’s keynote.

2 Comments

Filed under Cocoa, Mac