Tag Archives: python

Making KDE applications Python 3 friendly

When I’m not on forum duty or handling openSUSE-related contributions, I try to improve my code contributions to KDE, even though I’m by no means an experienced programmer (I program all day long in Python, but I’m still a biologist after all). For the upcoming 4.10 release I’ve been polishing up Python 3 support.

As you may know, Python 3 isn’t the standard in many distributions (Arch Linux excluded), but despite the slow start, it is slowly gaining steam. PyKDE4 was built keeping in mind Python 3 support, so theoretically we supported it right from the start.

When I wanted to package PyKDE4 for Python 3 in openSUSE, I stumbled upon some quirks: the CMake build system was not finding Python 3 properly. After some investigation, I noticed that the Python macros responsible for finding the Python library had some assumptions that worked fine for Python 2, but not for Python 3 (a lot of distributions name libypthon3 with a suffix, like mu). Since 4.10 was bumping the minimum required CMake version to 2.8.8, I moved in and rewrote the macros making use of the new functionality offered by upstream CMake (with a big help from Rolf Eike Beer).

As I worked more on PyKDE4, I hit a second snag: i18n() calls were chocking on Unicode. As QString is Unicode-aware, that was really difficult to debug. The root cause was actually a SNAFU of my own caused by swapping lines, and will be fixed in 4.9.4 and 4.10.

Lastly, I got word that Kate’s Pate was not building on Python 3. This caused a huge back and forth of mails and IRC conversations between me and Pate’s developer, Shaheed Haque. I initially fixed building with a very crude patch, which Shaheed improved afterwards. Then the product built under Python 3.3, but kept on crashing. After some debugging, two different issues were found (module not found when imported, and string conversions). With those fixed, Pate would load in Kate but crash when any plugin documentation was loaded. A few more rounds of discussion and commits, and I’m happy to report that everything works as intended!

In short, 4.10 should be a good release for Python 3 users. I have some ideas for 4.11 as well: time will tell if I get around to doing them.

Some more Nepomuk, please

Recently we’ve seen several blog posts on Planet KDE related to Nepomuk. Reading those I thought that I could add some (little) semantic features to Danbooru Client.

Danbooru Client already makes use of Nepomuk: if enabled, tags extracted from Danbooru items are added as Nepomuk tags. But since at least some Danbooru boards are specialized in certain types of images (e.g., wallpapers only, for examples) I found it would be nice to have Nepomuk show me only the images that come from a specific Danbooru board.

After a quick talk on IRC, the task proved to be easier than expected. What I did was to create a new  Nepomuk.Resource using the image board URL. Then I set it type as NFO::Website and added it as a related property to the resource pointing to the file. In code, this translates to (excerpt from the main file):

    resource = Nepomuk.File(KUrl(absolute_path))

    for tag in tags:

        if blacklist is not None and tag in blacklist:
            continue

        nepomuk_tag = Nepomuk.Tag(tag)
        nepomuk_tag.setLabel(tag)
        resource.addTag(nepomuk_tag)
        
    if board_url is not None:
        website_resource = Nepomuk.Resource(board_url)
        website_resource.addType(Nepomuk.Vocabulary.NFO.Website())
        website_resource.setLabel(board_url.prettyUrl())
        resource.setDescription(
                    i18n("Retrieved from %1").arg(board_url.prettyUrl()))
        resource.addIsRelated(website_resource)

Lo and behold, this is what happens after downloading one such image from Danbooru Client, in Dolphin (notice the “is related to”):

Clicking on the link will open in Dolphin all items related to the board in question. Neat, isn’t it? Of course, I’m very willing to add other features like that if there’s interest. Also, critiques on the approach are most welcome!

PyKDE4: Queries with Nepomuk

In one of my previous blog posts I dealt with tagging files and resources with Nepomuk. But Nepomuk is not only about storing metadata, it is also about retrieving and interrogating data. Normally, this would mean querying the metadata database directly, using queries written in SPARQL. But this is not intuitive, can be inefficient (if you do things the wrong way) and error prone (oops, I messed up a parameter!). 

Fortunately, the Nepomuk developers have come up with a high level API to query already stored metadata, and today’s post will deal with querying tags in Nepomuk. As per the past tutorials, the full source code is available in the kdeexamples module.

Let’s start off with the basic imports:

import sys

import PyQt4.QtCore as QtCore

import PyKDE4.kdecore as kdecore
import PyKDE4.kdeui as kdeui
from PyKDE4.kio import KIO
from PyKDE4.nepomuk import Nepomuk
from PyKDE4.soprano import Soprano

Then let’s create a simple class that wil be used for the rest of this exercise:

class NepomukTagQueryExample(QtCore.QObject):

    def __init__(self, parent=None):

        super(NepomukTagQueryExample, self).__init__(parent)

__init__ is just used to construct the instance, nothing more. The bulk of the work is in the query_tag() function, which we’ll take a look at in parts.

    def query_tag(self, tag):

        """Query for a specific tag."""

        tag = Nepomuk.Tag(tag)

First of all we convert the tag we want to query into a proper Nepomuk.Tag() instance. Of course we should use an already existing tag: even if Nepomuk.Tag() automatically creates new tags, it makes little sense to query for a newly created tag, doesn’t it?

For our job, we need to use properties which define the terms of our query. As we’re looking for tags, we’ll use Soprano.Vocabulary.NAO.hasTag():

        soprano_term_uri = Soprano.Vocabulary.NAO.hasTag()
        nepomuk_property = Nepomuk.Types.Property(soprano_term_uri)

The first call generates an URI pointing to a specific RDF resource for this specific term, which is then wrapped as a Nepomuk.Types.Property in the second call. While the C++ API docs don’t show this, I found it to be necessary, or the Python interpreter would raise a TypeError. Notice that this is not the only term we can use: aside for tags, there are a lot of other URIs we can use for querying, listed in the Soprano API docs.

Once we have our property set up, it’s time to define which kind of query we’re going to use. In this case, since we want to check for the presence of tags, we use a Nepomuk.Query.ComparisonTerm, which is a query term used to match values of specific properties (in our case, tags):

        comparison_term = Nepomuk.Query.ComparisonTerm(nepomuk_property,
                Nepomuk.Query.ResourceTerm(tag))

Our tag is wrapped in a ResourceTerm, which is used exactly for the purpose. Now we make the proper query: in this specific case, we want to look up files tagged, so we use a FileQuery. We could also get other items, such as mails (in Akonadi): in that case we could use a a Nepomuk.Query.Query():

        query = Nepomuk.Query.FileQuery(comparison_term)

Lastly, we want to get some results out of this query. There are different methods, but for this tutorial we’ll use the tried-and-tested KIO technology:

        search_url = query.toSearchUrl()
        search_job = KIO.listDir(kdecore.KUrl(search_url))
        search_job.entries.connect(self.search_slot)
        search_job.result.connect(search_job.entries.disconnect)

First we convert the query to a nepomuksearch:// url, which then we pass to KIO.listDir, to list the entries. Unlike my previous post on KIO, this job emits entries() every time one is found, so we connect the signal to our search_slot method. We also connect the job’s result() signal in a way that it will disconnect the job once it’s over.

Finally, let’s take a look at the search_slot function:

    def search_slot(self, job, data):

        # We may get invalid entries, so skip those
        if not data:
            return

        for item in data:
            print item.stringValue(KIO.UDSEntry.UDS_DISPLAY_NAME)

Entries are emitted as UDSEntries: to get something at least understandable, we turn them into the file name, which is obtained by the stringValue() call using KIO.UDSEntry.UDS_DISPLAY_NAME.

That’s it. As you can see, it was pretty easy. Of course there’s more than that. For further reading, take a look at Nepomuk’s Query API docs, and Query Examples. Bear in mind however that to the best of my knowledge, the “fancy operators” mentioned there will not work with Python.

Happy Nepomuk querying!