Development/Tutorials/Plasma4/DataEngines: Difference between revisions

    From KDE TechBase
    (Create a new tutorial for data engines)
     
     
    (19 intermediate revisions by 11 users not shown)
    Line 1: Line 1:
    {{TutorialBrowser|
    {{TutorialBrowser|


    Line 5: Line 6:
    name=Writing a Data Engine|
    name=Writing a Data Engine|


    pre=[http://mindview.net/Books/TICPP/ThinkingInCPP2e.html C++], [http://www.trolltech.com/products/qt/ Qt], [[Getting_Started/Build/KDE4|KDE4 development environment]]|
    pre=[http://mindview.net/Books/TICPP/ThinkingInCPP2e.html C++], [http://www.trolltech.com/products/qt/ Qt], [[Getting_Started/Build|KDE development environment]]|


    next=|  
    next=|  


    reading=[http://api.kde.org/4.x-api/kdebase-workspace-apidocs/libs/plasma/html/classPlasma_1_1DataEngine.html DataEngine API], [[Development/Tutorials/CMake|CMake]], [[Development/Tutorials/Plasma/GettingStarted|Writing a Plasmoid]]
    reading=[http://api.kde.org/4.x-api/kdelibs-apidocs/plasma/html/classPlasma_1_1DataEngine.html DataEngine API], [[Development/Tutorials/CMake|CMake Tutorials]], [[Development/Tutorials/Plasma/GettingStarted|Writing a Plasmoid (Tutorial)]]
    }}
    }}




     
    DataEngines provide a standardized interface to various data sources for visualizations to use.
    ==Abstract==


    This tutorial will lead you through creating a simple data engine.  The example we will use is a simplified version of the time data engine provided with the basic plasma installation in kdebase-workspace.
    This tutorial will lead you through creating a simple data engine.  The example we will use is a simplified version of the time data engine provided with the basic plasma installation in kdebase-workspace.


    The time data engine provides the current date and time for any timezone that the system knows about.  It is used by every one of the many clock plasmoids.  The obvious benefit of this is that there is no need to reimplement the logic for selecting timezones in every clock plasmoid.
    The time data engine provides the current date and time for any timezone that the system knows about.  It is used by every one of the many clock plasmoids.  The obvious benefit of this is that there is no need to reimplement the logic for selecting timezones in every clock plasmoid.


    ==The Plasma Engine Explorer==
    ==The Plasma Engine Explorer==


    A very useful tool for anyone writing data engines is the Plasma engine explorer.  You can use it to see how the time data engine should work now by running
    A very useful tool for anyone writing data engines is the Plasma engine explorer.  You can use it to see how the time data engine should work now by running
    <code bash>
    <syntaxhighlight lang="bash">
    plasmaengineexplorer
    plasmaengineexplorer
    </code>
    </syntaxhighlight>


    Near the top of the window is a drop-down box for selecting a data engine.  Choose the "time" engine.  You can now request a source.  Try requesting the "Local" source, with an update interval of 500ms (twice a second).  A single item, Local, should be visible in the sources list.  Click the + on the left to expand it, and you should get the date, time, timezone, timezone continent and timezone city listed, together with the type for each.
    Near the top of the window is a drop-down box for selecting a data engine.  Choose the "time" engine.  You can now request a source.  Try requesting the "Local" source, with an update interval of 500ms (twice a second).  A single item, Local, should be visible in the sources list.  Click the + on the left to expand it, and you should get the date, time, timezone, timezone continent and timezone city listed, together with the type for each.
    Line 44: Line 43:


    '''plasma-engine-testtime.desktop'''
    '''plasma-engine-testtime.desktop'''
    <code ini>
    <syntaxhighlight lang="ini">
    [Desktop Entry]
    [Desktop Entry]
    Name=Test Time Engine
    Name=Test Time Engine
    Line 62: Line 61:
    X-KDE-PluginInfo-License=LGPL
    X-KDE-PluginInfo-License=LGPL
    X-KDE-PluginInfo-EnabledByDefault=true
    X-KDE-PluginInfo-EnabledByDefault=true
    </code>
    </syntaxhighlight>


    The most important bits are:
    The most important bits are:
    * the Name, Comment and Type fields, which are required for all desktop files
    * the <tt>Name</tt>, <tt>Comment</tt> and <tt>Type</tt> fields, which are required for all desktop files
    * the X-KDE-ServiceTypes field, which tells plasma that this is a data engine
    * the <tt>X-KDE-ServiceTypes</tt> field, which tells plasma that this is a data engine
    * the X-KDE-Library field, which tells plasma how to load the data engine - in this case by loading plasma_engine_testtime.so from the plugin folder
    * the <tt>X-KDE-Library</tt> field, which tells plasma how to load the data engine - in this case by loading <tt>plasma_engine_testtime.so</tt> from the plugin folder
    * the X-Plasma-EngineName field, which tells plasma what the exported plugin name is (as given to K_EXPORT_PLASMA_DATAENGINE)
    * the <tt>X-Plasma-EngineName</tt> field, which tells plasma what the exported plugin name is (as given to <tt>K_EXPORT_PLASMA_DATAENGINE</tt>)
    * the <tt>X-KDE-PluginInfo-Name</tt> is important for DataEngines since 4.2, without it, plasma will not find your DataEngine




    Line 74: Line 74:


    '''testtimeengine.h'''
    '''testtimeengine.h'''
    <code cppqt>
    <syntaxhighlight lang="cpp-qt">
    // the following header is required by the LGPL because
    // the following header is required by the LGPL because
    // we are using the actual time engine code
    // we are using the actual time engine code
    Line 131: Line 131:


    #endif // TESTTIMEENGINE_H
    #endif // TESTTIMEENGINE_H
    </code>
    </syntaxhighlight>


    A separate header file may seem a bit pointless for a plugin this small, but it is a good habit to get into.
    A separate header file may seem a bit pointless for a plugin this small, but it is a good habit to get into.


    Note that I don't put a K_EXPORT_PLASMA_DATAENGINE in the header file.  When there is only one .cpp file in the project, there is no problem, but in larger plugins several files might include this header and there can only be one K_EXPORT_PLASMA_DATAENGINE in the project.  If K_EXPORT_PLASMA_DATAENGINE is compiled in multiple times, you will get a linker error when compiling.
    Note that I don't put a K_EXPORT_PLASMA_DATAENGINE in the header file.  When there is only one .cpp file in the project, there is no problem, but in larger plugins several files might include this header and there can only be one K_EXPORT_PLASMA_DATAENGINE in the project.  If K_EXPORT_PLASMA_DATAENGINE is compiled in multiple times, you will get a linker error when compiling.
     
    Also note that the name should '''not''' end on "engine" (e.g. "testtimeengine"), because then Plasma does not find the DataEngine correctly.


    ===The Main Code===
    ===The Main Code===


    '''testtimeengine.cpp'''
    '''testtimeengine.cpp'''
    <code cppqt>
    <syntaxhighlight lang="cpp-qt">
    // the following header is required by the LGPL because
    // the following header is required by the LGPL because
    // we are using the actual time engine code
    // we are using the actual time engine code
    Line 171: Line 171:
    #include <KDateTime>
    #include <KDateTime>


    #include "plasma/datacontainer.h"
    #include <Plasma/DataContainer>


    TestTimeEngine::TestTimeEngine(QObject* parent, const QVariantList& args)
    TestTimeEngine::TestTimeEngine(QObject* parent, const QVariantList& args)
    Line 220: Line 220:
    // this plugin.  The first argument must match
    // this plugin.  The first argument must match
    // the X-Plasma-EngineName in the .desktop file.
    // the X-Plasma-EngineName in the .desktop file.
    // The second argument is the name of the class in
    // your plugin that derives from Plasma::DataEngine
    K_EXPORT_PLASMA_DATAENGINE(testtime, TestTimeEngine)
    K_EXPORT_PLASMA_DATAENGINE(testtime, TestTimeEngine)


    // this is needed since TestTimeEngine is a QObject
    // this is needed since TestTimeEngine is a QObject
    #include "timeengine.moc"
    #include "testtimeengine.moc"
    </code>
    </syntaxhighlight>




    Line 238: Line 240:
    ====Controlling Update Frequency====
    ====Controlling Update Frequency====


    There are a couple of methods for controlling updates.  We use <tt>setMinimumPollingInterval(33)</tt> to prevent <tt>updateSourceEvent()</tt> being called more than three times a second for any given source.  You can also enforce a specific update interval for all sources provided by this engine with <tt>setPollingInterval()</tt>, which takes a time in milliseconds as its only argument.
    There are a couple of methods for controlling updates.  We use <tt>setMinimumPollingInterval(333)</tt> to prevent <tt>updateSourceEvent()</tt> being called more than three times a second for any given source.  You can also enforce a specific update interval for all sources provided by this engine with <tt>setPollingInterval()</tt>, which takes a time in milliseconds as its only argument.
     


    ====Responding to Outside Events====
    ====Responding to Outside Events====


    Polling does not make sense for all engines.  Some engines, such as the device engine, respond to outside events.  You can use <tt>setData()</tt> at any time to create or update a source.  <tt>removeData()</tt> and <tt>removeSource()</tt> are also useful methods.
    Polling does not make sense for all engines.  Some engines, such as the device engine, respond to outside events.  You can use <tt>setData()</tt> at any time to create or update a source.  <tt>removeData()</tt> and <tt>removeSource()</tt> are also useful methods. For the applets that want to be informed when the sources change, without using polling, use <tt>dataUpdated</tt>. Use it by calling <tt>connectSource</tt>




    Line 263: Line 264:


    The <tt>DataContainer</tt> class can be used to give you more control over updating data if you need it.  The relevant methods of <tt>DataEngine</tt> are <tt>DataContainer* containerForSource(const QString& source)</tt>, <tt>void addSource(DataContainer* source)</tt> and <tt>SourceDict containerDict() const</tt>.
    The <tt>DataContainer</tt> class can be used to give you more control over updating data if you need it.  The relevant methods of <tt>DataEngine</tt> are <tt>DataContainer* containerForSource(const QString& source)</tt>, <tt>void addSource(DataContainer* source)</tt> and <tt>SourceDict containerDict() const</tt>.


    ===Building It===
    ===Building It===
    Line 270: Line 269:
    The CMakeLists.txt file tells CMake how to build your plugin.  The following file will work for this project:
    The CMakeLists.txt file tells CMake how to build your plugin.  The following file will work for this project:


    <code bash>
    <syntaxhighlight lang="bash">
    # A name for the project
    # A name for the project
    project(plasma-testtime)
    project(plasma-testtime)
    Line 277: Line 276:
    find_package(KDE4 REQUIRED)
    find_package(KDE4 REQUIRED)
    include(KDE4Defaults)
    include(KDE4Defaults)
    find_package(Plasma REQUIRED)


    add_definitions (${QT_DEFINITIONS} ${KDE4_DEFINITIONS})
    add_definitions (${QT_DEFINITIONS} ${KDE4_DEFINITIONS})
    Line 293: Line 291:
    target_link_libraries(plasma_engine_testtime
    target_link_libraries(plasma_engine_testtime
                           ${KDE4_KDECORE_LIBS}
                           ${KDE4_KDECORE_LIBS}
                           ${PLASMA_LIBS})
                           ${KDE4_PLASMA_LIBS})


    install(TARGETS plasma_engine_testtime
    install(TARGETS plasma_engine_testtime
    Line 300: Line 298:
    install(FILES plasma-engine-testtime.desktop
    install(FILES plasma-engine-testtime.desktop
             DESTINATION ${SERVICES_INSTALL_DIR})
             DESTINATION ${SERVICES_INSTALL_DIR})
    </code>
    </syntaxhighlight>




    Line 306: Line 304:


    Run
    Run
    <code bash>
    <syntaxhighlight lang="bash">
    cmake -DCMAKE_BUILD_TYPE=debugfull -DCMAKE_INSTALL_PREFIX=$KDEDIR
    cmake -DCMAKE_BUILD_TYPE=debugfull -DCMAKE_INSTALL_PREFIX=$KDEDIR
    make
    make
    make install
    make install
    </code>
    </syntaxhighlight>


    Replace <tt>$KDEDIR</tt> with your kde installation directory if <tt>$KDEDIR</tt> is not set.
    Replace <tt>$KDEDIR</tt> with your kde installation directory if <tt>$KDEDIR</tt> is not set.


    Alternatively, you can run the following
    Alternatively, you can run the following
    <code bash>
    <syntaxhighlight lang="bash">
    cmake -DCMAKE_BUILD_TYPE=debugfull
    cmake -DCMAKE_BUILD_TYPE=debugfull
    make
    make
    cp ./lib/plasma_engine_testtime.so $KDEDIR/lib/kde4
    cp ./lib/plasma_engine_testtime.so $KDEDIR/lib/kde4
    cp ./plasma-engine-testtime.desktop $KDEDIR/share/kde4/services/
    cp ./plasma-engine-testtime.desktop $KDEDIR/share/kde4/services/
    </code>
    </syntaxhighlight>


    Now run <tt>kbuildsycoca4</tt> to tell KDE applications (including plasma and the plasma engine explorer) about the new desktop file.
    Now run <tt>kbuildsycoca4</tt> to tell KDE applications (including plasma and the plasma engine explorer) about the new desktop file.


    Now you can test it as we did the time engine.  Run
    Now you can test it as we did the time engine.  Run
    <code bash>
    <syntaxhighlight lang="bash">
    plasmaengineexplorer --engine testtime
    plasmaengineexplorer --engine testtime
    </code>
    </syntaxhighlight>


    Note that if you change a data engine, the change will not register in any running applications.  After modifying and reinstalling a data engine, to make plasma register the change you must run
    Note that if you change a data engine (or applet), the change will not register in any running applications.  After modifying and reinstalling a data engine, to make plasma register the change you must run
    <code bash>
    <syntaxhighlight lang="bash">
    kbuildsycoca4
    kbuildsycoca4
    kquitapp plasma
    kquitapp plasma-desktop (or plasma-netbook, etc)
    plasma  
    plasma-desktop
    </code>
    </syntaxhighlight>

    Latest revision as of 23:27, 11 September 2014

    Writing a Data Engine
    Tutorial Series   Plasma Tutorial
    Previous   C++, Qt, KDE development environment
    What's Next  
    Further Reading   DataEngine API, CMake Tutorials, Writing a Plasmoid (Tutorial)


    DataEngines provide a standardized interface to various data sources for visualizations to use.

    This tutorial will lead you through creating a simple data engine. The example we will use is a simplified version of the time data engine provided with the basic plasma installation in kdebase-workspace.

    The time data engine provides the current date and time for any timezone that the system knows about. It is used by every one of the many clock plasmoids. The obvious benefit of this is that there is no need to reimplement the logic for selecting timezones in every clock plasmoid.

    The Plasma Engine Explorer

    A very useful tool for anyone writing data engines is the Plasma engine explorer. You can use it to see how the time data engine should work now by running

    plasmaengineexplorer
    

    Near the top of the window is a drop-down box for selecting a data engine. Choose the "time" engine. You can now request a source. Try requesting the "Local" source, with an update interval of 500ms (twice a second). A single item, Local, should be visible in the sources list. Click the + on the left to expand it, and you should get the date, time, timezone, timezone continent and timezone city listed, together with the type for each.

    Have a play with selecting other timezones, such as "Europe/Paris" and "Asia/Tokyo".

    Remember to test your data engine using the Plasma engine explorer when you have created it!


    The Code

    The Desktop File

    Every data engine needs a description file to tell plasma how to load it and what it is called.

    plasma-engine-testtime.desktop

    [Desktop Entry]
    Name=Test Time Engine
    Comment=A duplicate of the Time Engine
    Type=Service
    
    X-KDE-ServiceTypes=Plasma/DataEngine
    X-KDE-Library=plasma_engine_testtime
    X-Plasma-EngineName=testtime
    X-KDE-PluginInfo-Author=Aaron Seigo
    X-KDE-PluginInfo-Email=[email protected]
    X-KDE-PluginInfo-Name=testtime
    X-KDE-PluginInfo-Version=0.1
    X-KDE-PluginInfo-Website=http://plasma.kde.org/
    X-KDE-PluginInfo-Category=Examples
    X-KDE-PluginInfo-Depends=
    X-KDE-PluginInfo-License=LGPL
    X-KDE-PluginInfo-EnabledByDefault=true
    

    The most important bits are:

    • the Name, Comment and Type fields, which are required for all desktop files
    • the X-KDE-ServiceTypes field, which tells plasma that this is a data engine
    • the X-KDE-Library field, which tells plasma how to load the data engine - in this case by loading plasma_engine_testtime.so from the plugin folder
    • the X-Plasma-EngineName field, which tells plasma what the exported plugin name is (as given to K_EXPORT_PLASMA_DATAENGINE)
    • the X-KDE-PluginInfo-Name is important for DataEngines since 4.2, without it, plasma will not find your DataEngine


    The Header File

    testtimeengine.h

    // the following header is required by the LGPL because
    // we are using the actual time engine code
    /*
     *   Copyright 2007 Aaron Seigo <[email protected]>
     *
     *   This program is free software; you can redistribute it and/or modify
     *   it under the terms of the GNU Library General Public License as
     *   published by the Free Software Foundation; either version 2 or
     *   (at your option) any later version.
     *
     *   This program is distributed in the hope that it will be useful,
     *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     *   GNU General Public License for more details
     *
     *   You should have received a copy of the GNU Library General Public
     *   License along with this program; if not, write to the
     *   Free Software Foundation, Inc.,
     *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
     */
    
    // a standard include guard to prevent problems if the
    // header is included multiple times
    #ifndef TESTTIMEENGINE_H
    #define TESTTIMEENGINE_H
    
    // We need the DataEngine header, since we are inheriting it
    #include <Plasma/DataEngine>
    
    /**
     * This engine provides the current date and time for a given
     * timezone.
     *
     * "Local" is a special source that is an alias for the current
     * timezone.
     */
    class TestTimeEngine : public Plasma::DataEngine
    {
        // required since Plasma::DataEngine inherits QObject
        Q_OBJECT
    
        public:
            // every engine needs a constructor with these arguments
            TestTimeEngine(QObject* parent, const QVariantList& args);
    
        protected:
            // this virtual function is called when a new source is requested
            bool sourceRequestEvent(const QString& name);
    
            // this virtual function is called when an automatic update
            // is triggered for an existing source (ie: when a valid update
            // interval is set when requesting a source)
            bool updateSourceEvent(const QString& source);
    };
    
    #endif // TESTTIMEENGINE_H
    

    A separate header file may seem a bit pointless for a plugin this small, but it is a good habit to get into.

    Note that I don't put a K_EXPORT_PLASMA_DATAENGINE in the header file. When there is only one .cpp file in the project, there is no problem, but in larger plugins several files might include this header and there can only be one K_EXPORT_PLASMA_DATAENGINE in the project. If K_EXPORT_PLASMA_DATAENGINE is compiled in multiple times, you will get a linker error when compiling. Also note that the name should not end on "engine" (e.g. "testtimeengine"), because then Plasma does not find the DataEngine correctly.

    The Main Code

    testtimeengine.cpp

    // the following header is required by the LGPL because
    // we are using the actual time engine code
    /*
     *   Copyright 2007 Aaron Seigo <[email protected]>
     *
     *   This program is free software; you can redistribute it and/or modify
     *   it under the terms of the GNU Library General Public License as
     *   published by the Free Software Foundation; either version 2 or
     *   (at your option) any later version.
     *
     *   This program is distributed in the hope that it will be useful,
     *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     *   GNU General Public License for more details
     *
     *   You should have received a copy of the GNU Library General Public
     *   License along with this program; if not, write to the
     *   Free Software Foundation, Inc.,
     *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
     */
    
    #include "testtimeengine.h"
    
    #include <QDate>
    #include <QTime>
    
    #include <KSystemTimeZones>
    #include <KDateTime>
    
    #include <Plasma/DataContainer>
    
    TestTimeEngine::TestTimeEngine(QObject* parent, const QVariantList& args)
        : Plasma::DataEngine(parent, args)
    {
        // We ignore any arguments - data engines do not have much use for them
        Q_UNUSED(args)
    
        // This prevents applets from setting an unnecessarily high
        // update interval and using too much CPU.
        // In the case of a clock that only has second precision,
        // a third of a second should be more than enough.
        setMinimumPollingInterval(333);
    }
    
    bool TestTimeEngine::sourceRequestEvent(const QString &name)
    {
        // We do not have any special code to execute the
        // first time a source is requested, so we just call
        // updateSourceEvent().
        return updateSourceEvent(name);
    }
    
    bool TestTimeEngine::updateSourceEvent(const QString &name)
    {
        QString timezone;
    
        if (name == I18N_NOOP("Local")) {
            // Local is a special case - we just get the current time and date
            setData(name, I18N_NOOP("Time"), QTime::currentTime());
            setData(name, I18N_NOOP("Date"), QDate::currentDate());
        } else {
            // First check the timezone is valid
            KTimeZone newTz = KSystemTimeZones::zone(name);
            if (!newTz.isValid()) {
                return false;
            }
    
            // Get the date and time
            KDateTime dt = KDateTime::currentDateTime(newTz);
            setData(name, I18N_NOOP("Time"), dt.time());
            setData(name, I18N_NOOP("Date"), dt.date());
        }
        return true;
    }
    
    // This does the magic that allows Plasma to load
    // this plugin.  The first argument must match
    // the X-Plasma-EngineName in the .desktop file.
    // The second argument is the name of the class in
    // your plugin that derives from Plasma::DataEngine
    K_EXPORT_PLASMA_DATAENGINE(testtime, TestTimeEngine)
    
    // this is needed since TestTimeEngine is a QObject
    #include "testtimeengine.moc"
    


    Responding to Requests for Sources

    The sourceRequestEvent() method is called the first time a source is loaded. You can use this to do any special processing that should be done the first time a source is created, but not when it is subsequently updated.

    If a source is created with a polling interval, updateSourceEvent() will be called each time that interval expires. For example, if an applet requests a source "foo" with a polling interval of 500ms, sourceRequestEvent("foo") will be called initially, then half a second later, and every half a second after that, updateSourceEvent("foo") will be called.

    Note, however, that applets do not have to set a polling interval and may instead simply wait for an engine to push data to it.


    Controlling Update Frequency

    There are a couple of methods for controlling updates. We use setMinimumPollingInterval(333) to prevent updateSourceEvent() being called more than three times a second for any given source. You can also enforce a specific update interval for all sources provided by this engine with setPollingInterval(), which takes a time in milliseconds as its only argument.

    Responding to Outside Events

    Polling does not make sense for all engines. Some engines, such as the device engine, respond to outside events. You can use setData() at any time to create or update a source. removeData() and removeSource() are also useful methods. For the applets that want to be informed when the sources change, without using polling, use dataUpdated. Use it by calling connectSource


    Listing Potential Sources

    Applets can request a list of the available sources for an engine. By default, this is just a list of every source that has been created (with setData() or addSource()) so far (and not removed, of course). However, you may wish to advertise sources but not actually create them. For example, we could advertise every timezone that the system knows about, but not actually create and populate the sources, since hardly any will actually be used.

    This can be done by reimplementing virtual QStringList sources() const and returning a list of the names of all the available sources.


    Initialisation

    If you want to perform any initialisation beyond setting simple properties, such as populating sources, you cannot do it in the constructor, since the data engine has not been properly initialised at this point. Instead, reimplement the virtual void init() method.

    For example, the tasks data engine uses the init() method to get a list of all the currently running tasks and creates sources for each of them.


    More Control Over Data

    The DataContainer class can be used to give you more control over updating data if you need it. The relevant methods of DataEngine are DataContainer* containerForSource(const QString& source), void addSource(DataContainer* source) and SourceDict containerDict() const.

    Building It

    The CMakeLists.txt file tells CMake how to build your plugin. The following file will work for this project:

    # A name for the project
    project(plasma-testtime)
    
    # Find the required Libaries
    find_package(KDE4 REQUIRED)
    include(KDE4Defaults)
    
    add_definitions (${QT_DEFINITIONS} ${KDE4_DEFINITIONS})
    include_directories(
       ${CMAKE_SOURCE_DIR}
       ${CMAKE_BINARY_DIR}
       ${KDE4_INCLUDES}
       )
    
    # We add our source code here
    set(testtime_engine_SRCS testtimeengine.cpp)
    
    # Now make sure all files get to the right place
    kde4_add_plugin(plasma_engine_testtime ${testtime_engine_SRCS})
    target_link_libraries(plasma_engine_testtime
                          ${KDE4_KDECORE_LIBS}
                          ${KDE4_PLASMA_LIBS})
    
    install(TARGETS plasma_engine_testtime
            DESTINATION ${PLUGIN_INSTALL_DIR})
    
    install(FILES plasma-engine-testtime.desktop
            DESTINATION ${SERVICES_INSTALL_DIR})
    


    Testing

    Run

    cmake -DCMAKE_BUILD_TYPE=debugfull -DCMAKE_INSTALL_PREFIX=$KDEDIR
    make
    make install
    

    Replace $KDEDIR with your kde installation directory if $KDEDIR is not set.

    Alternatively, you can run the following

    cmake -DCMAKE_BUILD_TYPE=debugfull
    make
    cp ./lib/plasma_engine_testtime.so $KDEDIR/lib/kde4
    cp ./plasma-engine-testtime.desktop $KDEDIR/share/kde4/services/
    

    Now run kbuildsycoca4 to tell KDE applications (including plasma and the plasma engine explorer) about the new desktop file.

    Now you can test it as we did the time engine. Run

    plasmaengineexplorer --engine testtime
    

    Note that if you change a data engine (or applet), the change will not register in any running applications. After modifying and reinstalling a data engine, to make plasma register the change you must run

    kbuildsycoca4
    kquitapp plasma-desktop (or plasma-netbook, etc)
    plasma-desktop