Development/Tutorials/Plasma4/UsingExtenders

From KDE TechBase
Revision as of 16:04, 21 September 2008 by Pinda (talk | contribs)

Under Construction

Abstract

This tutorial needs KDE 4.2 (trunk) to build. You'll also need to build the kuiserver dataengine (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:
       //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. The default implementation of PopupApplet's graphicsWidget() returns it's extender if it has one. This is exactly what we want (display the extender in the popup), so we don't need 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.
   //Calling extender() instantiates an extender for you if you haven't already
   //done so. Never instantiate an extender before init() since Extender needs
   //access to applet->config() to work.
   //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);
   //Notify ourself whenever a new job is created.
   connect(dataEngine("kuiserver"), SIGNAL(sourceAdded(const QString &)),
           this, SLOT(sourceAdded(const QString &)));

}

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! :)