Writing Plasma Data Engines in Ruby

    richard dale's picture
    2008
    14
    Apr

    It sounds as though exciting things are happening at the Milan Tokamak Plasma sprint, with an api review and the Widgets on Canvas changes happening at the moment. Meanwhile, I've been having my own 'mini-sprint' this last week in Gran Canaria. I've ported the digital clock as well as the analog one, along with the web applet, plasmoid viewer and data engine browsers apps, and the time data engine to Ruby.

    I'll describe how the 'ruby-time' Data Engine code compares with the C++ one, and how the .desktop file looks like for a Ruby data engine plugin. This is the code for the time engine:

    require 'plasma_applet'
    
    class TimeEngine < Plasma::DataEngine
    
      def initialize(parent, args)
        super(parent)
        setMinimumUpdateInterval(333)
    
        # To have translated timezone names
        # (effectively a noop if the catalog is already present).
        KDE::Global.locale.insertCatalog("timezones4")
      end
    
      def sourceRequested(name)
        # puts "TimeEngine#sourceRequested #{name}"
        return updateSource(name)
      end
    
      def updateSource(tz)
        # puts "TimeEngine#updateTime"
        localName = I18N_NOOP("Local")
        if tz == localName
            setData(localName, I18N_NOOP("Time"), Qt::Variant.new(Qt::Time.currentTime))
            setData(localName, I18N_NOOP("Date"), Qt::Variant.new(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"), Qt::Variant.new(dt.time))
            setData(tz, I18N_NOOP("Date"), Qt::Variant.new(dt.date))
            timezone = tz
        end
    
        trTimezone = i18n(timezone)
        setData(tz, I18N_NOOP("Timezone"), Qt::Variant.new(trTimezone))
        tzParts = trTimezone.split("/")
    
        setData(tz, I18N_NOOP("Timezone Continent"), Qt::Variant.new(tzParts[0]))
        setData(tz, I18N_NOOP("Timezone City"), Qt::Variant.new(tzParts[1]))
    
        return true
      end
    end
    

    In the constructor, setMinimumUpdateInterval(333) specifies that the data should be update every third of a second. The engine implements the method updateSource() and uses setData() calls to pass the new values. Note that because Ruby doesn't have implicit constructors, you have to create Qt::Variants explicitely. Similarly, in the above example an instance of KDE::DateTime::Spec needs to be created, whereas in C++ it happens 'behind the scenes'.

    The .desktop file is called plasma-dataengine-ruby-time.desktop and here are the entries for the a Ruby engine:

    [Desktop Entry]
    Name=Time Data Engine
    Comment=Time data for Plasmoids
    ...
    X-KDE-ServiceTypes=Plasma/DataEngine
    Type=Service
    Icon=alarmclock
    X-KDE-Library=krubypluginfactory
    X-KDE-PluginKeyword=plasma-ruby-engine-time/time_engine.rb
    X-Plasma-EngineName=ruby-time
    

    The expected class of the data engine is derived from the ruby source filename, and so in this case 'TimeEngine' should be in time_engine.rb. The service type is just the same as for a C++ engine, and the X-KDE-Library and X-KDE-PluginKeyword entries specify how to load the ruby code. The library used is the same 'krubypluginfactory' for all KDE Ruby plugins.

    To install the engine, there is a very simple two line CMakeLists.txt file:

    install(FILES plasma-dataengine-ruby-time.desktop DESTINATION ${SERVICES_INSTALL_DIR} )
    install(FILES time_engine.rb DESTINATION ${DATA_INSTALL_DIR}/plasma-ruby-engine-time)
    

    All the example code is now under kdebindings/ruby/plasma/examples, and I've moved the clock example from the playground to there. One issue that I've come across is that in Ruby you need to make sure that the class names of your applets are unique. For example, both the digital and analog clock classes were called 'Clock' in C++, and that meant you couldn't run them at the Ruby versions at the same time. So I've renamed them DigitalClock and AnalogClock respectively. Similarly, the classes in the .ui files needed renaming as they were both called 'clockConfig' in the C++ versions.

    Tobias Koenig has written a nifty Ruby plasma applet that tells fortunes, and he will be giving a talk in May about Plasma, and using scripting languages with Plasma. I think writing applets and engines in Ruby is great fun anyway, and hope other people will start whipping them up soon..

    Comments

    Comment viewing options

    Select your preferred way to display the comments and click "Save settings" to activate your changes.
    chani's picture

    er...

    "In the constructor, setMinimumUpdateInterval(333) specifies that the data should be update every third of a second."

    not quite. it means that the data will never ever be updated any more frequently than 3 times a second. the exact update interval can be chosen by the applet using it - so it could be slower.

    apanloco's picture

    Nice!

    This was a nice read!
    Is the reason for two separate paths for timezones that getting local time+date is faster?

    /A

    richard dale's picture

    Re: Nice, but

    Yes, I think so - that's what the C++ engine did. A comment says 'this is relatively cheap - KSTZ.local is cached', and so I assume only local timezone stuff is cached.

    Comment viewing options

    Select your preferred way to display the comments and click "Save settings" to activate your changes.