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:
- krossexample.cpp : opening, reading functions
- main.cpp : the main function
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.
- in kdevelop :
- choose the automake manager
- create a new subproject (call it scripting for instance)
- add a target (Libtool library, kde_module, call it libkrossexamplescriping.la for instance, and check "Create a library that can be dynamically loaded (-module)")
- then in the option of the target select to link with your kpart and in the external libraries section: -lkrossmain -lkrossapi
- and it the option of the subproject, you need to add -I/usr/include/kross, in the include tab
- with your favorite editor :
- create a directory with konqueror or konsole or anything else (call it scripting for instance)
- create a Makefile.am in your favorite editor with the following line :
INCLUDES = -I$(top_srcdir)/src/part -I/usr/include/kross $(all_includes)
METASOURCES = AUTO
kde_module_LTLIBRARIES = libkrossexamplescripting.la
libkrossexamplescripting_la_LIBADD = $(top_builddir)/src/part/libkrossexamplepart.la -lkrossmain -lkrossapi
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 kdevelop :
- choose the automake manager
- add a target (Libtool library, kde_module, call it krossexamplesbinding.la for instance, note that the name of your library should start by kross, and check "Create a library that can be dynamically loaded (-module)")
- then in the option of the target: you need to add $(KDE_PLUGIN) in the filed other which allready have $(all_libraries), and then you need to select to link with your kpart and in the external libraries section: -lkrossmain -lkrossapi
- unfortunately kdevelop force the name of your library to start by lib, but it must start by kross, so you will need to edit the Makefile.am by hand (do it after adding the file) and you will also need to add KDE_CXXFLAGS = $(USE_EXCEPTIONS)
- with your favorite editor :
- add to the Makefile.am in your favorite editor the following line :
kde_module_LTLIBRARIES = libkrossexamplescriping.la krossexamplesbinding.la
krossexamplesbinding_la_LIBADD = $(top_builddir)/src/libkrossexamplepart.la -lkrossmain -lkrossapi
krossexamplesbinding_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module
KDE_CXXFLAGS = $(USE_EXCEPTIONS)
Note that you should allready have the first line, and therefore only needs to add krossexamplesbinding.la to it
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!");