All this stuff is filed under "code"

Implementing scene fades in WebOS

I had a heck of a time implementing a simple scene fade (or “scroll fades” as they’re also called) in TapNote, mainly thanks to the lack of good information surrounding them. Most folks in the Palm forums recommend looking at the Style Matters example project, but Style Matters was apparently coded by someone who also believed that Coding Style Doesn’t Matter, so screw that.

Sadly, scene fades are only provided for free with some elements (the command menu, for instance), so most of the time you have to implement them by hand.

To get your scene fades up and running, you’ll need to stick a little extra HTML in your scene, add a CSS snippet to your stylesheet, and copy the image resources into your app.

HTML

The HTML is very straight-forward. Just stick this at the very bottom of your scene (don’t nest it in anything):

<div class="scene-fade bottom" x-mojo-scroll-fade="bottom"></div>
<div class="scene-fade top" x-mojo-scroll-fade="top"></div>

Obviously, if you only wanted a top or bottom fade, you’d only include one or the other.

CSS

This CSS snippet is a vastly condensed and improved version of the CSS used by the Style Matters program. Note that you’ll probably need to set height: 100% on your HTML and body elements because otherwise the absolute positioning of the scene fades might not work. If you do so and you’re still not having your bottom scene fade positioned properly, try additionally setting height: 100% on the wrapper element for your scene.

html, body {
    height: 100%;
}

/* === Scene Fades === */

.scene-fade {
    height: 54px;
    width: 100%;
    position: fixed;
    left: 0px;
    z-index: 100000;
    -webkit-palm-mouse-target: ignore;
}

.scene-fade.top {
    top: 0px;
    background: url(../images/fade-top.png) repeat-x center bottom;
}

.scene-fade.bottom {
    bottom: 0px;
    background: url(../images/fade-bottom.png) repeat-x center bottom;
}

Images

The images you need are squirreled away in the WebOS SDK. For instance, the ones I ended up using were located in this folder:

PalmSDK/0.1/share/samplecode/stylematters/images/

And were called my-scene-fade-bottom.png and my-scene-fade-top.png.

If locating those images is too much effort, though, you can just download the example code.

Finally, you will of course need to drop the images into your root images folder, and adjust the path to them in the CSS if it’s located more than a single level away from the project root. Enjoy!

Data model design in WebOS

When I was first starting to program TapNote I did a lot of reading through Palm’s documentation and other WebOS developer help sites, but I never was able to find any good information on how to set up a data model.

Turns out that there aren’t any official recommendations. Palm mentions the app/models folder mainly just to get you thinking about using an MVC design for your app, I suspect.

While I designed the architecture of my app, however, I thought a fair amount about how to break down the logic, and I ended up coming up with some guidelines that may help other developers with their app design. Of course, this is probably pretty obvious stuff for people who design with MVC regularly, but I would have found it useful when I was starting out with WebOS programming (particularly because it was complicated by WebOS’s persistently asynchronous nature).

Knowledge and responsibilities

The controller (or assistant, in WebOS parlance) knows these things:

  • The interface for the model
  • The expected Javascript object schema for the data
  • Details about the view elements (views being the HTML files for the scene)

The controller’s responsibilities are:

  • Querying the model for new data when needed
  • Updating view items with data once the model sends it over

The model (which should be either a Prototype class or a unique object stored in your app/models directory) knows these things:

  • Exactly what storage system is being used for the data
  • How to interact with the storage system (SQL queries or whatever else)
  • The Javascript object schema for the data

The model’s responsibilities are:

  • Gathering data when it is requested
  • Transforming the data from its raw format into the proper Javascript object
  • Returning the data using the provided callback

A decent metaphor is that of two coworkers in neighboring cubicles. The controller asks for something from the model, and tells it where to send the results. The model does what it needs to do to gather those results, then sends them where the controller requested. Neither can see into the other’s cubicle to know what exactly what their coworker is doing, but neither one needs to. Either model or controller can change what they’re doing with the data (how it’s stored or how it’s displayed), but this won’t affect their relationship to one another.

If you ever have something in the controller that depends on knowing what type of storage system is used for the data, you are doing it wrong. Similarly, if you ever have anything in the model that references or needs to know about specifically what view items are being populated with data, you need to rethink your architecture.

Basic layout

Unlike assistants which are automatically loaded by Mojo when their scene is pushed, you will need to add your model class to your app’s sources.json file by hand:

[
    {"source": "app/models/myDataModel.js"},
    {
        "scenes": "Myscene",
        "source": "app/assistants/myscene-assistant.js"
    },
]

What this does is expose the contents of myDataModel.js globally to your app (so be careful with your globally scoped variable names!).

Typical round trips

Based on TapNote’s design (in which I tried to follow the above guidelines), this is a typical function layout for the controller:

  • Request function: this is typically triggered by an event in the view, and all it does is request something from the model (passing along any relevant info from the event, along with the callback function reference)
  • Callback function: this accepts data from the model, and does whatever needs to be done with it in the view.

A typical controller would thus look like this:

var MysceneAssistant = Class.create({
    /* Standard function for Prototype classes */
    initialize: function() {
        this.model = new MyDataModel();
        // Always save static bound references to member functions
        this.bound = {
            populateData = this.populateData.bind(this)
        };
    },

    /* ... Setting up events etc. happens here ... */

    /* This function is triggered by an event */
    refreshData: function(event) {
        // Any necessary logic prior to requesting data
        this.model.refreshData(this.bound.populateData);
    },

    populateData: function(dataObject) {
        // Handle populating the view with the data object here
    }
});

My model ended up being very similar, since it too had an asynchronous call (although if you were accessing data in a depot or cookie you might not need two functions):

  • Request function: receives requests from the controller, builds the necessary SQL (or whatever) and executes it, binding the callback into the success function of the datastore
  • Return function: processes the successful results from the query, and sends the results of processing to the callback

Using the previous example code for the controller, here’s how our model class would look:

var MyDataModel = Class.create({
    /* ... Set up the model here ... */

    refreshData: function(callback) {
        // Set up the SQL, etc. here
        // Query the database here binding in our unique callback
        this.db.query(query, {
            onSuccess: this.returnData.bind(this, callback)
        });
    },

    /* Because of the on-the-fly bind, the callback is the first arg */
    returnData: function(callback, results) {
        // Transform results into Javascript object; simple example:
        var resultObject = results[0];
        // Send the object to the controller
        callback(resultObject);
    }
});

Unlike the controller, in the model we have to do the binding on the fly because otherwise we can’t pass the callback through. If you can guarantee that there will only ever be one controller requesting data at any given time, you could instead use a this.bound object like the controller and save the callback to a class variable (for instance, this.callback).

Brief side note: you may notice the unexplained this.db.query() call above. When I was writing TapNote, one of the first things I did was write a generic Database class to abstract away from the heinous syntax of the HTML 5 database connection. This is a generically useful class that I’ve released for free.

Implementing in your own app

The above examples certainly are not the only way to do things (I actually deviated from that design a bit for some data responses when TapNote became a multi-stage app partway through development), and you will likely find yourself changing things up based on the needs of your own app. However, if you start with this design as a basis and keep in mind the roles and responsibilities of model and controller, then you will hopefully create an app whose code is easy to debug and expand on down the road.

Encode named HTML entities with Python

If you’re using Python to parse text that’s going to end up on the web, odds are you need to worry about character entities for Unicode characters.

Obtaining numeric HTML/XML entities is easy enough; I found several different ways to do so in a quick Google search, this being the easiest (text is just an example variable):

text = text.encode('ascii', 'xmlcharrefreplace')

However, it is significantly more difficult to find a way to encode text as named character entities, which if you’re ever going to need to look at the markup later is vastly preferable. After a lot of digging, I discovered some basic logic for named HTML entities in the Python Cookbook. After improving on it to make sure that all entities get replaced (even high level ones without named equivalents) I’ve got something that others may find useful in turn. Simply place this code into a file called named_entities.py and stick it somewhere that your script can find it (or just stick the code at the top of your file, if you only need it in one place). Usage info is in the comment at the top of the code.

Please note that this code is for Python 2.x. Python 3 moved codepoint2name into html.entities.codepoint2name, so you’d need to modify the import.

'''
Registers a special handler for named HTML entities

Usage:
import named_entities
text = u'Some string with Unicode characters'
text = text.encode('ascii', 'named_entities')
'''

import codecs
from htmlentitydefs import codepoint2name

def named_entities(text):
    if isinstance(text, (UnicodeEncodeError, UnicodeTranslateError)):
        s = []
        for c in text.object[text.start:text.end]:
            if ord(c) in codepoint2name:
                s.append(u'&%s;' % codepoint2name[ord(c)])
            else:
                s.append(u'&#%s;' % ord(c))
        return ''.join(s), text.end
    else:
        raise TypeError("Can't handle %s" % text.__name__)
codecs.register_error('named_entities', named_entities)

One last thing: whether you use numeric or named entities, you’ll probably want to encode ampersands afterward. Here’s the regex that I’ve been using to do so, and it’s safe to run on the output of either the named or numeric entity creation code (remember to import re before trying this at home):

text = re.sub('&(?!([a-zA-Z0-9]+|#[0-9]+|#x[0-9a-fA-F]+);)', '&amp;', text)

Enjoy!

Tips, tricks, and gotchas for writing bundles with PyObjC

Part of the reason that I haven’t been posting very actively to Beckism.com or Tagamac is that I’ve recently been getting exceedingly involved in a number of personal projects involving Python. Python itself is super fantastic and learning it has been a blast, but one project in particular has been causing me undue amounts of head-to-desk contact thanks to its reliance on the oh-so-cool but insufficiently documented PyObjC, and I’d like to share some of the things that I’ve discovered for other would-be programmers who want to extend their favorite Mac OS X applications with PyObjC powered plugins.

For those of you who prefer source to exposition, the project that I’ve been working on is TEA for Espresso and you can find the full source code over at the TEA for Espresso project on GitHub. As you may have surmised, it’s a plugin for the upcoming Espresso text editor created because I wanted to play with Espresso but am addicted to Textmate-style HTML actions.

My personal preference for Python is to use four spaces rather than a tab for indentation, so keep that in mind if you’re trying to execute the code samples below and your document is using tabs.

Using py2app to create semi-standalone code

Py2app allows you to work completely in Python without ever needing to boot up Xcode and touch Objective-C. There’s plenty of information around the web about how to setup a basic py2app setup.py file, but one of the things that I had to discover via trial and error was how to make my code semi-standalone.

Semi-standalone is an option you can enable with py2app that makes your code reliant on the version of Python that is installed with the OS. By default, py2app tries to bundle any dependencies for your project into your bundle, but if you’re only distributing the bundle/plugin/app/whatever to people using the same major OS version this makes for about 15 MB minimum of unnecessary junk getting tossed into your bundle.

Turning on semi-standalone is as simple as adding a key to your py2app options dictionary, but what you might not know is that you also need to enable site-packages, as well (which apparently encourages py2app to create the links to Python necessary for getting the bundle up and running, although it’s only supposed to tell it to include the system and user site-packages in the system path). So to get a semi-standalone bundle, your code needs to look something like this:

setup(
    name='My Plugin Bundle',
    plugin = ['MyPluginBundle.py'],
    options=dict(py2app=dict(
        extension='.bundle',
        semi_standalone = True,
        site_packages = True
    )),
)

One of the most frustrating aspects of working with py2app for me was the lack of any decent documentation on the main py2app site. Fortunately, there’s a much more comprehensive page of documentation available in the MacPython py2app SVN repository, which is where I discovered the site-packages option.

Automatically including project data files

Odds are since you’re using Python you’ll need to include some arbitrary data files with your application, be they NIB files, extra Python scripts and classes, or whatever else. However, py2app’s method of declaring what files you want included with your project is to list them explicitly in a data_files array. This is a major pain, since every time you add or remove a file you have to remember to edit setup.py. No thanks.

Fortunately, Python comes with some powerful file system modules that allow you to traipse around your file system, making note of files and folders as you go. The following code snippet, if included in your setup.py script, will automatically parse through a directory of files and add them to your data_files array without you needing to lift a finger (assuming that you configure the first couple variables to fit your project, that is). Files that start with a period (hidden files) will be ignored. If you’re using SVN, you may also need to add some logic to exclude folders that start with a period (TEA for Espresso is using Git, so this hasn’t been an issue for me).

The downside to using this code, of course, is that you’ll need to nest all your files in the directory structure that you want in your final bundle. So, for example, in order to include something in the Resources folder for TEA for Espresso, I have to nest it in src/Contents/Resources/ whereas I otherwise could have placed it in the root project directory. If you’re doing a simple project with very few files, it might be more worth your while to define data_files differently.

import os

# Sets what directory to crawl for files to include
# Relative to location of setup.py; leave off trailing slash
includes_dir = 'src'

# Set the root directory for included files
# Relative to the bundle's Resources folder, so '../../' targets bundle root
includes_target = '../../'

# Initialize an empty list so we can use list.append()
includes = []

# Walk the includes directory and include all the files
for root, dirs, filenames in os.walk(includes_dir):
    if root is includes_dir:
        final = includes_target
    else:
        final = includes_target + root[len(includes_dir)+1:] + '/'
    files = []
    for file in filenames:
        if (file[0] != '.'):
            files.append(os.path.join(root, file))
    includes.append((final, files))

setup(
    name='My Plugin Bundle',
    plugin = ['MyPluginBundle.py'],
    data_files = includes,
    options=dict(py2app=dict(
        extension='.bundle'
    )),
)

Avoid release statements in Python

I’m sure this is extremely obvious for anyone with significant PyObjC experience, but TEA for Espresso lay fallow for over a month after I started it because I couldn’t get the example plugin ported from Objective-C to Python. It turns out that almost everything in Objective-C can be easily ported to Python using the simple conversion rules (colons to underscores, etc.; see the PyObjC intro for more info), except for any calls to release objects. For instance, here’s the code that was breaking my project:

Objective-C
[selectedRanges release];

Ported into Python (don’t do this!)
selectedRanges.release()

PyObjC auto-releases absolutely everything, so don’t port lines like the one above. They will break your project. This is actually described in the PyObjC introduction if you’d like a more technical explanation; apparently I just missed the significance of it when I read the intro the first time.

Handling **NSError arguments

If you’re implementing a bundle that will be loaded into another application, odds are you’ll need to implement an Objective-C protocol in Python, and Objective-C functions occasionally have an **NSError parameter. If you’re not going to be returning any error information, you can safely ignore the **NSError in your Python code. The bridge automatically handles it. If you might return an error, then you may find this thread on the PyObjC-dev mailing list useful.

For example, for TEA for Espresso I needed to implement this Objective-C method in Python:

- (BOOL)performActionWithContext:(id)context error:(NSError **)outError;

Since I’m never returning any error information, in Python this translates to:

def performActionWithContext_error_(self, context):

Although woefully out of date in some respects, one of the most useful and understandable sources of information about using **NSError with PyObjC I found was Apple’s PyObjC for Developing Cocoa Apps page.

Implementing Objective-C interfaces without a protocol

An interesting problem that can arise if your bundle is implementing an Objective-C interface (rather than conforming to a protocol) is that when your code is loaded you may find that Objective-C can’t find your class methods. To solve this, usually all you need to do is explicitly define the type encoding of your class method. For instance, TEA for Espresso’s main bundle class has the following method:

class TEAforEspresso(NSObject):
    @objc.signature('B@:@')
    def canPerformActionWithContext_(self, context):

The @objc.signature() decorator tells PyObjC that this particular method returns a bool (”B”), is an object method (”@” means object, and “:” means method selector), and accepts one argument which should be a generic object (”@” again meaning object). For a full list of available encoding characters, see the Objective-C Runtime Programming Guide.

You can find more information about this bit of weirdness over at Jim Matthews Blog or, to a very limited extent, in the PyObjC class wrapping documentation.

Accept text from either LaunchBar or Quicksilver in Applescript

Here’s the thing: I absolutely adore LaunchBar. I use it constantly throughout the day, and if I’m using a computer without LaunchBar I practically can’t function because I keep habitually hitting the LaunchBar shortcut and opening Spotlight on accident. So when I write Applescripts that I want to accept text and do something with it, I use LaunchBar-specific code.

However, there’s a large number of people out there who prefer Quicksilver (link to Google Code project; also see Blacktree’s site). I don’t know if it’s for the sexier (if more complicated) interface or just because they’ve got Quicksilver embedded in their fingers as badly as I’ve got LaunchBar in mine, but they’re unlikely to switch anytime soon.

And then there’s the folks out there who haven’t discovered the joys of a launcher program and just run Applescripts manually or with something like FastScripts.

All of which adds up to a bit of a conundrum if you want to share your favorite Applescripts with the world. Fortunately, with a bit of Googling and some good old trial and error, I’ve written a simple Applescript template that allows all users, no matter how they like to launch scripts, to use yours. Without further ado, here’s the code:

-- You'll want to rename this function something appropriate
on action_function(someText)
    -- Check to see if we've got text, ask for it if not
    if someText is equal to "" then
        set question to display dialog ("Enter text:") default answer ""
        set someText to text returned of question
    end if
    -- Do whatever your script does here
end action_function

-- Quicksilver tie-in code
using terms from application "Quicksilver"
    on process text qsText
        my action_function(qsText)
    end process text
end using terms from

-- LaunchBar tie-in function
on handle_string(lbText)
    my action_function(lbText)
    open location "x-launchbar:hide"
end handle_string

-- Call the function in case the script was run directly
-- (Don't worry; this line won't execute if called from LB or QS)
my action_function("")

If you’re familiar with Applescript, the code should be pretty self-explanatory. You wrap all of the scripts actions into a function (called action_function in the example), and then call that function using the specific access routines for Quicksilver or LaunchBar (with a normal call to the function included just in case the user directly accesses the script). The code within action_function is merely a fall-back in case it receives a blank string (you’ll likely only need the fallback if they are calling the script directly). I leave any more specific error checking to you.

This template specifically accepts text; if your script needs to process a list of files, for example, you’ll need to change the code for the various programs appropriately (how specifically to do that I leave up to you, but the general template should still serve you well). One caveat is that if you’re compiling the code, you’ll need Quicksilver installed (even if you only use LaunchBar). However, as far as I can tell, if you’re just running the Applescript you don’t need to have either program installed (or you can have just one or the other). The main downside of needing both to compile is that if you rely on your users to adjust config variables in your script, then they will need to install Quicksilver (unless you include instructions to remove Quicksilver’s code if they aren’t using it).

Let me know if you run into any trouble using this code! I haven’t run into any errors with it in my testing, but that certainly doesn’t mean that they don’t exist.

Track me like a stalker:
  • Tagamac
  • Twitter
  • beckbits
Clicky Web Analytics