A tutorial for integrating KROSS in your own application

While you can integrate KROSS in non-kpartified applications, it's easier to do with the kpart system, so this tutorial will consider that you have a kpart application. I will start from the kdeveloper's template "Application framework (KParts)". Or at least with a modified version of this template, I have moved the kpart library in a separate directory. If you prefer to have all your sources file in the same directory, it is allways possible.

The content of the Application framework (KParts)

In the target krossexample, you have the main application:

In the target libkrossexamplepart.la, you have the code source of the kpart with a simple text editor:

But we don't need to touch to this files to add scripting/kross support, we will use a kpart plugin.

Launching scripts from the apps

The plugin

You first need to create a new kpart plugins, meaning a new target, I tend to preffer to have them in subdirectory, but there it is not mandatory.

As I don't know how to do the following with kdevelop, you will have to use your your favorite editor to add this to the Makefile.am and to create the files :
kde_services_DATA = krossexamplescripting.desktop krossexamplercdir = $(kde_datadir)/krossexamplepart/kpartplugins krossexamplerc_DATA = krossexamplescripting.rc

Now you will need to add the plugin file.

scriptingplugin.h

Below, you will find the code for the scriptingplugin.h :
#ifndef SCRIPTINGPLUGIN_H
#define SCRIPTINGPLUGIN_H

#include <kparts/plugin.h>

namespace Kross {
    namespace Api {
        class ScriptGUIClient;
        class ScriptAction;
    }
}

class KrossExamplePart;

class ScriptingPlugin : public KParts::Plugin
{
    Q_OBJECT
    public:
        ScriptingPlugin(QObject *parent, const char *name, const QStringList &);
        virtual ~ScriptingPlugin();
    private:
        KrossExamplePart* m_part;
        Kross::Api::ScriptGUIClient* m_scriptguiclient;
};

#endif

scriptingplugin.cc

#include "scriptingplugin.h"

#include <qapplication.h>

#include <kdebug.h>
#include <kgenericfactory.h>
#include <kinstance.h>
#include <kstandarddirs.h>

#define KROSS_MAIN_EXPORT KDE_EXPORT
#include <main/manager.h>
#include <main/scriptguiclient.h>

#include "krossexample_part.h"

typedef KGenericFactory KrossExampleScriptingFactory;
K_EXPORT_COMPONENT_FACTORY( libkrossexamplescripting, KrossExampleScriptingFactory( "krossexample" ) )
ScriptingPlugin::ScriptingPlugin(QObject *parent, const char *name, const QStringList &)
    : KParts::Plugin(parent, name)
{
    setInstance(KrossExampleScriptingFactory::instance());
    kdDebug() << "loading scripting plugin"<< endl;
    
    if ( parent->inherits("KrossExamplePart") )
    {
        setInstance(ScriptingPlugin::instance());
        m_part = (KrossExamplePart*) parent;
        // Initialize the scripting support
        setXMLFile(locate("data","krossexample/kpartplugins/krossexamplescripting.rc"), true);
        m_scriptguiclient = new Kross::Api::ScriptGUIClient( this, m_part->widget() );
        // Add menu entries
        new KAction(i18n("Execute Script File..."), 0, 0, m_scriptguiclient, SLOT(executeScriptFile()), actionCollection(), "executescriptfile");
        new KAction(i18n("Script Manager..."), 0, 0, m_scriptguiclient, SLOT(showScriptManager()), actionCollection(), "configurescripts");
        // Export the part to the scriptManager as an object usuable in your apps
        Kross::Api::Manager::scriptManager()->addQObject(m_part, "KrossExamplePart");
    }
}

ScriptingPlugin::~ScriptingPlugin()
{
}

krossexamplescripting.rc

<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
<kpartgui library="libkrossexamplescripting" version="1">
    <MenuBar>
        <Menu name="Scripts"><text>S&cripts</text>
            <Separator/>
            <Action name="executescriptfile"/>
            <Action name="configurescripts"/>
        </Menu>
    </MenuBar>
</kpartgui>

krossexamplescripting.desktop

[Desktop Entry]
Encoding=UTF-8
Name=Scripting plugin
# ServiceTypes=Krita/ViewPlugin
Type=Service
X-KDE-Library=libkrossexamplescripting
X-KDE-Version=1

And now, you should see a scripts menu in your apps, and you should be able to launch scripts from there !

Bindings

And the final parts of the tutorial is how to add bindings to the internal of your application. You will need a special library that script writer will have to load in their scripts.

In scriptingmodule.*, we will add a class that is initialized when the module is called from a script, that's where we bind the object exported in ScriptingPlugin::ScriptingPlugin.

In partbinding.*, we will add a class with the binding between our part and functions that might be called from the scripts.

scriptingmodule.h

#ifndef SCRIPTINGMODULE_H
#define SCRIPTINGMODULE_H

#include <api/module.h>

namespace Kross { namespace Api {
    class Manager;
}}

class ScriptingModule : public Kross::Api::Module
{
    public:
        ScriptingModule(Kross::Api::Manager* manager);
        virtual ~ScriptingModule();
    private:
        Kross::Api::Manager* m_manager;
};

#endif

scriptingmodule.cc

#include "scriptingmodule.h"

#include <api/qtobject.h>
#include <main/manager.h>

#include "krossexample_part.h"
#include "partbinding.h"

extern "C"
{
    /**
     * Exported an loadable function as entry point to use
     * the module.
     */
    Kross::Api::Object* init_module(Kross::Api::Manager* manager)
    {
        return new ScriptingModule(manager);
    }
}

ScriptingModule::ScriptingModule(Kross::Api::Manager* manager)
    : Kross::Api::Module("ScriptingModule") , m_manager(manager)
{
    // Wrap doc
    Kross::Api::Object::Ptr krossexamplepart = ((Kross::Api::Object*)manager)->getChild("KrossExamplePart");
    bool examplePartFound = false;
    if(krossexamplepart) {
        Kross::Api::QtObject* krossexamplepartqt = dynamic_cast( krossexamplepart.data() );
        if(krossexamplepartqt) {
            KrossExamplePart* part = dynamic_cast( krossexamplepartqt->getObject() );
            if(part) {
                addChild( new PartBinding(part) );
                examplePartFound = true;
            }
        }
    }
    if(!examplePartFound){
        throw Kross::Api::Exception::Ptr( new Kross::Api::Exception("There was no 'KrossExamplePart' published.") );
    }
}

ScriptingModule::~ScriptingModule()
{
}

partbinding.h

#ifndef _PART_BINDING_H_
#define _PART_BINDING_H_

class KrossExamplePart;

#include <api/class.h>

class PartBinding : public Kross::Api::Class<PartBinding>
{
    public:
        explicit PartBinding(::KrossExamplePart* part);
        virtual ~PartBinding();
        virtual const QString getClassName() const;
    private:
        Kross::Api::Object::Ptr getText(Kross::Api::List::Ptr);
        Kross::Api::Object::Ptr setText(Kross::Api::List::Ptr);
    private:
        KrossExamplePart* m_part;

};

#endif

partbinding.cc

#include "partbinding.h"

#include <qmultilineedit.h>

#include "krossexample_part.h"

PartBinding::PartBinding(::KrossExamplePart* part) : Kross::Api::Class("PartBinding", 0 ), m_part(part) {
     addFunction("getText", &PartBinding::getText);
     addFunction("setText", &PartBinding::setText);
}

PartBinding::~PartBinding() {
}

const QString PartBinding::getClassName() const {
    return "PartBinding";
}

Kross::Api::Object::Ptr PartBinding::getText(Kross::Api::List::Ptr)
{
    return new Kross::Api::Variant( m_part->editor()->text() );
}

Kross::Api::Object::Ptr PartBinding::setText(Kross::Api::List::Ptr args)
{
    QString text = Kross::Api::Variant::toString(args->item(0));
    m_part->editor()->setText(text);
    return 0;
}

Alternative for partbinding.h

An alternate way to create bindings is to connect direct with any kind of C++ method.
#ifndef _PART_BINDING_H_
#define _PART_BINDING_H_

class KrossExamplePart;

#include <api/class.h>

class AnotherPartBinding : public Kross::Api::Class
{
    public:
        explicit AnotherPartBinding(::KrossExamplePart* part) {
            // This class provides a method named "getText" which returns a QString.
            this->addProxyFunction<
                Kross::Api::ProxyValue<Kross::Api::Variant,QString>// returnvalue
                >(
                "getText", // the name the method will be published as.
                part, // the class-instance which has the method.
                 &::KrossExamplePart::text // Pointer to the method in the class. "QString text()" in this case.
            );
            // This class also provides a "setText" method.
            this->addProxyFunction<
                Kross::Api::ProxyValue<Kross::Api::Object,void>, // returnvalue
                Kross::Api::ProxyValue<Kross::Api::Variant,QString> // parameter
                > (
                "setText", // the name the method will be published as.
                part, // the class-instance which has the method.
                &::KrossExamplePart::setText // Pointer to the method in the class. "void setText(QString)" in this case.
            );
        }
        virtual ~AnotherPartBinding();
        virtual const QString getClassName() const { return "AnotherPartBinding"; }
    private:
        KrossExamplePart* m_part;
};
#endif

An example of script

Now we are done with integrating KROSS in our application.

In ruby

require "krossexamplesbinding" part = Krossexamplesbinding::get("PartBinding") part.setText("Hello World!");