Development/Tutorials/Plasma4/Python/Using DataEngines
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.