Development/Tutorials/Plasma4/Python/Using DataEngines: Difference between revisions

    From KDE TechBase
    (corrections)
    m (Text replace - "</code>" to "</syntaxhighlight>")
    (3 intermediate revisions by 2 users not shown)
    Line 10: Line 10:


    Plasma comes with a handy tool called <tt>plasmaengineexplorer</tt> which lists all of the available DataEngines and can be used to examine them to see what kind of data and fields they send. <tt>plasmaengineexplorer</tt> can be started directly from the shell.
    Plasma comes with a handy tool called <tt>plasmaengineexplorer</tt> which lists all of the available DataEngines and can be used to examine them to see what kind of data and fields they send. <tt>plasmaengineexplorer</tt> can be started directly from the shell.
    <code bash>
    <syntaxhighlight lang="bash">
    plasmaengineexplorer
    plasmaengineexplorer
    </code>
    </syntaxhighlight>
    Select the "time" in the pulldown list and then click on "Request". This will fetch the list of data sources which the "time" DataEngine makes available.
    Select the "time" in the pulldown list and then click on "Request". This will fetch the list of data sources which the "time" DataEngine makes available.


    Line 27: Line 27:


    The code to connect to the "time" DataEngine is in the clock's <tt>connectToEngine()</tt> method, shown below.
    The code to connect to the "time" DataEngine is in the clock's <tt>connectToEngine()</tt> method, shown below.
    <code python>
    <syntaxhighlight lang="python">
    def connectToEngine(self):
    def connectToEngine(self):
         self.timeEngine = self.dataEngine("time")
         self.timeEngine = self.dataEngine("time")
    Line 36: Line 36:
             self.timeEngine.connectSource(self.currentTimezone,
             self.timeEngine.connectSource(self.currentTimezone,
                 self, 6000, Plasma.AlignToMinute)
                 self, 6000, Plasma.AlignToMinute)
    </code>
    </syntaxhighlight>
    This method is called from the clock's <tt>init()</tt> method. It first calls the <tt>dateEngine()</tt> method on the <tt>plasmascript.Applet</tt> class to get a reference to the "time" DataEngine.
    This method is called from the clock's <tt>init()</tt> method. It first calls the <tt>dateEngine()</tt> method on the <tt>plasmascript.Applet</tt> class to get a reference to the "time" DataEngine.


    Line 44: Line 44:


    Data is injected into the applet via the <tt>dataUpdate()</tt> method.
    Data is injected into the applet via the <tt>dataUpdate()</tt> method.
    <code python>
    <syntaxhighlight lang="python">
    @pyqtSignature("dataUpdated(const QString &, const Plasma::DataEngine::Data &)")
    @pyqtSignature("dataUpdated(const QString &, const Plasma::DataEngine::Data &)")
    def dataUpdated(self, sourceName, data):
    def dataUpdated(self, sourceName, data):
    Line 56: Line 56:
         self.lastTimeSeen = self.time
         self.lastTimeSeen = self.time
         self.update()
         self.update()
    </code>
    </syntaxhighlight>
    The <tt>dataUpdated()</tt> method takes the normal Python <tt>self</tt> parameter in first place, then the name of the data source (<tt>sourceName</tt>) which called <tt>dataUpdated()</tt>, and then the data itself. The big <tt>@pyqtSignature()</tt> line just before <tt>dataUpdated()</tt> is a Python decorator which marks this Python method as having <tt>const QString &, const Plasma::DataEngine::Data &</tt> as its C++ method signature. This is needed so that Qt and find the correct method on our applet when data is sent.
    The <tt>dataUpdated()</tt> method takes the normal Python <tt>self</tt> parameter in first place, then the name of the data source (<tt>sourceName</tt>) which called <tt>dataUpdated()</tt>, and then the data itself. The big <tt>@pyqtSignature()</tt> line just before <tt>dataUpdated()</tt> is a Python decorator which marks this Python method as having <tt>const QString &, const Plasma::DataEngine::Data &</tt> as its C++ method signature. This is needed so that Qt can find the correct method on our applet when data is sent.


    The code just ignores the <tt>sourceName</tt> parameter because it knows that the data must be from the "time" DataEngine, and just concentrates on the data. The <tt>sourceName</tt> parameter becomes important when you have multiple DataEngines attached to the same applet. The data is delivered as a Python <tt>dict</tt> mapping <tt>QString</tt> keys to <tt>QVariant</tt> object values. When looking up the "Time" key you need to be careful to use a <tt>QString</tt> as a key and not just a normal Python string. Notice how the code also extracts a <tt>QTime</tt> object from the <tt>QVariant</tt> using the <tt>toTime()</tt> method.
    The code just ignores the <tt>sourceName</tt> parameter because it knows that the data must be from the "time" DataEngine, and just concentrates on the data. The <tt>sourceName</tt> parameter becomes important when you have multiple DataEngines attached to the same applet. The data is delivered as a Python <tt>dict</tt> mapping <tt>QString</tt> keys to <tt>QVariant</tt> object values. When looking up the "Time" key you need to be careful to use a <tt>QString</tt> as a key and not just a normal Python string. Notice how the code also extracts a <tt>QTime</tt> object from the <tt>QVariant</tt> using the <tt>toTime()</tt> method.

    Revision as of 20:54, 29 June 2011

    Abstract

    One of the most important concepts in Plasma is that of the "DataEngine". DataEngines are used to deliver data from somewhere, often from an internet service for example, to your applet. In this tutorial we will discuss DataEngines, what they are, and how to use them from your Plasma applet.

    What are DataEngines?

    As already mentioned, DataEngines are objects which serve to deliver some kind of data to one or more Plasma applets. Plasma supports many different DataEngines out of the box which deliver all kinds of varied information about things like the state of the machine, e.g. CPU usage, memory usage, position of the mouse pointer; to things such as the current weather report, current time or the latest comic from the internet. DataEngines are used to supply the raw data which an applet displays. Separating the part of the code that fetches data from the part which displays the data, means that one DataEngine can be reused by many applets, regardless of which language the DataEngine or applet is written in. It also makes it easy for people to display the same data using different applets which may tuned towards different uses or screen form factors.

    Exploring DataEngines

    Plasma comes with a handy tool called plasmaengineexplorer which lists all of the available DataEngines and can be used to examine them to see what kind of data and fields they send. plasmaengineexplorer can be started directly from the shell.

    plasmaengineexplorer
    

    Select the "time" in the pulldown list and then click on "Request". This will fetch the list of data sources which the "time" DataEngine makes available.

    The "time" DataEngine periodically publishes the current time for many different timezones. The first timezone in the list ("/etc/localtime") is special and simply refers to the current local time on the computer without referring to any specific timezone. If you open the "/etc/localtime" node in the tree you can see which data fields the "time" DataEngine provides. The most important fields are "Time" and "Date". The plasmaengineexplorer also lists what data type the field is, e.g. a QString, QTime or QDate. This information is needed when hooking up a DataEngine to your applet.

    Explore some of the other DataEngines in the list. Perhaps it will give you some good ideas for new and interesting applets.

    Connecting to a DataEngine

    The Python clock code is a good example of how to connect to the "time" DateEngine. You can view the main.py code here online. Let's take a look at how the Python clock works.

    The code to connect to the "time" DataEngine is in the clock's connectToEngine() method, shown below.

    def connectToEngine(self):
        self.timeEngine = self.dataEngine("time")
        if self.showSecondHand:
            self.timeEngine.connectSource(self.currentTimezone,
                self, 500)
        else:
            self.timeEngine.connectSource(self.currentTimezone,
                self, 6000, Plasma.AlignToMinute)
    

    This method is called from the clock's init() method. It first calls the dateEngine() method on the plasmascript.Applet class to get a reference to the "time" DataEngine.

    The connectSource() method on the DataEngine binds it to this applet. The first parameter is the "DataSource" name. This corresponds to the "DataSouce" column in plasmaengineexplorer. For the "time" DataEngine, it selects which timezone you want the time for. The next parameter is where the data should be sent. In this case it is quite simple, just send the data to this applet. The number is the "polling interval" in milliseconds. This is how often the data should be updated. Depending on how often the clock needs to update its display, it calls the connectSource() method with either 500 or 6000 milliseconds as the polling interval.

    Plasma.AlignToMinute indicates to Plasma whether the updates should be syncronised to the system clock. When many applets syncronise the same way, then Plasma can handle the data updates for all of the applets in large batches instead of many small batches. This is important on mobile devices which throttle the CPU down to save battery power. It is more energy efficient for these devices if updates are done in big batches instead of many small batches.

    Data is injected into the applet via the dataUpdate() method.

    @pyqtSignature("dataUpdated(const QString &, const Plasma::DataEngine::Data &)")
    def dataUpdated(self, sourceName, data):
        self.time = data[QString("Time")].toTime()
    
        if self.time.minute() == self.lastTimeSeen.minute() and \
               self.time.second() == self.lastTimeSeen.second():
            # avoid unnecessary repaints
            return
    
        self.lastTimeSeen = self.time
        self.update()
    

    The dataUpdated() method takes the normal Python self parameter in first place, then the name of the data source (sourceName) which called dataUpdated(), and then the data itself. The big @pyqtSignature() line just before dataUpdated() is a Python decorator which marks this Python method as having const QString &, const Plasma::DataEngine::Data & as its C++ method signature. This is needed so that Qt can find the correct method on our applet when data is sent.

    The code just ignores the sourceName parameter because it knows that the data must be from the "time" DataEngine, and just concentrates on the data. The sourceName parameter becomes important when you have multiple DataEngines attached to the same applet. The data is delivered as a Python dict mapping QString keys to QVariant object values. When looking up the "Time" key you need to be careful to use a QString as a key and not just a normal Python string. Notice how the code also extracts a QTime object from the QVariant using the toTime() method.

    Next the dataUpdated() method has some code to make sure that the clock's display is only updated if really necessary, followed by the code to actually update the time and redraw the display.

    Conclusion

    As you can see DataEngines are a great way of delivering data to applets which can also be easily reused by different applets. Connecting a DataEngine to your applet is a simple matter of fetching the data engine itself, connecting up to the wanted data source and then accepting the processing the data in your own dataUpdated() method.