Writing Plasma Data Engines in C# and Ruby
I feel a bit stuck in a time warp, having already written blogs with much the same title and subject as this one, back in April. The difference is that it is now possible to use the Plasma Script Engine api and associated packaging mechanism, as opposed to the earlier bindings, which were based on the C++ plugin api. Of course, being able to write engines in C# as well as Ruby is something new.
And don't forget that for Python fans, Simon Edwards has implemented similarly comprehensive bindings very close to the C++ api, that you can use to write both applets and data engines with KDE 4.2.
There is very little change needed in the code compared with the earlier Ruby bindings. You now need to call the main class 'Main' and it needs to be a subclass of PlasmaScripting::DataEngine instead of Plasma::DataEngine.
Here is what the Ruby code for the time engine now looks like:
require 'plasma_applet' module RubyTime class Main < PlasmaScripting::DataEngine def initialize(parent, args = nil) super(parent) end def init setMinimumPollingInterval(333) # To have translated timezone names # (effectively a noop if the catalog is already present). KDE::Global.locale.insertCatalog("timezones4") dbus = Qt::DBusConnection.sessionBus dbus.connect("", "", "org.kde.KTimeZoned", "configChanged", this, SLOT(:updateAllSources)) end def sources timezones = KDE::SystemTimeZones.zones.keys timezones << "Local" return timezones end def sourceRequestEvent(name) return updateSourceEvent(name) end def updateSourceEvent(tz) # puts "TimeEngine#updateTime" localName = I18N_NOOP("Local") if tz == localName setData(localName, I18N_NOOP("Time"), Qt::Time.currentTime) setData(localName, I18N_NOOP("Date"), Qt::Date.currentDate) # this is relatively cheap - KSTZ.local is cached timezone = KDE::SystemTimeZones.local.name else newTz = KDE::SystemTimeZones.zone(tz) unless newTz.valid? return false end dt = KDE::DateTime.currentDateTime(KDE::DateTime::Spec.new(newTz)) setData(tz, I18N_NOOP("Time"), dt.time) setData(tz, I18N_NOOP("Date"), dt.date) timezone = tz end trTimezone = i18n(timezone) setData(tz, I18N_NOOP("Timezone"), trTimezone) tzParts = trTimezone.split("/") setData(tz, I18N_NOOP("Timezone Continent"), tzParts[0]) setData(tz, I18N_NOOP("Timezone City"), tzParts[1]) return true end end end
You need to put the code into a standard plasmoid directory structure like this:
time metadata.desktop contents code main.rb
To install a data engine you use the plasmapkg tool from the command line like this:
# Initial installation: $ plasmapkg --install time --type dataengineTo reinstall:
$ plasmapkg --upgrade time --type dataengine
The Ruby desktop file for the time engine looks like this:
[Desktop Entry] Name=Date and Time Comment=Time data for PlasmoidsType=Service ServiceTypes=Plasma/DataEngine X-Plasma-API=ruby-script
X-KDE-PluginInfo-Author=Richard Dale X-KDE-PluginInfo-Email=richard.j.dale@gmail.com X-KDE-PluginInfo-Name=ruby-time X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=http://plasma.kde.org/ X-KDE-PluginInfo-Category=Date and Time X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true
The 'X-KDE-PluginInfo-Name=ruby-time' line is used to name where the engine gets installed, and it is also the name you use to invoke it.
Here is the C# version of the same engine for comparison:
public class TimeEngine : PlasmaScripting.DataEngine, IDisposable { private static string localName = "Local"; public TimeEngine(DataEngineScript parent) : base(parent) { SetMinimumPollingInterval(333); // To have translated timezone names // (effectively a noop if the catalog is already present). KGlobal.Locale().InsertCatalog("timezones4"); } public override void Init() { base.Init(); QDBusConnection dbus = QDBusConnection.SessionBus(); dbus.Connect("", "", "org.kde.KTimeZoned", "configChanged", this, SLOT("UpdateAllSources()")); } public override ListSources() { List timezones = new List (KSystemTimeZones.Zones().Keys); timezones.Add("Local"); return timezones; } public override bool SourceRequestEvent(string name) { return UpdateSourceEvent(name); } public override bool UpdateSourceEvent(string tz) { string timezone; if (tz == localName) { SetData(localName, "Time", QTime.CurrentTime()); SetData(localName, "Date", QDate.CurrentDate()); // this is relatively cheap - KSTZ::local() is cached timezone = KSystemTimeZones.Local().Name(); } else { KTimeZone newTz = KSystemTimeZones.Zone(tz); if (!newTz.IsValid()) { return false; } KDateTime dt = KDateTime.CurrentDateTime(new KDateTime.Spec(newTz)); SetData(tz, "Time", dt.Time()); SetData(tz, "Date", dt.Date()); timezone = tz; } string trTimezone = KDE.I18n(timezone); SetData(tz, "Timezone", trTimezone); string[] tzParts = trTimezone.Split(new char[] { '/' }); SetData(tz, "Timezone Continent", tzParts[0]); SetData(tz, "Timezone City", tzParts[1]); return true; } }
And the metadate.desktop file for C# looks like this:
[Desktop Entry] Name=Date and Time Comment=Time data for Plasmoids Type=Service ServiceTypes=Plasma/DataEngine X-Plasma-API=mono-scriptX-KDE-PluginInfo-Author=Richard Dale X-KDE-PluginInfo-Email=richard.j.dale@gmail.com X-KDE-PluginInfo-Name=csharp-time X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=http://plasma.kde.org/ X-KDE-PluginInfo-Category=Date and Time X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true
Whereas you can just edit the Ruby code when it is in the standard plasmoid directory structure, for C# it is a bit more tricky and you need to create a cmake file. Arno Rehn has done an ingenious CMakeFile.txt that will allow you to work on the source files in your $src directory, and then compile them into the plasmoid directory structure in the $build directory that can be directly installed by 'plasmapkg':
project(cs-time-engine) include(CSharpMacros) set(SRC_TIMEENGINE timeengine.cs) set( CS_FLAGS -warn:0 "-r:${LIBRARY_OUTPUT_PATH}/qt-dotnet.dll, ${LIBRARY_OUTPUT_PATH}/kde-dotnet.dll, ${LIBRARY_OUTPUT_PATH}/plasma-dll.dll" ) add_cs_library(time-engine "${SRC_TIMEENGINE}" ALL) add_dependencies(time-engine plasma) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/csharp-time/contents/code) install( FILES ${LIBRARY_OUTPUT_PATH}/time-engine.dll DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/csharp-time/contents/code RENAME main ) install(FILES metadata.desktop DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/csharp-time)
Hopefully, by the time KDE 4.2 is released we get can this kind of info copied over to the Tech Base Wiki, and also add tutorials and other examples to try and get the non-C++ Plasmoid community boot strapped.- if anyone wants to help out on that it would be great. I'm really looking forward to seeing what sort of scripting Plasmoids people will come up with.