Reinventing iTunes

brian_d_foy on 2002-09-25T02:53:52

As part of my MacOS X conference tutorial (Programming Perl on MacOS X), I want to show off my Mac::iTunes module which does various iTunes sorts of things from Perl.

Mac::iTunes can communicate with iTunes through AppleScript, which can be really handy, and now that it works decently, I can finally control iTunes remotely (Apache module in the works...). Within my home network I can use my Tk version of iTunes (which I will release after I show it off at MacOS X Conference---register now to see if before it makes it to CPAN!).

The problem with programming user interfaces is that you have to program user interfaces. Since I add to Mac::iTunes as I find features I want in TkiTunes, the actions of my button widgets get really complex.

The basic button creation is a simple Tk construct that sets a few attributes and packs the widget.

$frame->Button(
	-text       => $title,
	-command    => $sub,
	-background => "#$color",
	)->pack(
		-anchor  => 'w',
		-side    => 'top',
		-fill    => 'both',
		);


The problem is making all the right things happen in the anonymous subroutine $sub. The more I play with TkiTunes, the more things I realize have to happen with each user interaction. The $sub gets pretty ugly.

my $sub = sub {
    $Controller->play;
    $State->reset_time;
    $State->reset_scale;
    ...
    };


Since I have about six buttons in the window, depending on my mood that hour, I have about six really ugly anonymous subroutines to maintain, or at least I did.

I created an %Actions hash that had as value a bunch of single actions.

%Actions = (
	play         => sub { $C->play        },
	pause        => sub { $C->pause       },
	back_track   => sub { $C->back_track  },
	'next'       => sub { $C->next        },
	previous     => sub { $C->previous    },
	stop         => sub { $C->stop        },
	reset_time   => sub { ... },
        reset_scale  => sub { ... },
        );


Once I have a bunch of little actions I just string them together in a pipeline.

my @pipeline = qw(play reset_time reset_scale);


To make it all work, I need to translate the pipeline in an anonymous subroutine that the Tk widget can use.

my $sub = make_action( \@pipeline );

sub make_action { my $pipeline = shift;

my $sub = sub { foreach my $action ( @$pipeline ) { $Actions{$action}->(); } };

return $sub; }


If you see the actual code, you can see that all of the anonymous subroutines are really closures, which makes this an example of the dispatch table technique that Adam Turoff talked about in his August ;login: column.

All sorts of things immediately became really simple once I had a way to group a series of actions together. Now that its easy to say what a particular widget should do, its also really easy to change what it should not do when I change things. I specify the actions as strings, not as code, and whenever I can move code out of the way I am happy. :)