Unity certainly has been getting a lot of attention in the past year. Love it or hate it, everybody seems to have something to say, whether it’s about the Launcher, application indicators, or the window control buttons being on the left. But with all the talk about Unity, good and bad, one very unique aspect that hasn’t been getting nearly enough attention are Lenses.
Lenses are a central part of the Unity desktop, and anybody who’s used it will be familiar with the default Application and File lenses, maybe even the Music lens. But there’s so much more to this technology than you might think. In fact, David Callé has recently been spearheading an effort to build out a large number of small but incredibly useful lenses. I first took notice of David’s work when he released his Book lens which, being a huge ebook fan, really brought home the usefulness of Unity Lenses for me.
More recently, David has been writing Scopes for the One Hundred Scopes project. A Scope is the program that feeds results into a Lens. While the Lens defines the categories and filters, it’s the Scopes that do the heavy lifting of finding and organizing the data that will ultimately be displayed on your Dash. If you follow David on Google+, chances are you’ve seen him posting screenshots of one scope after another as he writes them, often multiple of them per day.
Seeing how quickly he was able to write these, I decided to dive in and try it out myself. You can write Lenses in a variety of languages, including Python, my language of choice. I decided to start of with something relatively easy, and something that I’ve personally been missing for a while. I used to use a Gnome2 applet called Deskbar, which let you type in a short search word or phrase, and it presented you with search results and various other options. Included among those was the option to lookup the word in the gnome-dictionary, and I used this option on a startlingly frequent basis. Unfortunately Deskbar fell out of favor and development even before the switch to Gnome 3, and I’d been lacking a quick way to lookup words ever since. So I decided that the Lens I wanted was one that would replace this missing functionality, a Dictionary lens.
My first task was to find out how to write a lens. I checked the Ubuntu Wiki and the Unity portal, both of which offered a lot of technical information about writing lenses, but unfortunately not very much that I found helpful for someone just starting off. In fact, I had to get a rather large amount of one-on-one help from David Callé before I could even get the most basic functionality working.
Lenses and Scopes all communicate with each other and with the Unity Dash via DBus, and for anybody not familiar with DBus this makes for a very steep learning curve. On top of that, writing it in Python means you’ll be relying on GObject Introspection (GI), which is a very nice way of making APIs written in one language automatically available to another, but it also means you’re going to be using the lowest common denominator when it comes to language features. I found that learning to work with these two technologies accounted for 90% or more of the time it took me to write my Dictionary lens. Before I write another Lens or Scope, I plan on wrapping much of the DBus and GI boilerplate and wiring behind a simple, reusable set of Python classes. I hope this will help developers, both newbies and seasoned Unity hackers, in writing simple Scopes by allowing them to focus 90% of their time on writing the code that does the actual searching.
But by the end of the day I had a working Dictionary lens. It uses your local spellcheck dictionary, via python-enchant, to both confirm whether or not the word you typed in is spelled correctly, as well as offer a list of suggested alternatives. I also dug through the gnome-dictionary code and found that it was pulling its definitions from the dict.org online database using an open protocol. Using the python-dictclient I was able to query the same database, and include the start of a word’s definition in the Lens itself.
This lens turned out to be more complex than I had originally envisioned, not just for the features listed above, but also because I needed to override what happened when you clicked on an item. When you build up your results, you have to give each item a unique URI, which is often in the form or an http:// or file:// URL that Gnome knows how to handle. But for results that were just words, I needed to do the handling myself, which meant more DBus wiring to have the mouse click event call a local function. From there I was able to copy the word to the Gnome clipboard or launch gnome-dictionary for viewing the full definition.
After seeing that first Lens running in my Dash, I felt an urge to try another. Since I already had all of the DBus and GI code, I wouldn’t have to mess with all of that and I could focus just on the functionality I wanted. Jorge Castro has been trying to get me to write a lens for the LoCo Teams Portal (formerly LoCo Directory) since the concept was first introduced to Unity under the name “Places”. Since LTP already offers a REST/JSON api, this turned out to be remarkably simple to do. Between the existing DBus/GI code copied from my previous lens, and an existing python client library for the LTP, I was able to get a working lens in only a couple of hours.
For item icons, you can use either a local icon, or a URL to a remote image. For this lens, I used the team’s mugshot URL that LTP pulls from the team’s Launchpad information. When you search, it’ll show matching Teams, as well as Events and Meetings for those teams, and any Event or Meeting that also matches your search criteria. I’ve also added the ability for this lens to lookup your Launchpad username (by checking for it in your bazaar.conf) and defaulting it to display the LoCo Teams you are a member of, as well as their upcoming Events and Meetings.
Both the Dictionary Lens and the LTP Lens lump the Lens and Scope code in the same Python class, but it doesn’t have to be this way. You can write a Scope for someone else’s Lens, and vice versa. In fact, plan on separating the LTP lens into a general purpose Community Lens, with an LTP Scope feeding it results about LoCo Teams. From there, others can write scopes pulling in other community information to be made available on the Dash. This will also be my prototype for a Python-friendly wrapper around all of the DBus and GI work that scope writers probably don’t need to know about anyway.