Development/Tutorials/Plasma4/UsingExtenders: Difference between revisions
No edit summary |
m (D ed moved page Development/Tutorials/Plasma/UsingExtenders to Development/Tutorials/Plasma4/UsingExtenders) |
||
(17 intermediate revisions by 5 users not shown) | |||
Line 1: | Line 1: | ||
==Abstract== | ==Abstract== | ||
This tutorial needs KDE 4.2 | This tutorial needs KDE 4.2 to build. | ||
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. | 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. Stripped down to the point where it isn't very useful anymore, but that allows us to focus on extenders. | ||
==About extenders== | ==About extenders== | ||
Extenders allow applets to, with only very little extra lines of code, use relocatable widgets. These {{class| | Extenders allow applets to, with only very little extra lines of code, use relocatable widgets. These {{class|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 {{class|Plasma::Extender}}. | The class that shows and manages these extender items is {{class|Plasma::Extender}}. | ||
Line 13: | Line 11: | ||
If you want to use extenders in your applet you'll usually have to do a couple of things: | If you want to use extenders in your applet you'll usually have to do a couple of things: | ||
===Instatiate an {{class|Extender}}=== | |||
This is quite easy: calling extender() from your applet will instantiate an extender for you if your applet doesn't have an extender yet. Note that an applet cannot have more then one extender. Also note that you should always instantiate an extender in your applet's init() function. An extender requires a scene, which isn't available before the call to init(). | |||
===Add {{class|ExtenderItem}} objects to this Extender=== | |||
Just instantiating an ExtenderItem with your extender as parameter to the constructor will add a new ExtenderItem to this extender. In this tutorial we will add an ExtenderItem whenever a new kuiserver job is created. After instantiating an ExtenderItem you'll usually want to set a name, title, icon, and probably even other information which can be stored by accessing the item's config(). Once you've got an ExtenderItem set up, you'll need to call initExtenderItem(ExtenderItem*) to actually set the QGraphicsWidget that will be wrapped inside this ExtenderItem. | |||
===Implement a initExtenderItem(ExtenderItem*) function in your applet=== | |||
This function will be used to recreate detached ExtenderItems after plasma has been restarted. It will not only be called by your applet after instantiating a new ExtenderItem, it will also be called whenever plasma is started and detached ExtenderItems are restored from configuration. In this function you'll need to extract the required information from the item (name, or other information you have stored in the item's config()), and use this information to create the desired widget to be wrapped inside this item. Then call setWidget on this item to actually wrap this widget inside the ExtenderItem. | |||
==The code== | ==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 | 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 the icon of the application that created the job will be shown. | ||
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. | 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. | ||
As basis for this applet we will use {{class|PopupApplet}. This means that the applet will automatically collapse into an icon when put into a panel. PopupApplet will also take care of setting the correct appearence on your extender, depending of where it's located (top of screen, bottom of screen, desktop). PopupApplet is a convenient base class for all applets that use extenders and I'd strongly advice you to use it. | |||
===The header=== | ===The header=== | ||
< | <syntaxhighlight lang="cpp-qt"> | ||
#ifndef EXTENDERTUTORIAL_H | #ifndef EXTENDERTUTORIAL_H | ||
#define EXTENDERTUTORIAL_H | #define EXTENDERTUTORIAL_H | ||
Line 47: | Line 48: | ||
protected: | 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 | |||
//Implement this function to make ExtenderItems persistent. This function will get called on | //ExtenderItem here. | ||
void initExtenderItem(Plasma::ExtenderItem *item); | void initExtenderItem(Plasma::ExtenderItem *item); | ||
public slots: | public slots: | ||
//We want to add a new ExtenderItem everytime a new job is started. | //We want to add a new ExtenderItem everytime a new job | ||
//is started. | |||
void sourceAdded(const QString &source); | void sourceAdded(const QString &source); | ||
}; | }; | ||
Line 64: | Line 64: | ||
#endif | #endif | ||
</ | </syntaxhighlight> | ||
This header should be mostly self explanetory. We use PopupApplet to have a nice basis for this kind of applet, | 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=== | ===The implementation=== | ||
< | <syntaxhighlight lang="cpp-qt"> | ||
#include "extendertutorial.h" | #include "extendertutorial.h" | ||
Line 81: | Line 81: | ||
#include <Plasma/Meter> | #include <Plasma/Meter> | ||
ExtenderTutorial::ExtenderTutorial(QObject *parent, const QVariantList &args) | ExtenderTutorial::ExtenderTutorial(QObject *parent, | ||
const QVariantList &args) | |||
: Plasma::PopupApplet(parent, args) | : Plasma::PopupApplet(parent, args) | ||
{ | { | ||
//We want to collapse into an icon when put into a panel. | |||
//If you don't call this function, you can display another | |||
//widget, or draw something yourself. | |||
setPopupIcon("extendertutorial"); | |||
} | } | ||
Line 92: | Line 97: | ||
void ExtenderTutorial::init() | void ExtenderTutorial::init() | ||
{ | { | ||
// | //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. | |||
//The message to be shown when there are no ExtenderItems in this extender. | |||
extender()->setEmptyExtenderMessage(i18n("no running jobs...")); | extender()->setEmptyExtenderMessage(i18n("no running jobs...")); | ||
//Notify ourself whenever a new job is created. | //Notify ourself whenever a new job is created. | ||
connect(dataEngine("kuiserver"), SIGNAL(sourceAdded(const QString &)), | connect(dataEngine("kuiserver"), | ||
SIGNAL(sourceAdded(const QString &)), | |||
this, SLOT(sourceAdded(const QString &))); | this, SLOT(sourceAdded(const QString &))); | ||
} | } | ||
Line 128: | Line 118: | ||
meter->setMeterType(Plasma::Meter::BarMeterHorizontal); | meter->setMeterType(Plasma::Meter::BarMeterHorizontal); | ||
meter->setSvg(" | meter->setSvg("widgets/bar_meter_horizontal"); | ||
meter->setMaximum(100); | meter->setMaximum(100); | ||
meter->setValue(0); | meter->setValue(0); | ||
Line 134: | Line 124: | ||
meter->setMinimumSize(QSizeF(250, 45)); | meter->setMinimumSize(QSizeF(250, 45)); | ||
meter->setPreferredSize(QSizeF(250, 45)); | meter->setPreferredSize(QSizeF(250, 45)); | ||
//often, you'll want to connect dataengines or set properties | |||
//depending on information contained in item->config(). | |||
//In this situation that won't be necessary though. | |||
item->setWidget(meter); | item->setWidget(meter); | ||
//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. | |||
//Job names are not unique across plasma restarts (kuiserver engine just starts with Job1 | |||
item->setName(""); | item->setName(""); | ||
// | //Show a close button. | ||
item->showCloseButton(); | |||
} | } | ||
Line 157: | Line 142: | ||
{ | { | ||
//Add a new ExtenderItem | //Add a new ExtenderItem | ||
Plasma::ExtenderItem *item = new Plasma::ExtenderItem(extender()); | Plasma::ExtenderItem *item = | ||
new Plasma::ExtenderItem(extender()); | |||
initExtenderItem(item); | initExtenderItem(item); | ||
//We give this item a name, which we don't use in this example, but allows us to look up | //We give this item a name, which we don't use in this | ||
// | //example, but allows us to look up extenderItems by calling | ||
//detached ExtenderItems between session, because you can check if a certain item | //extenderItem(name). That function is useful to avoid | ||
//duplicating detached ExtenderItems between session, because | |||
//you can check if a certain item already exists. | |||
item->setName(source); | item->setName(source); | ||
//And we give this item a title. | //And we give this item a title. Titles, along with icons and | ||
//between sessions. | //names are persistent between sessions. | ||
item->setTitle(source); | item->setTitle(source); | ||
//Connect a dataengine. If this applet would display data where datasources would have unique | //Connect a dataengine. If this applet would display data where | ||
//datasources would have unique names, even between sessions, | |||
// | //you should do this in initExtenderItem, so that after a plasma | ||
//restart, datasources would still get connected to the | |||
dataEngine("kuiserver")->connectSource(source, item->widget(), 200); | //appropriate sources. Kuiserver jobs are not persistent however, | ||
//so we connect them here. | |||
dataEngine("kuiserver")->connectSource(source, | |||
dynamic_cast<QObject*>(item->widget()), 200); | |||
//Show the popup for 5 seconds if in panel, so the user notices that there's a new job running. | //Show the popup for 5 seconds if in panel, so the user notices | ||
//that there's a new job running. | |||
showPopup(5000); | showPopup(5000); | ||
} | } | ||
#include "extendertutorial.moc" | #include "extendertutorial.moc" | ||
</ | </syntaxhighlight> | ||
This piece of code is well documented so I won't explain this any further. | This piece of code is well documented so I won't explain this any further. | ||
==The rest== | ==The rest== | ||
Finally you'll need a cmakelist | Finally you'll need a cmakelist and a .desktop file. | ||
The .desktop file: | |||
<syntaxhighlight lang="ini"> | |||
[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 | |||
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 | |||
</syntaxhighlight> | |||
The CMakeList.txt: | |||
<syntaxhighlight lang="bash"> | |||
set(extendertutorial_SRCS | |||
extendertutorial.cpp) | |||
kde4_add_plugin(plasma_applet_extendertutorial ${extendertutorial_SRCS}) | |||
target_link_libraries(plasma_applet_extendertutorial ${KDE4_PLASMA_LIBS} ${KDE4_KIO_LIBS} ${KDE4_KDEUI_LIBS}) | |||
install(TARGETS plasma_applet_extendertutorial DESTINATION ${PLUGIN_INSTALL_DIR}) | |||
install(FILES plasma-applet-extendertutorial.desktop DESTINATION ${SERVICES_INSTALL_DIR}) | |||
</syntaxhighlight> | |||
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 {{class|Extender}} and {{class|ExtenderItem}}. I'm looking forward to see your extender | 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 {{class|Extender}} and {{class|ExtenderItem}}. Especially ExtenderItem has a lot of features that are not used in this example but can be really useful like being able to set a timeout and add custom actions to the item, that are show in the item's drag handle. | ||
I'm looking forward to see your applet using extender, be creative! :) |
Latest revision as of 23:27, 11 September 2014
Abstract
This tutorial needs KDE 4.2 to build.
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. Stripped down to the point where it isn't very useful anymore, but that allows us to focus on extenders.
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
This is quite easy: calling extender() from your applet will instantiate an extender for you if your applet doesn't have an extender yet. Note that an applet cannot have more then one extender. Also note that you should always instantiate an extender in your applet's init() function. An extender requires a scene, which isn't available before the call to init().
Add ExtenderItem objects to this Extender
Just instantiating an ExtenderItem with your extender as parameter to the constructor will add a new ExtenderItem to this extender. In this tutorial we will add an ExtenderItem whenever a new kuiserver job is created. After instantiating an ExtenderItem you'll usually want to set a name, title, icon, and probably even other information which can be stored by accessing the item's config(). Once you've got an ExtenderItem set up, you'll need to call initExtenderItem(ExtenderItem*) to actually set the QGraphicsWidget that will be wrapped inside this ExtenderItem.
Implement a initExtenderItem(ExtenderItem*) function in your applet
This function will be used to recreate detached ExtenderItems after plasma has been restarted. It will not only be called by your applet after instantiating a new ExtenderItem, it will also be called whenever plasma is started and detached ExtenderItems are restored from configuration. In this function you'll need to extract the required information from the item (name, or other information you have stored in the item's config()), and use this information to create the desired widget to be wrapped inside this item. Then call setWidget on this item to actually wrap this widget inside the ExtenderItem.
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 the icon of the application that created the job will be shown. 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. As basis for this applet we will use {{class|PopupApplet}. This means that the applet will automatically collapse into an icon when put into a panel. PopupApplet will also take care of setting the correct appearence on your extender, depending of where it's located (top of screen, bottom of screen, desktop). PopupApplet is a convenient base class for all applets that use extenders and I'd strongly advice you to use it.
The header
#ifndef EXTENDERTUTORIAL_H
#define EXTENDERTUTORIAL_H
#include <plasma/popupapplet.h>
#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)
#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
#include "extendertutorial.h"
#include <QAction>
#include <QGraphicsLinearLayout>
#include <Plasma/Containment>
#include <Plasma/DataEngine>
#include <Plasma/Extender>
#include <Plasma/ExtenderItem>
#include <Plasma/Meter>
ExtenderTutorial::ExtenderTutorial(QObject *parent,
const QVariantList &args)
: Plasma::PopupApplet(parent, args)
{
//We want to collapse into an icon when put into a panel.
//If you don't call this function, you can display another
//widget, or draw something yourself.
setPopupIcon("extendertutorial");
}
ExtenderTutorial::~ExtenderTutorial()
{
}
void ExtenderTutorial::init()
{
//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..."));
//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("widgets/bar_meter_horizontal");
meter->setMaximum(100);
meter->setValue(0);
meter->setMinimumSize(QSizeF(250, 45));
meter->setPreferredSize(QSizeF(250, 45));
//often, you'll want to connect dataengines or set properties
//depending on information contained in item->config().
//In this situation that won't be necessary though.
item->setWidget(meter);
//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("");
//Show a close button.
item->showCloseButton();
}
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 already exists.
item->setName(source);
//And we give this item a title. Titles, along with icons and
//names are persistent between sessions.
item->setTitle(source);
//Connect a dataengine. If this applet would display data where
//datasources would have unique names, even between sessions,
//you should 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,
dynamic_cast<QObject*>(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);
}
#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 and a .desktop file.
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
X-KDE-PluginInfo-Email=[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 ${KDE4_PLASMA_LIBS} ${KDE4_KIO_LIBS} ${KDE4_KDEUI_LIBS})
install(TARGETS plasma_applet_extendertutorial DESTINATION ${PLUGIN_INSTALL_DIR})
install(FILES plasma-applet-extendertutorial.desktop DESTINATION ${SERVICES_INSTALL_DIR})
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. Especially ExtenderItem has a lot of features that are not used in this example but can be really useful like being able to set a timeout and add custom actions to the item, that are show in the item's drag handle. I'm looking forward to see your applet using extender, be creative! :)