Development/Tutorials/Plasma4/UsingExtenders

    From KDE TechBase
    Revision as of 12:32, 1 September 2008 by Pinda (talk | contribs)

    Under Construction

    Abstract

    This tutorial needs KDE 4.2 (trunk) to build. For correct behavior you should also build ExtenderApplet in playground (I really should be moving this to kdebase soon). And finally, you'll need to build the kuiserver dataengine (also in playground).

    In this tutorial I will show how to create a simple applet that uses an extender. This applet is basically a very stripped down version of the kuiserver applet.

    About extenders

    Extenders allow applets to, with only very little extra lines of code, use relocatable widgets. These ExtenderItem objects, can be dragged around by the user, dropped anywhere, and can even be persistent between sessions. They can also keep running, even when the applet that created them, isn't. The class that shows and manages these extender items is Plasma::Extender.

    How to approach extenders

    If you want to use extenders in your applet you'll usually have to do a couple of things:

    • Instatiate an Extender.
    • Add ExtenderItem objects to this Extender.
    • optional: Implement a initExtenderItem(ExtenderItem*) function in your applet, if you want to make your extender items persistent. Use this function to create a widget to display in your ExtenderItem.
    • optional: Add some extra actions to your ExtenderItems.

    To don't have to border with your applet resizing correctly when the extender changes size, and to have your applet automatically inconified when in a panel, I'd strongy advice you to use PopupApplet as a base class for your applets if you wish to use extenders. It will spare you a lot of hassle.

    The code

    Here I will show you our very simple stripped down kuiserver applet. It will show every new job in an ExtenderItem using a Plasma::Meter to display the jobs progress. In the title bar of the ExtenderItem the name of the datasource will be displayed, and a remove ExtenderItem action is added, so a 'delete' icon is displayed in the items titlebar. Finally, the items will also be sort of persistent. When plasma is restarted, all detached items will get shown again, but will only show the name of the original datasource it was connected to as title. Kuiserver datasources are not persistent between sessions (yet?) so the meter won't show progress anymore. Yes, it isn't very usable, but this is only an example.

    The header

    1. ifndef EXTENDERTUTORIAL_H
    2. define EXTENDERTUTORIAL_H
    1. include <plasma/popupapplet.h>
    2. include <plasma/dataengine.h>

    namespace Plasma {

       class ExtenderItem;
    

    } // namespace Plasma

    class ExtenderTutorial : public Plasma::PopupApplet {

       Q_OBJECT
       public:
           ExtenderTutorial(QObject *parent, const QVariantList &args);
           ~ExtenderTutorial();
    
           void init();
    
       protected:
           //We implement graphicsWidget to let PopupApplet know we want to use this applet's extender
           //as popup.
           QGraphicsWidget *graphicsWidget();
    
           //Implement this function to make ExtenderItems persistent. This function will get called on
           //plasma start for every ExtenderItem that belonged to this applet, and is still around.
           //Instantiate the widget to be wrapped in the ExtenderItem here.
           void initExtenderItem(Plasma::ExtenderItem *item);
    
       public slots:
           //We want to add a new ExtenderItem everytime a new job is started.
           void sourceAdded(const QString &source);
    

    };

    K_EXPORT_PLASMA_APPLET(extendertutorial, ExtenderTutorial)

    1. endif

    This header should be mostly self explanetory. We use PopupApplet to have a nice basis for this kind of applet, which requires us to implement graphicsWidget().

    The implementation

    1. include "extendertutorial.h"
    1. include <QAction>
    2. include <QGraphicsLinearLayout>
    1. include <Plasma/Containment>
    2. include <Plasma/DataEngine>
    3. include <Plasma/Extender>
    4. include <Plasma/ExtenderItem>
    5. include <Plasma/Meter>

    ExtenderTutorial::ExtenderTutorial(QObject *parent, const QVariantList &args)

       : Plasma::PopupApplet(parent, args)
    

    { }

    ExtenderTutorial::~ExtenderTutorial() { }

    void ExtenderTutorial::init() {

       //An init() function like this is usefull for most applets that use extenders.
    
       //Always instatiate an extender in init, and not earlier, since extenders need to access
       //configuration, and a corona is needed for that.
       new Plasma::Extender(this);
       //The message to be shown when there are no ExtenderItems in this extender.
       extender()->setEmptyExtenderMessage(i18n("no running jobs..."));
       //A sane size policy: a fixed verticle size feels natural for vertical lists.
       extender()->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
    
       //Create a layout, and add the extender to it.
       QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(this);
       setLayout(layout);
       layout->addItem(extender());
    
       //Notify ourself whenever a new job is created.
       connect(dataEngine("kuiserver"), SIGNAL(sourceAdded(const QString &)),
               this, SLOT(sourceAdded(const QString &)));
    

    }

    QGraphicsWidget *ExtenderTutorial::graphicsWidget() {

       //We want to have the Extender as 'popup widget'
       return extender();
    

    }

    void ExtenderTutorial::initExtenderItem(Plasma::ExtenderItem *item) {

       //Create a Meter widget and wrap it in the ExtenderItem
       Plasma::Meter *meter = new Plasma::Meter(item);
    
       meter->setMeterType(Plasma::Meter::BarMeterHorizontal);
       meter->setSvg("extendertutorial/progress-bar");
       meter->setMaximum(100);
       meter->setValue(0);
    
       meter->setMinimumSize(QSizeF(250, 45));
       meter->setPreferredSize(QSizeF(250, 45));
       meter->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
    
       item->setWidget(meter);
    
       //Load the title of the ExtenderItem from it's config and set it.
       item->setTitle(item->config().readEntry("title", ""));
    
       //Job names are not unique across plasma restarts (kuiserver engine just starts with Job1
       //again), so avoid problems and just don't give reinstantiated items a name.
       item->setName("");
    
       //Add a 'remove' action.
       QAction *removeAction = new QAction(item);
       removeAction->setIcon(KIcon("edit-delete"));
       removeAction->setEnabled(true);
       removeAction->setVisible(true);
       item->addAction("remove", removeAction);
       connect(removeAction, SIGNAL(triggered()), item, SLOT(destroy()));
    

    }

    void ExtenderTutorial::sourceAdded(const QString &source) {

       //Add a new ExtenderItem
       Plasma::ExtenderItem *item = new Plasma::ExtenderItem(extender());
       initExtenderItem(item);
    
       //We give this item a name, which we don't use in this example, but allows us to look up
       //extenderItems by calling extenderItem(name). That function is useful to avoid duplicating
       //detached ExtenderItems between session, because you can check if a certain item allready
       //exists.
       item->setName(source);
    
       //And we give this item a title. It's stupid, but it shows how to make your items persistent
       //between sessions.
       item->setTitle(source);
       item->config().writeEntry("title", source);
    
       //Connect a dataengine. If this applet would display data where datasources would have unique
       //names, even between sessions, you would want to do this in initExtenderItem, so that after a
       //plasma restart, datasources would still get connected to the appropriate sources. Kuiserver
       //jobs are not persistent however, so we connect them here.
       dataEngine("kuiserver")->connectSource(source, item->widget(), 200);
    
       //Show the popup for 5 seconds if in panel, so the user notices that there's a new job running.
       showPopup(5000);
    

    }

    1. include "extendertutorial.moc"

    This piece of code is well documented so I won't explain this any further.

    The rest

    Finally you'll need a cmakelist, a desktop file, and a svg for the meter widget.

    The .desktop file: [Desktop Entry] Name=Extender Tutorial Type=Service X-KDE-ServiceTypes=Plasma/Applet

    X-KDE-Library=plasma_applet_extendertutorial X-KDE-PluginInfo-Author=The Plasma Team [email protected] X-KDE-PluginInfo-Name=extendertutorial X-KDE-PluginInfo-Version=pre0.1 X-KDE-PluginInfo-Website=http://plasma.kde.org/ X-KDE-PluginInfo-Category= X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true

    The CMakeList.txt: set(extendertutorial_SRCS

       extendertutorial.cpp)
    

    kde4_add_plugin(plasma_applet_extendertutorial ${extendertutorial_SRCS}) target_link_libraries(plasma_applet_extendertutorial plasma ${KDE4_KIO_LIBS})

    install(TARGETS plasma_applet_extendertutorial DESTINATION ${PLUGIN_INSTALL_DIR}) install(FILES plasma-applet-extendertutorial.desktop DESTINATION ${SERVICES_INSTALL_DIR}) install(FILES progress-bar.svg DESTINATION ${DATA_INSTALL_DIR}/desktoptheme/default/extendertutorial/)

    You can download a Meter SVG here:

    So there it is, all it takes to make a simple applet that uses an extender. For a more complete example I'd like to point you at the kuiserver applet which is in playground right now. And I'd encourage you to read to api documentation of Extender and ExtenderItem. I'm looking forward to see your extender using applet, be creative! :)