shinyblog

Saturday, March 25, 2006

Flickr in Open Laszlo

Working on the Open Laszlo LZPIX multiple runtimes demo made me fall in love with data-binding in lzx. LZPIX is a flickr browser written in Open Laszlo, which can be compiled as a DHTML app or a Flash app. To share the data-binding joy, I wrote a little flickr browser (requires Flash Player 7 or later) and annotated the application code.


rubyonrails.org has a really cool screencast showing how they built a flickr viewer in five minutes with, of course, Ruby on Rails. That screencast inspired me to get a Rails environment set up on my mac, so I'm following suit with a similar Open Laszlo app in the hopes that I'll inspire more developers to give Open Laszlo a test run. I'm not trying to argue that Ruby on Rails is better or worse than Open Laszlo. Consider this article as "imitation as flattery" not "competitive analysis."


Open Laszlo is a great platform for mashups. (marketing-speak: Open Laszlo is a great platform for building rich internet applications using third-party REST web services.) OL has data binding and replication built in. The results of an xml query (a "dataset" in OL) can be mapped directly to elements in the OL scene graph (the "canvas" in OL). Attributes of elements in the OL scene graph can be mapped from attributes of the elements in the dataset. The data replication manager creates views to represent each result of a query to a REST api.

I've used a simple wrapper for the flickr api, written by Elliot Winard. For this article, I'll write from the perspective of an application developer using this existing api. libflickr defines a class, photo, which is a thumbnail view of an image in a result from a flickr query.

OL is tag-based and requires well-formed XML, so I start off my lzx document with a single root element, canvas, and include the flickr library:
<canvas title="Flickr on Open Laszlo">
<include href="incubator/libflickr.lzx" />


To access flickr, we need an api key. I define a global attribute with the value of my api key. Get your own from flickr
<attribute name="myapikey" value="xxxx_GET_YOUR_OWN_xxxx" type="string"/>

At first I just want to view some flickr data without worrying about how I got it, so I'll just use this little stub that kicks off a simple query when the canvas initializes. I call the method "getRecent" on the gFlickr node, which is global. I pass in my api key as an argument to the request.
<method event="oninit">
gFlickr.getRecent(canvas.myapikey);
</method>


Add a view for the search results, containing its own layout:

<view name="results"
x="10"
width="${parent.height-20}" >
<!-- The wrapping layout arranges the search results in a grid, and
starts a new row ("wraps" the contents) when the contents fill each row.
This gives grid-like behavior without scaling the subviews.
-->
<wrappinglayout axis="x" spacing="5" />

<!-- Data binding is so cool. This element says, "make an instance
of class coolphoto for the first ten nodes in the dataset "photods" with
"rsp" as grandparent and "photos" as parent.
-->
<photo datapath="photods:/rsp/photos/photo[1-24]" />
</view>

That's all it takes. The app now displays see a grid of photos recently added to flickr. (Here's the code so far)

Now let's enhance it. I want some more interesting searches, so I'll add a few buttons to control the search:
<view
x="10">
<!-- Within this view, put the subviews in a row. -->
<simplelayout axis="x" spacing="5" />

<!-- Label the search field -->
<text y="5">Search Flickr:</text>
<edittext name="tagQuery" text="lzpix">
<method event="onkeyup" args="kc">
if (kc == 13) gFlickr.searchWithTags(this.getValue(), canvas.myapikey);
</method>
</edittext>

<button onclick="gFlickr.searchWithTags(parent.tagQuery.getValue(), canvas.myapikey)">
go
</button>
<button onclick="gFlickr.getRecent(canvas.myapikey)">recent</button>
<button onclick="gFlickr.getInteresting(canvas.myapikey)">interesting</button>
</view>



I want to show a larger image when I click on a thumbnail, so I'll create a view for the larger image:
<view id="gPhotoDetails"
x="10" y="${parent.results.y + parent.results.height + 10}">
</view>


Nothing goes in that view yet, though, so I'll subclass the photo class, yielding the coolphoto class, and add an onclick handler. In the onclick handler, I'll set the source of the details view to the url to the medium sized image.

<handler name="onclick">
var s = this.getImageURL("_m");
gPhotoDetails.largeview.setSource( s );
</handler>


Now the app now has some search ui, and shows a larger image when you click on a thumbnail.(Here's the code so far)
Let's add some tasty effects. I'll add an animator to the coolphoto class, which fades in the photo from transparent to fully opaque.
<!-- This animator fades the image in by animating its opacity. -->
<animator name="appear_animator"
attribute="opacity"
to="1"
start="false"
duration="800"/>


We want that animator to start as soon as we've got the data for this photo, so we add an init handler:
<handler name="ondata" args="d">
<![CDATA[
// Start the fade-in animation
appear_animator.doStart();
]]></handler>


I want to show some information about the thumbnails when I mouse over them. I'll create a tiny view called gFloater to float in front of the photos, and animate the floater to just below the photo being mouse-over'd. To get the photos title and owner, I just run an XPath query on my data.
<view id="gFloater" width="160" bgcolor="#333333" 
options="ignorelayout"
x="1000"> <!-- start off offscreen -->
<simplelayout axis="y" spacing="1" />
<text name="titlelabel" fgcolor="white" width="150">title</text>
<text name="ownerlabel" fgcolor="white" width="150">owner</text>
</view>
<method event="onmouseover">
gFloater.animate("x", this.x + 10, 500);
gFloater.animate("y", this.y + parent.y + this.height - 10, 500);
gFloater.titlelabel.setText( "t: " + this.datapath.xpathQuery('@title'));
gFloater.ownerlabel.setText( "o: " + this.datapath.xpathQuery('@ownername'));
</method>


the complete code and the finished application.

Tuesday, March 21, 2006

podcasts: good as a media delivery mechanism, bad as a standalone media

Diggnation and This Week in Tech are moderately entertaining but not more so than the standard friday afternoon gather around somebody's computer and show off some cool stuff. The guys of Diggnation and TWiT don't seem to have any special information to tell me; I maybe get a nice "in the know" glow from hearing similiar tidbits of useless information emerge in a disconnected context. (Can it be a context if it's disconnected?) Patrick Norton and John Dvorak and Leo Laporte each have interesting personalities, but I think they're interesting like, if this guy is sitting in the break room, i'd be willing to sit down and hear about what he's up to, but I wouldn't sit in rapt attention while he goofs off with a few other moderately entertaining curmudgeons. Podcast qua Podcast: no.
But! Podcast as a way to deliver actual valuable news and entertainment and analysis -- that's just right. I use my ipod (thank you, Marshall, so much) as a time shifter. NPR and BBC and Jim Lehrer and Weekend America and Slate all deliver bits to my device, and I just keep a casual queue going, with some smart filters. Most days I have a few hours of NPR content available, and a few audiobooks I get through slowly... The content is what's important. A bunch of guys chatting about tech, or, worse, just one guy talking about tech: blech. But if you've got some actual journalists composing stories and pieces and reporting on the news, and you make that content available to me to put in my pocket and listen to anytime -- now you've got my attention. Which is the new new new economy.

"It's the FED, you idiot!"

George W. Bush cannot summon the words "the Fed" when asked in a press conference what he thinks about rising interest rates. Check this out:
Q: For the first time in years, interest rates are rising in the U.S., Europe and Japan at the same time. Is this a concern for you? And how much strain are higher interest rates placing on consumers and companies?
THE PRESIDENT: First of all, interest rates are set by an independent organization, which --
Q -- still, are you concerned about that?
THE PRESIDENT: Well, I'm not quite through with my answer yet.
Q I'm sorry.
THE PRESIDENT: I'm kind of stalling for time here. (Laughter.) Interest rates are set by the independent organization. I can only tell you that the economy of the United States looks very strong.
from whitehouse.gov's transcript of today's press conference
It's the FED, you idiot! The Federal Reserve Board. Remember, you just appointed Ben Bernanke to be the Chairman of the Fed six weeks ago? Arrrrg. It's hopeless. Can we impeach him for gross incompetence?

Sunday, March 19, 2006

making my slow mac fast

My 867 Mhz, 512 mb ram 15" titanium powerbook g4 is not slow. The key to getting this four year old to perform is to understand and take advantage of OS X's virtual memory structure. (Oversimplifications follow, but they get the job done; my mac feels fast.)
Virtual memory is on disk; physical memory is on chips. Chips are faster than disk. To run, an application must be in physical memory. When switching applications, the new application must be "swapped in" to physical memory. If that physical memory was in use by another application, that application must be "swapped out." A standard mac-ish application takes about 100 mb of memory. Switching between a paged-in application and a paged-out application involves writing ~100 mb to disk, and reading another 100 mb from disk. 200 mb of disk access takes a non-trivial amount of time... enough to explain that annoying pause when I hit cmd-tab or click on another icon in the dock.
Running fewer applications requires less application switching, so it feels faster. This brings me to my key insight for the day: For improved performance on low-memory systems, use web apps in the browser to replace traditional desktop apps. Firefox is faster at switching between windows and tabs (all resident in physical memory, presumably) than OS X is at swapping applications between physical and virtual memory. Did someone say "the network is the computer"?
This approach is great for the things that I need to do with a portable machine: coding (editing text files on the server, using Transmit and BBEedit), email (although Mail.app can be rather slow on big mailboxes; I should probably switch to Laszlo Mail soon), and web browsing. Photoshop, no way; I don't even have photoshop installed on this machine. Nor do I run tomcat or an Open Laszlo server on this machine. I offload those jobs to the right machines, which I have access to because my employer understands: a blazing dual-opteron linux server for tomcat and OLS, and a bomber dual-2 G5 tower at work.
Diagnosing where my memory is going is possible using top, and easy using Activity Monitor or iPulse. I learned that Adium uses an astonishing amount of memory, as do dashboard clients. This mac is going to be my portable machine for a long time yet, so, goodbye Adium, goodbye cute dashboard widgets... Hello, terminal, my old friend!