Singlet, part 0.2

I’ve finally had a little extra time to get back to working on Singlet.  There’s been a lot of progress since the first iteration.  To start with, Singlet had to be upgraded to work with the new Lens API introduced when Unity 5.0 landed in the Precise repos.  Luckily the Singlet API didn’t need to change, so any Singlet lenses written for Oneiric and Unity 4 will only need the latest Singlet to work in Precise[1].

The more exciting development, though, is that Singlet 0.2 introduces an API for Scopes.  This means you can write Lenses that support external scopes from other authors, as well as external Scopes for existing lenses.  They don’t both need to be based on Singlet either, you can write a Singlet scope for the Music Lens if you wanted to, and non-Singlet scopes can be written for your Singlet lens.  They don’t even have to be in Python.

In order to make the Scope API, I chose to convert my previous LoCo Teams Portal lens into a generic Community lens and separate LoCo Teams scope.  The Lens itself ends up being about as simple as can be:

 
from singlet.lens import Lens, IconViewCategory, ListViewCategory 

class CommunityLens(Lens): 

    class Meta:
        name = 'community'
        description = 'Ubuntu Community Lens'
        search_hint = 'Search the Ubuntu Community'
        icon = 'community.svg'
        category_order = ['teams', 'news', 'events', 'meetings']

    teams = IconViewCategory("Teams", 'ubuntu-logo')

    news = ListViewCategory("News", 'news-feed')

    events = ListViewCategory("Events", 'calendar')

    meetings = ListViewCategory("Meetings", 'applications-chat')


As you can see, it’s really nothing more that some meta-data and the categories.  All the real work happens in the scope:

class LocoTeamsScope(Scope):

    class Meta:
        name = 'locoteams'
        search_hint = 'Search LoCo Teams'
        search_on_blank = True
        lens = 'community'
        categories = ['teams', 'news', 'events', 'meetings']

    def __init__(self, *args, **kargs):
        super(LocoTeamsScope, self).__init__(*args, **kargs)
        self._ltp = locodir.LocoDirectory()
        self.lpusername = None

        if os.path.exists(os.path.expanduser('~/.bazaar/bazaar.conf')):
            try:
                import configparser
            except ImportError:
                import ConfigParser as configparser

            bzrconf = configparser.ConfigParser()
            bzrconf.read(os.path.expanduser('~/.bazaar/bazaar.conf'))

            try:
                self.lpusername = bzrconf.get('DEFAULT', 'launchpad_username')
            except configparser.NoOptionError:
                pass

    def search(self, search, model, cancellable): 


I left out the actual search code, because it’s rather long and most of it isn’t important when talking about Singlet itself.  Just like the Lens API, a Singlet Scope uses an inner Meta class for meta-data.  The most important fields here are the ‘lens’ and ‘categories’ variables.  The ‘lens’ tells Singlet the name of the lens your scope is for.  Singlet uses this to build DBus names and paths, and also to know where to install your scope.  The ‘categories’ list will let you define a result item’s category using a descriptive name, rather than an integer.


 model.append('http://loco.ubuntu.com/events/%s/%s/detail/' % (team['lp_name'], tevent['id']), team['mugshot_url'], self.lens.events, "text/html", tevent['name'], '%s\n%s' % (tevent['date_begin'], tevent['description']), '') 

It’s important that the order of the categories in the Scope’s Meta matches the order of categories defined in the Lens you are targeting, since in the end it’s still just the position number that’s being passed back to the Dash.

After all this, I still had a little bit of time left in the day.  And what good is supporting external scopes if you only have one anyway?  So I spent 30 minutes creating another scope, one that will read from the Ubuntu Planet news feed:

The next step is to add some proper packaging to get these into the Ubuntu Software Center, but you impatient users can get them either from their respective bzr branches, or try the preliminary packages from the One Hundred Scopes PPA.

[1] Note that while lenses written for Singlet 0.1 will work in Singlet 0.2 on Precise, the reverse is not necessarily true.  Singlet 0.2, as well as lenses and scopes written for it, will not work on Oneiric.

This entry was posted in LoCo, OpenSource, Programming, Work and tagged , , , , , , , , . Bookmark the permalink.

29 Responses to Singlet, part 0.2

  1. Randall says:

    Great work Michael. Thank you!

  2. Pingback: Looking for Quickly template help | Michael Hall's Blog

  3. Pingback: Learn Ubuntu Development in just 3 days! | Michael Hall's Blog

  4. Tory says:

    I am trying to make a websearch lens and google scope with the latest singlet, I’m using Ubuntu 11.10, is Unity 5 required for this version? Or does the latest version still support Unity 4 as well?

    I’d hate to have to use singlet 1 and have to make it as a SingleScopeLens until Precise is finished…

    However if it does support Unity 4, the error I’m getting when I use ‘setsid unity’ is this:
    WARN 2012-02-06 02:06:14 unity.glib.dbusproxy GLibDBusProxy.cpp:269 Calling method failed: GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: No such interface `com.canonical.Unity.Lens’ on object at path /unity/singlet/lens/web_search

    And if I use ‘sudo python web_search’ I get this:
    (process:2504): libunity-DEBUG: unity-scope-factory.vala:57: Searching for Scopes in /usr/share/unity/lenses/web_search
    (process:2504): libunity-DEBUG: unity-scope-factory.vala:102: Successfully loaded /usr/share/unity/lenses/web_search/google.scope: unity.singlet.lens.web_search.google | /unity/singlet/lens/web_search/google
    (process:2504): libunity-DEBUG: unity-scope-proxy-remote.vala:115: Scope vanished: unity.singlet.lens.web_search.google
    (process:2504): libunity-DEBUG: unity-scope-proxy-remote.vala:105: Scope appeared: unity.singlet.lens.web_search.google

    (process:2504): libunity-WARNING **: unity-scope-proxy-remote.vala:97: Unable to connect to Scope (/unity/singlet/lens/web_search/google @ unity.singlet.lens.web_search.google): GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: No such interface `com.canonical.Unity.Scope’ on object at path /unity/singlet/lens/web_search/google

    (process:2504): libunity-WARNING **: unity-scope-proxy-remote.vala:97: Unable to connect to Scope (/unity/singlet/lens/web_search/google @ unity.singlet.lens.web_search.google): GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: No such interface `com.canonical.Unity.Scope’ on object at path /unity/singlet/lens/web_search/google
    (process:2504): libunity-DEBUG: unity-scope-proxy-remote.vala:115: Scope vanished: unity.singlet.lens.web_search.google
    The DBus info is all correct in the .service, .lens, and .scope files

  5. Tory says:

    OK, thanks, I guess I’ll write a SingleScopeLens for now then…

  6. Tory says:

    OK, I got my scope working using singlet 0.1, well sort of, but the search results are not showing up, also, I installed your test lens from singlet 0.1, and it has the same problem, it acts like it works, but no results show up, all lenses I installed via softwtare center are working fine. When you type in the search the circle is spinning as if it’s searching, then it stops spinning, but nothing shows.

    I should also note that when I run setsid unity in a terminal, and then try to use the singlets, there are no error messages, the singlets are displaying the same info as all the working lenses.
    I’m running Ubuntu 11.10 with Unity 4.28.0

    On another note, I realized that at least with singlet 0.1, you can not use any special characters in the name, I was using web-search and the ‘-’ was causing it to fail, as would ‘_’, I had to use websearch as the name, don’t know why it does this, but it does.

    • Michael Hall says:

      Can you run the lens script from the command line, and see if you get any errors? I’ve seen this happen when the search code itself throws an error.

  7. Tory says:

    It doesn’t throw any errors, it just says it’s searching for scopes, also, I used my searching data in a test python program and it gets all the data correctly.

  8. Tory says:

    Also, like I wrote earlier the test lens included in singlet does the same thing.

  9. Tory says:

    I’ll post it here:

    import sys, urllib, json
    from singlet.lens import SingleScopeLens, ListViewCategory
    from singlet.utils import run_lens

    class WebSearchLens(SingleScopeLens):
    class Meta:
    name = “websearch”
    description = “Search the web”
    search_hint = “Search Google and Wikipedia”
    icon = “websearch.svg”
    search_on_blank = True

    webcrawler = ListViewCategory(‘Web Crawler’, ‘webcrawler.svg’)
    wiki = ListViewCategory(‘Wiki’, ‘file’)

    def search(self, search, model):

    if len(search) == 0:
    model.append(‘http://google.com/’,
    “/usr/share/unity/lenses/websearch/webcrawler.svg”,
    self.webcrawler,
    “text/html”,
    ‘Google Home Page’)
    else:
    ##format search string into url#
    query = urllib.urlencode({‘q’: search})
    url = “http://ajax.googleapis.com/ajax/services/search/web?v=1.0&rsz=large&%s” % query

    ##open and read the url, convert to Python object#
    search_results = urllib.urlopen(url).read()
    data = json.loads(search_results)['responseData']

    ##retrieve desired info from data#
    for item in data['results']:
    title = item['titleNoFormatting']
    url = item['url']
    description = item['content']
    model.append(url,
    “/usr/share/unity/lenses/websearch/webcrawler.svg”,
    self.webcrawler,
    “text/html”,
    title, description)

    if __name__ == “__main__”:
    run_lens(WebSearchLens, sys.argv)

  10. Tory says:

    And don’t worry, my indentation was all correct, the comment just removed it :P

  11. Tory says:

    In case you wanted to see the code with all the correct indentation and everything I just posted it here:
    http://www.python-forum.org/pythonforum/viewtopic.php?f=4&t=32433

  12. Matt says:

    The unity-community-lens requires unity-singlet to install, but the package name I see is python-unity-singlet. Are these equivalent or am I missing a package?

    • Michael Hall says:

      Yes, I had to rename the binary to python-unity-singlet, the unity-community-lens package needs to be updated.

  13. ben says:

    Hi,

    I’ve been playing with the dictionnary lens and when I add “search_on_blank = True” in its meta. The lens errors out with the following: “unknown signal name: active”. I’ve trace that to single/lens/base.py starting at line 159 where it registers what should happen when the “active” signal is reached.

    Unfortunately, such a signal doesn’t seems to exist and thus cannot be registered.

    I can’t seem to find documentation on which signals are supported. Documentation is pretty rare and discombobulated as you pointed out in an earlier post.

    Any pointers?

    Thanks you for all your work making this a simpler process.

  14. ben says:

    Hey sorry to bug you again, I see that you’ve commited a code change thank you. This however removes the capability to execute a blank search upon the lens being activated does it not?

    Is there any other way that I can populate the lens with some defaults?

    • Michael Hall says:

      You still have that ability, a blank search will still fire the search_changed signal which Singlet is already listening for. This code change just stops Singlet from trying to connect to the old ‘activate’ signal that isn’t there anymore.

    • Michael Hall says:

      To clarify, search_on_blank will still work as expected, it just doesn’t need to bind to ‘activate’ to do that anymore.

      • ben says:

        Negatory,

        no blank search gets fired even with “search_on_blank = True”

        I’ve verified that by grabbing the latest code & having the lens’ search function load stuff in the model no matter what.

        This will only happen after a key press, not when the lens comes into focus.

        I’m happy to make a video if my description isn’t clear.

        • ben says:

          My bad I’m a moron, I had a statement clear my model. Took me forever to put my finger on it.

          Everything works like a charm!

  15. Pingback: Lens for Ubuntu Unity (12.04) to browse tasks on Remember The Milk | thealarmclocksixam

  16. Gary Bishop says:

    Mark, have you seen problems with Firefox after launching a link from the dash? I filed a bug here.

    I see it on two different ubuntu precise machines, running firefox in safe-mode or not. If I launch a page from the dash, and then close that tab, firefox will be unable to draw dropdown menus (like the search engine chooser or the completion drop downs) until it is restarted.

    Launching a page using xdg-open does not cause the problem.

    Just wondering if you had seen it.

    Thanks for singlet, it really makes writing a lens easy.

  17. Pingback: Singlet 0.3 adds Unity Previews | Michael Hall

Comments are closed.