Development/Tutorials/Kross/Call Functions in Kross: Difference between revisions

From KDE TechBase
(New Kross Tutorial)
 
(Mark for updating)
 
(22 intermediate revisions by 3 users not shown)
Line 1: Line 1:
This tutorial shows how to deal with functions provided by a script. It builds upon the [[Development/Tutorials/Kross/Hello World|Kross Hello World]] tutorial and extends that tutorial where needed.
{{Review|Port to KF5}}


== The source files ==
This tutorial shows how to deal with functions provided by a script. This tutorial is based on the [[Development/Tutorials/Kross/Hello_World|Hello World]] tutorial and extends the codebase we wrote there with new functionality to outline how calling scripting functions could be done.


=== CMakeList.txt ===
Please note, that it is recommed to use [[Development/Tutorials/Kross/Connecting_Signals_and_slots_in_Kross#Autoconnecting_Signals_and_Slots| Autoconnected Signals and Slots]] if you just like to have a bunch of scripting functions within a script that should be called on demand. That way you have a clear interface with signals that are mapped transparent to scripting functions. The "trick" here is, that Kross will automaticly connect each signal a QObject has to a matching scripting function and at the C++ side all what is
needed is to emit a signal and behind the scene the arguments will be
translated and a possible defined scripting function got called. A good example here is [[Development/Tutorials/SuperKaramba|SuperKaramba]] which uses the signal+slot rather then callFunction() way. All it defines is the rather huge class that inherits QObject ([http://websvn.kde.org/trunk/KDE/kdeutils/superkaramba/src/karambainterface.h?view=markup karambainterface.h]). The signals within that class are transparent mapped to scripting functions (e.g. [http://websvn.kde.org/trunk/KDE/kdeutils/superkaramba/examples/template.py?view=markup| template.py] and [http://websvn.kde.org/trunk/KDE/kdeutils/superkaramba/examples/template.js?view=markup| template.js]) while the slots are then callable from within the scripting code.
Signals and slots are faster and more type-safe then the in this tutorial used callFunction() (see also the [http://api.kde.org/4.x-api/kdelibs-apidocs/kross/html/classKross_1_1Action.html Kross::Action] class).


== The C++ Code ==
The following code is a KDE C++ project to demonstrate how to deal with scripting functions.
=== CMakeLists.txt ===
We are using cmake to build our small sample project. The CMakeLists.txt file looks like;
<syntaxhighlight lang="cmake">
project (krosshello)
project (krosshello)
find_package(KDE4 REQUIRED)
find_package(KDE4 REQUIRED)
Line 11: Line 23:
kde4_add_executable(krosshello ${krosshello_SRCS})
kde4_add_executable(krosshello ${krosshello_SRCS})
target_link_libraries(krosshello ${KDE4_KDEUI_LIBS} ${KDE4_KROSSUI_LIBS})
target_link_libraries(krosshello ${KDE4_KDEUI_LIBS} ${KDE4_KROSSUI_LIBS})
</syntaxhighlight>


=== main.cpp ===
=== main.cpp ===


<code cpp>
The main.cpp does create the sample application and shows the mainwindow instance.
 
<syntaxhighlight lang="cpp">
#include <QString>
#include <QString>
#include <KApplication>
#include <KApplication>
Line 24: Line 39:


int main (int argc, char *argv[]) {
int main (int argc, char *argv[]) {
// Used to store information about a program.
    // Used to store information about a program.
KAboutData aboutData("krosshello",
    KAboutData aboutData("krosshello",
0,
        0,ki18n("Kross Hello World"),"1.0",
ki18n("Kross Hello World"),
ki18n("Hello World application for Kross"),
"1.0",
KAboutData::License_GPL,
ki18n("Hello World application for Kross"),
ki18n("(c) 2007"),ki18n("Some text..."),
KAboutData::License_GPL,
"http://kross.dipe.org","[email protected]");
ki18n("(c) 2007"),
    // Access to the command-line arguments.
ki18n("Some text..."),
    KCmdLineArgs::init( argc, argv, &aboutData );
"http://kross.dipe.org",
    // Initialize the application.
    KApplication app;
 
    // Create and show the main window.
// Access to the command-line arguments.
    MainWindow* window = new MainWindow();
KCmdLineArgs::init( argc, argv, &aboutData );
    window->show();
// Initialize the application.
    // Finally execute the application.
KApplication app;
    return app.exec();
 
// Create and show the main window.
MainWindow* window = new MainWindow();
window->show();
 
// Finally execute the application.
return app.exec();
}
}
</code>
</syntaxhighlight>


=== mainwindow.h ===
=== mainwindow.h ===


<code cpp>
This is the main windows class which defines some displayed widgets for our small sample application.
 
<syntaxhighlight lang="cpp">
#ifndef MAINWINDOW_H
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#define MAINWINDOW_H
Line 59: Line 69:
#include <QLabel>
#include <QLabel>
#include <QLineEdit>
#include <QLineEdit>
#include <kross/core/action.h>
#include <kross/core/action.h>


Line 65: Line 74:
class MainWindow : public QWidget
class MainWindow : public QWidget
{
{
Q_OBJECT
    Q_OBJECT
public:
    public:
// The constructor.
        MainWindow(QWidget *parent=0);
MainWindow(QWidget *parent=0);
    private Q_SLOTS:
private Q_SLOTS:
        // This slot is called when the item in the
// This slot is called when the item in the combobox is changed.
        // interpreter-combobox is changed.
void interpreterActivated(const QString &);
        void interpreterActivated(const QString &);
private:
    private:
QLineEdit* txtInputString;
        QLineEdit* txtInputString;
QLabel* lblMessage;
QLabel* lblMessage;
QComboBox* cmbInterpreters;
QComboBox* cmbInterpreters;
// We now have the action as class-member.
Kross::Action* action;
Kross::Action* action;
};
};


#endif
#endif
</code>
</syntaxhighlight>


=== mainwindow.cpp ===
=== mainwindow.cpp ===


<code cpp>
The implementation of the main window functionality. We are displaying a QLineEdit that will be passed as first argument to a scripting function named "reverseString". Those function returns a string that will then displayed in a QLabel. Just like at the [[Development/Tutorials/Kross/Hello World|Kross Hello World]] tutorial, we also display a QComboBox that does allow to choose an interpreter.
#include "mw.h"


<syntaxhighlight lang="cpp">
#include "mainwindow.h"
#include <QVBoxLayout>
#include <QVBoxLayout>
#include <QDebug>
#include <QDebug>
#include <kross/core/manager.h>
#include <kross/core/manager.h>
#include <kross/core/action.h>
#include <kross/core/action.h>
Line 97: Line 105:
MainWindow::MainWindow(QWidget *parent) : QWidget(parent)
MainWindow::MainWindow(QWidget *parent) : QWidget(parent)
{
{
txtInputString = new QLineEdit();
    txtInputString = new QLineEdit();
lblMessage = new QLabel("Hello");
    lblMessage = new QLabel("Hello");
cmbInterpreters = new QComboBox ();
    cmbInterpreters = new QComboBox ();
cmbInterpreters->addItem("Choose Interpreter", "");
    cmbInterpreters->addItem("Choose Interpreter", "");
    foreach(QString s, Kross::Manager::self().interpreters())
        cmbInterpreters->addItem(s);
    connect(cmbInterpreters, SIGNAL(activated(const QString &)),
            SLOT(interpreterActivated(const QString &)));


foreach(QString s, Kross::Manager::self().interpreters())
    QVBoxLayout *vLayout = new QVBoxLayout;
cmbInterpreters->addItem(s);
    vLayout->addWidget(cmbInterpreters);
    vLayout->addWidget(txtInputString);
    vLayout->addWidget(lblMessage);
    setLayout(vLayout);


connect(cmbInterpreters, SIGNAL(activated(const QString &)),
    // Create the Kross::Action instance .
SLOT(interpreterActivated(const QString &)));
    action = new Kross::Action(this, "MyScript");


QVBoxLayout *vLayout = new QVBoxLayout;
    // We don't need to publish any QObject instances
vLayout->addWidget(cmbInterpreters);
    // for this sample.
vLayout->addWidget(txtInputString);
    //action->addObject(txtInputString, "MyInputString");
vLayout->addWidget(lblMessage);
    //action->addObject(cmbInterpreters, "MyInterpreter");
setLayout(vLayout);
    //action->addObject(lblMessage, "MyLabel");
}


// This time we create the Kross::Action already within the
// this slot is called when the active item of the combobox changes.
// constructor and add the objects that should be accessible
void MainWindow::interpreterActivated(const QString &interpr)
// from within scripting code.
{
action = new Kross::Action(this, "MyScript");
    // this time we are using external script files.
    QString filename;
    if(interpr == "python") // Python backend
        filename = "Testscriptfile.py";
    else if(interpr == "javascript") // JavaScript backend
        filename = "Testscriptfile.js";
    else { // no other sample script files
        lblMessage->setText("-");
        return;
    }
 
    // set the script file that should be executed
    action->setFile(filename);
 
    // execute the scripting code, i.e. preload
    // please note, that it's needed to trigger aka
    // execute the script before calling a function
    // using callFunction() or before asking what
    // functions are available using functionNames().
    // also it's only needed to trigger the script
    // once and then call whatever functions should
    // be called or signals emitted or whatever.
    action->trigger();


     action->addObject(txtInputString, "MyInputString");
     // The list of arguments passed to the function
action->addObject(cmbInterpreters, "MyInterpreter");
    QVariantList args;
action->addObject(lblMessage, "MyLabel");
 
    // here we pass in the QLineEdit instance
    // as first argument
    QVariant v;
    v.setValue( (QWidget*) txtInputString );
    args << v;
 
    // and the second argument is a string
    args << "Hello World";
 
    // Call the arbitrary function
    QVariant result = action->callFunction("reverseString",args);
 
    // Use the returnvalue of the function call
    lblMessage->setText(result.toString());
}
}
</syntaxhighlight>


// this slot is called when the active item of the combobox changes.
== The Script Files ==
void MainWindow::interpreterActivated(const QString &strSelectedInterpreter)
{
if(strSelectedInterpreter.isEmpty()) {
lblMessage->setText("-");
return;
}


// this time we are using external script files.
We are then able to use supported scripting backends like Python, Ruby and JavaScript to execute a scripting function named "reverseString" and called in the mainwindow.cpp file. The "reverseString" function has a QLineEdit and a QString as arguments and returns a QString.
QString filename;
if(strSelectedInterpreter == "python")
filename = "krossSigsSlots.py";
else if(strSelectedInterpreter == "javascript")
filename = "krossSigsSlots.js";
else
return;


// set the script file that should be executed.
=== Testscriptfile.js ===
action->setFile(filename);


// finally execute the scripting code, i.e. preload
The Testscriptfile.js JavaScript file does provide us the reverseString scripting function.
action->trigger();


// now we can call arbitrary functions
<syntaxhighlight lang="javascript">
QVariantList arguments;
function reverseString(lineedit, s)
{
    println("reverseString lineedit=" + lineedit + " s=" + s);
    s = "JsReverse: " + s;


QVariant v;
    lineedit.setText(s);
v.setValue( (QWidget*) txtInputString );
    //MyInputString.text = s;
arguments << v;


arguments << "Hello World";
    return s.split("").reverse().join("");
QVariant result = action->callFunction("reverseString", arguments);
lblMessage->setText(result.toString());
}
}
</code>
</syntaxhighlight>
 
=== Testscriptfile.py ===
 
The Testscriptfile.py Python file does provide us the reverseString scripting function.
 
<syntaxhighlight lang="python">
def reverseString(lineedit, s):
    print "reverseString lineedit=%s s=%s" % (lineedit,s)
    s = "PyReverse: %s" % s
    lineedit.setText(s)
    return s[::-1]
</syntaxhighlight>

Latest revision as of 08:11, 31 May 2019

Warning
This page needs a review and probably holds information that needs to be fixed.

Parts to be reviewed:

Port to KF5

This tutorial shows how to deal with functions provided by a script. This tutorial is based on the Hello World tutorial and extends the codebase we wrote there with new functionality to outline how calling scripting functions could be done.

Please note, that it is recommed to use Autoconnected Signals and Slots if you just like to have a bunch of scripting functions within a script that should be called on demand. That way you have a clear interface with signals that are mapped transparent to scripting functions. The "trick" here is, that Kross will automaticly connect each signal a QObject has to a matching scripting function and at the C++ side all what is needed is to emit a signal and behind the scene the arguments will be translated and a possible defined scripting function got called. A good example here is SuperKaramba which uses the signal+slot rather then callFunction() way. All it defines is the rather huge class that inherits QObject (karambainterface.h). The signals within that class are transparent mapped to scripting functions (e.g. template.py and template.js) while the slots are then callable from within the scripting code. Signals and slots are faster and more type-safe then the in this tutorial used callFunction() (see also the Kross::Action class).

The C++ Code

The following code is a KDE C++ project to demonstrate how to deal with scripting functions.

CMakeLists.txt

We are using cmake to build our small sample project. The CMakeLists.txt file looks like;

project (krosshello)
find_package(KDE4 REQUIRED)
include_directories( ${KDE4_INCLUDES} )
set(krosshello_SRCS main.cpp mainwindow.cpp)
kde4_add_executable(krosshello ${krosshello_SRCS})
target_link_libraries(krosshello ${KDE4_KDEUI_LIBS} ${KDE4_KROSSUI_LIBS})

main.cpp

The main.cpp does create the sample application and shows the mainwindow instance.

#include <QString>
#include <KApplication>
#include <KAboutData>
#include <KMessageBox>
#include <KCmdLineArgs>
#include <KLocalizedString>
#include "mainwindow.h"

int main (int argc, char *argv[]) {
    // Used to store information about a program.
    KAboutData aboutData("krosshello",
        0,ki18n("Kross Hello World"),"1.0",
	ki18n("Hello World application for Kross"),
	KAboutData::License_GPL,
	ki18n("(c) 2007"),ki18n("Some text..."),
	"http://kross.dipe.org","[email protected]");
    // Access to the command-line arguments.
    KCmdLineArgs::init( argc, argv, &aboutData );
    // Initialize the application.
    KApplication app;
    // Create and show the main window.
    MainWindow* window = new MainWindow();
    window->show();
    // Finally execute the application.
    return app.exec();
}

mainwindow.h

This is the main windows class which defines some displayed widgets for our small sample application.

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QComboBox>
#include <QLabel>
#include <QLineEdit>
#include <kross/core/action.h>

// The main window to display our combobox and the label.
class MainWindow : public QWidget
{
    Q_OBJECT
    public:
        MainWindow(QWidget *parent=0);
    private Q_SLOTS:
        // This slot is called when the item in the
        // interpreter-combobox is changed.
        void interpreterActivated(const QString &);
    private:
        QLineEdit* txtInputString;
	QLabel* lblMessage;
	QComboBox* cmbInterpreters;
	Kross::Action* action;
};

#endif

mainwindow.cpp

The implementation of the main window functionality. We are displaying a QLineEdit that will be passed as first argument to a scripting function named "reverseString". Those function returns a string that will then displayed in a QLabel. Just like at the Kross Hello World tutorial, we also display a QComboBox that does allow to choose an interpreter.

#include "mainwindow.h"
#include <QVBoxLayout>
#include <QDebug>
#include <kross/core/manager.h>
#include <kross/core/action.h>

// the constructor.
MainWindow::MainWindow(QWidget *parent) : QWidget(parent)
{
    txtInputString = new QLineEdit();
    lblMessage = new QLabel("Hello");
    cmbInterpreters = new QComboBox ();
    cmbInterpreters->addItem("Choose Interpreter", "");
    foreach(QString s, Kross::Manager::self().interpreters())
        cmbInterpreters->addItem(s);
    connect(cmbInterpreters, SIGNAL(activated(const QString &)),
            SLOT(interpreterActivated(const QString &)));

    QVBoxLayout *vLayout = new QVBoxLayout;
    vLayout->addWidget(cmbInterpreters);
    vLayout->addWidget(txtInputString);
    vLayout->addWidget(lblMessage);
    setLayout(vLayout);

    // Create the Kross::Action instance .
    action = new Kross::Action(this, "MyScript");

    // We don't need to publish any QObject instances
    // for this sample.
    //action->addObject(txtInputString, "MyInputString");
    //action->addObject(cmbInterpreters, "MyInterpreter");
    //action->addObject(lblMessage, "MyLabel");
}

// this slot is called when the active item of the combobox changes.
void MainWindow::interpreterActivated(const QString &interpr)
{
    // this time we are using external script files.
    QString filename;
    if(interpr == "python") // Python backend
        filename = "Testscriptfile.py";
    else if(interpr == "javascript") // JavaScript backend
        filename = "Testscriptfile.js";
    else { // no other sample script files
        lblMessage->setText("-");
        return;
    }

    // set the script file that should be executed
    action->setFile(filename);

    // execute the scripting code, i.e. preload
    // please note, that it's needed to trigger aka
    // execute the script before calling a function
    // using callFunction() or before asking what
    // functions are available using functionNames().
    // also it's only needed to trigger the script
    // once and then call whatever functions should
    // be called or signals emitted or whatever.
    action->trigger();

    // The list of arguments passed to the function
    QVariantList args;

    // here we pass in the QLineEdit instance
    // as first argument
    QVariant v;
    v.setValue( (QWidget*) txtInputString );
    args << v;

    // and the second argument is a string
    args << "Hello World";

    // Call the arbitrary function
    QVariant result = action->callFunction("reverseString",args);

    // Use the returnvalue of the function call
    lblMessage->setText(result.toString());
}

The Script Files

We are then able to use supported scripting backends like Python, Ruby and JavaScript to execute a scripting function named "reverseString" and called in the mainwindow.cpp file. The "reverseString" function has a QLineEdit and a QString as arguments and returns a QString.

Testscriptfile.js

The Testscriptfile.js JavaScript file does provide us the reverseString scripting function.

function reverseString(lineedit, s)
{
    println("reverseString lineedit=" + lineedit + " s=" + s);
    s = "JsReverse: " + s;

    lineedit.setText(s);
    //MyInputString.text = s;

    return s.split("").reverse().join("");
}

Testscriptfile.py

The Testscriptfile.py Python file does provide us the reverseString scripting function.

def reverseString(lineedit, s):
    print "reverseString lineedit=%s s=%s" % (lineedit,s)
    s = "PyReverse: %s" % s
    lineedit.setText(s)
    return s[::-1]