Development/Tutorials/Plasma/Python/Writing DataEngines

Jump to: navigation, search

Abstract

As you have seen in the using DataEngines tutorial, Plasma applets can make use of DataEngines to visualize data of many different kinds. In fact, the already available DataEngines offer many options for your applets. But what if you have a specific need, not covered by those?

The problem is easily solved by writing your own Plasma DataEngine, and this tutorial will show you how to create one.

Prerequisites

As with applets, DataEngines need the same directory structure (see the Getting Started tutorial), so the first step is to create the appropriate directories. The difference with respect to applets lies in the metadata.desktop file:

[Desktop Entry] Name=Python Date and Time Comment=Python Time data for Plasmoids Type=Service Icon=preferences-system-time ServiceTypes=Plasma/DataEngine X-Plasma-API=python X-Plasma-MainScript=code/main.py X-KDE-PluginInfo-Author=Simon Edwards X-KDE-PluginInfo-Email=simon@simonzone.com X-KDE-PluginInfo-Name=plasma-dataengine-pytime X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=http://plasma.kde.org/ X-KDE-PluginInfo-Category=Python Date and Time X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=LGPL X-KDE-PluginInfo-EnabledByDefault=true

Take a look at the ServiceType line. When using applets, it was "Plasma/Applet", but since now we're dealing with DataEngines, its value is "Plasma/DataEngine".

Main script

For this tutorial we'll use the plasma-dataengine-pytime code, which is available on KDE's SVN. This DataEngine is an equivalent to the C++ time DataEngine we connected to in the using DataEngines tutorial. Its function is to display time and date with respect to the current timezone. You can view the main.py file online. As the file is rather long, we'll take a look at it in sections.

# Copyright stuff
 
from PyQt4.QtCore import *
from PyKDE4.kdecore import *
from PyKDE4 import plasmascript
 
class PyTimeEngine(plasmascript.DataEngine):
    def __init__(self,parent,args=None):
        plasmascript.DataEngine.__init__(self,parent)
 
    def init(self):
        self.setMinimumPollingInterval(333)
</code>
 
We start with the copyright information, followed by importing the required modules (<tt>PyQt4.QtCore</tt> and <tt>plasmascript</tt>). Since we're dealing with time and timezones, we also import <tt>kdecore</tt> to make use of what KDE offers. 
 
In <tt>__init__</tt> we just initialize the class. In <tt>init()</tt> instead, we set the minimum polling interval, that is  the minimum time (in milliseconds) that needs to pass between one data request (from an applet, for example) and another.
 
<syntaxhighlight lang="python">
# Continued from above
    def sources(self):
        sources = ["Local"]
        sources.extend(KSystemTimeZones.zones().keys())
        return sources
 
    def sourceRequestEvent(self, name):
        return self.updateSourceEvent(name)
</code>
 
The <tt>sources()</tt> function is called from applets to request what data sources a DataEngine has (for example, if your applet needs to connect to an online service, the DataEngine could offer a list of suitable services). In this case we first create a list with the "Local" name (which refers to the local time on your computer) and then we extend the list using <tt>KSystemTimeZones.zones().keys()</tt>. This adds the time zones known to KDE to the sources. As a final step, we return this list.
 
<tt>sourceRequestEvent</tt> is a function that is called when the applet accesses the DataEngine and requests a specific source (<tt>name</tt>). This function is usually used to make preparations (for example, setting up a connection) before the data can be received. In this case, we simply return <tt>updateSourceEvent</tt> with the source name.
 
<syntaxhighlight lang="python">
# Continued from above
    def updateSourceEvent(self, tz):
        localName = "Local"
        if tz == localName:
            self.setData(localName, "Time", QVariant(QTime.currentTime()))
            self.setData(localName, "Date", QVariant(QDate.currentDate()))
            # this is relatively cheap - KSTZ::local() is cached
            timezone = KSystemTimeZones.local().name()
        else:
            newTz = KSystemTimeZones.zone(tz)
            if not newTz.isValid():
                return False
            dt = KDateTime.currentDateTime(KDateTime.Spec(newTz))
            self.setData(tz, "Time", QVariant(dt.time()))
            self.setData(tz, "Date", QVariant(dt.date()))
            timezone = tz
 
        trTimezone = timezone
        self.setData(tz, "Timezone", QVariant(trTimezone));
        tzParts = str(trTimezone).split("/")
        if len(tzParts)>=2:
            self.setData(tz, "Timezone Continent", QVariant(tzParts[0]))
            self.setData(tz, "Timezone City", QVariant(tzParts[1]))
 
        return True
</code>
 
<tt>updateSourceEvent</tt> is the main function needed for a DataEngine, as it takes care of updating and storing the data from a specific source (in our case, <tt>tz</tt>, a timezone). As a first step, we set <tt>localName</tt> to be "Local", and we compare the timezone supplied to the function to it. If they're the same, i.e. local time on the computer, we set the data to current time and date (<tt>QTime.currentTime()</tt> and <tt>QDate.currentDate()</tt>), and then we get the name of the timezone using <tt>KSystemTimeZones.local().name()</tt>.
 
The important part here is <tt>setData</tt>. That is how we put data inside a DataEngine. <tt>setData</tt> accepts a source name, a key which will be used to look up the information, and the actual information, stored as a <tt>QVariant</tt>.
 
If "Local" is ''not'' the timezone supplied to <tt>updateSourceEvent</tt>, we obtain data on the timezone itself using <tt>KSystemTimeZones.zone(tz)</tt> and we assign it to <tt>newTz</tt>. Of course we may have supplied a non valid timezone, so we check it with <tt>newTz.isValid()</tt>, and we return False if it is not. After that, it's a matter of getting time and date relative to this timezone, through <tt>KDateTime.currentDateTime(KDateTime.Spec(newTz))</tt>. Then, we set the data into the DataEngine relative to date and time, and we assign <tt>timezone</tt> to <tt>tz</tt>.
 
Then, we set the timezone name (for example, "Europe/Rome") into the DataEngine. We also want to get continent and city into the DataEngine, so we split the string into a list with "/" and we check if contains two or more elements. If so, we set the first (for example "Europe") as continent and the second ("Rome") as city. Then the function returns True. <tt>updateSourceEvent</tt> should return True if succesful, and False if otherwise.
 
<syntaxhighlight lang="python">
def CreateDataEngine(parent):
    return PyTimeEngine(parent)
</code>
 
The last two lines deal, in a similar manner as applets, with creating the DataEngine.
 
== Installing the DataEngine ==
 
First of all, zip the directory contents (see the [[../GettingStarted|Getting Started tutorial]]), then invoke <tt>plasmapkg</tt> like this:
 
 plasmapkg -t dataengine -i <zip file name>
 
Plasma can't automatically determine the type of package you are installing (defaults to applet) so you have to manually specify the <tt>-t dataengine</tt> option. 
Once installed, you will be able to use this DataEngine in your applets.
 
== Testing the DataEngine ==
 
Run the <tt>plasmaeengineexplorer</tt> application (see [[../Using DataEngines | the Using DataEngines tutorial]]) and select the <tt>plasma-dataengine-pytime</tt> DataEngine. If all went well, it will behave in the same manner as the <tt>time</tt> DataEngine.
 
== Conclusion ==
 
You can effectively separate presentation and data handling in your applets by creating DataEngines which deal with all the needs for data. In a few lines of code, you can set up your sources, retrieve data and then store it for applets to use.
 
== Problems ==
 
Using your own python dataengines in KDE 4.2.0 leads to a crash. The problem is fixed in KDE 4.2.1.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V.Legal