Konqueror • Technical • Embedded tutorial • Part 5

Embedded Components Tutorial - Page 5

Making the Component Functional

The Plan

You're on the home stretch now. You have a fully working embedded component -- you just need to make it do something!

This step is one of the shortest ones in this tutorial but will likely be the hardest in real use. This is where you need to connect your applications functionality to the KParts interface.

In the case of aKtion, nearly all of the actual "playing" code is encapsulated in the KXAnim class. This made it very easy to use in a KParts interface. All that was needed was to replace the QLabel example with KXAnim and everything worked (mostly).

The Files

 aktion_part.h
#ifndef __aktion_part_h__
#define __aktion_part_h__

#include <kparts/browserextension.h>
#include <klibloader.h>

class KInstance;
class KXAnim;
class AktionBrowserExtension;

class AktionFactory : public KLibFactory
{
    Q_OBJECT
public:
    AktionFactory();
    virtual ~AktionFactory();

    virtual QObject* create(QObject* parent = 0, const char* name = 0,
                            const char* classname = "QObject",
                            const QStringList &args = QStringList());

    static KInstance *instance();

private:
    static KInstance *s_instance;
};

class AktionPart: public KParts::ReadOnlyPart
{
    Q_OBJECT
public:
    AktionPart(QWidget *parent, const char *name);
    virtual ~AktionPart();

    virtual bool closeURL();

protected:
    virtual bool openFile();

protected slots:
    void slotPlay();
    void slotStop();

private:
    KXAnim *widget;
};

#endif
 aktion_part.cpp
#include "aktion_part.h"

#include <kinstance.h>
#include <klocale.h>
#include <kaboutdata.h>

#include "kxanim.h"
#include <qtimer.h>

extern "C"
{
    /**
     * This function is the 'main' function of this part.  It takes
     * the form 'void *init_lib<library name>()'.  It always returns a
     * new factory object
     */
    void *init_libaktion()
    {
        return new AktionFactory;
    }
};

/**
 * We need one static instance of the factory for our C 'main'
 * function
 */
KInstance *AktionFactory::s_instance = 0L;

AktionFactory::AktionFactory()
{
}

AktionFactory::~AktionFactory()
{
    if (s_instance)
        delete s_instance;

    s_instance = 0;
}

QObject *AktionFactory::create(QObject *parent, const char *name, const char*,
                               const QStringList& )
{
    QObject *obj = new AktionPart((QWidget*)parent, name);
    emit objectCreated(obj);
    return obj;
}

KInstance *AktionFactory::instance()
{
    if ( !s_instance )
    {
        KAboutData about("aktion", I18N_NOOP("aKtion"), "1.99");
        s_instance = new KInstance(&about);
    }
    return s_instance;
}

AktionPart::AktionPart(QWidget *parent, const char *name)
    : KParts::ReadOnlyPart(parent, name)
{
    setInstance(AktionFactory::instance());

    // create a canvas to insert our widget
    QWidget *canvas = new QWidget(parent);
    canvas->setFocusPolicy(QWidget::ClickFocus);
    setWidget(canvas);

    // create our animation widget
    widget = new KXAnim(canvas);
    widget->setLoop(true);
    widget->show();
}

AktionPart::~AktionPart()
{
    slotStop();
}

bool AktionPart::openFile()
{
    widget->setFile(m_file);
    widget->stop();
    widget->show();
    QTimer::singleShot(2000, this, SLOT(slotPlay()));

    return true;
}

bool AktionPart::closeURL()
{
    slotStop();
    return true;
}

void AktionPart::slotPlay()
{
    widget->play();
}

void AktionPart::slotStop()
{
    widget->stop();
}

Line by Line

There isn't much non-aKtion specific stuff to show in this step. The two major chunks of code are in the constructor and in the openFile method.

// create our animation widget
widget = new KXAnim(canvas);
widget->setLoop(true);
widget->show();
This creates our animation widget and sets some default parameters.

widget->setFile(m_file);
widget->stop();
widget->show();
QTimer::singleShot(2000, this, SLOT(slotPlay()));

And this actually plays the file. Some hackery needed to be done to get it to play due to some timing problems with KXAnim. And that's pretty much it!

Visible Result

Step 3

Practical Matters

As said earlier, this is one of the shorted and simplest steps in this example.. but could easily be the hardest in your own application. It all depends on how much functionality is encapsulated in your "main" view object.

The key to this step is to have one view object already in your application in which all processing of the file takes place. If you have several classes that all depend on each other with no central "viewing" object, then you will have problems.

As a case study, KGhostView was one of the hardest programs to port to the KParts system. The reason for this was that all of the UI elements were scattered all over the application. There was no one coherent interface to the UI aspects -- essentially no way for a third party app to "view" the same thing that KGhostView was.

The solution to this was to create a view class that combined most of the required UI elements in one place. After this was done, it was trivial to plug this view into the KParts system.

Things To Remember:
The ease of integrating your application into the KParts architecture depends almost entirely on how encapsulated your UI elements are into one coherent interface. The less coherent, the more problems you will have.

[ Edit ]

Global navigation links