Development/Tutorials/Kross/Introduction: Difference between revisions
No edit summary |
|||
Line 1: | Line 1: | ||
==Kross== | ==Kross== | ||
The [http://kross.dipe.org Kross] scripting framework provides full | ===Introduction=== | ||
[http://www.python.org/ Python], [http://www.ruby-lang.org/ Ruby] and | The [http://kross.dipe.org Kross] scripting framework provides full [http://www.python.org/ Python], [http://www.ruby-lang.org/ Ruby] and [http://xmelegance.org/kjsembed KDE JavaScript] scripting support. The goal was | ||
[http://xmelegance.org/kjsembed KDE JavaScript] scripting support. The goal was | to limit the work needed on applications to have them fully scriptable and to provide a modular way to transparently integrate additional interpreters and in that way extend your application with a new scripting-backend without any new line of code and even without any recompile. To achieve this internally Qt's introspection-functionality like signals, slots, properties, enums, QVariant, QObject, QMetaObject, QMetaType, etc. are used to deal with functionality at runtime. | ||
to limit the work needed on applications to have them fully scriptable and to provide a modular | |||
way to transparently integrate additional interpreters and in that way extend your application | ===What Kross is=== | ||
with a new scripting-backend without any new line of code and even without any recompile. To | Kross is a modular scripting framework that provides a complete framework to embed scripting interpreters like Python, Ruby and JavaScript transparently into native applications to bridge the static and dynamic worlds together. | ||
achieve this internally Qt's introspection-functionality like signals, slots, properties, | |||
enums, QVariant, QObject, QMetaObject, QMetaType, etc. are used to deal with functionality | Scripting interpreters are plugins that are loaded dynamicly on demand at runtime by the Kross framework. The application that uses Kross to provide scripting functionality to the user does not need to know anything about the interpreter itself or any other backend-details. All the application needs to know is, that there exists scripting code that should be executed. Kross will then take care of it. | ||
at runtime. | |||
===What Kross is not=== | |||
Kross does not implement an own scripting language rather then providing access to already existing solutions. Kross does not implement a complete Toolkit-binding rather providing access to already existing solutions like PyQt/PyKDE or TkInter for Python or Korundum/QtRuby for Ruby. Kross does not auto-generate code, it is not needed to maintain any configurationfiles and Kross does not come with yet another own bindings-API. All it does is to connect things transparently together and to offer optional access to already existing technologies. | |||
===Reuse and choice are the goals=== | |||
There exist a wide range of solutions to deal with scripting. Languages like Python did enjoyed years of evolution, a large community around them with 3rd-party modules for nearly everything exist. Same goes for a bunch of other languages where sometimes a specialy functionality is only available for some of them and where each language comes with it's own way of doing things. Each user has an own preference for what scripting language he likes to use and maybe even already collected skills in some specific areas. | |||
Rather then limiting us and our users to only one specific language with it's own way of doing things, Kross does offer a flexible way to be optional able to support all of them without additional code and maintaining work at the application level. To allow the user to choose what he likes and knows most does limit the entry-barrier to get his things done and even to be able to contribute own solutions significantly. | |||
===Supported interpreters=== | |||
As of today Python, Ruby and JavaScript are supported. While all of them are passing the unittests, current state is, that Python is the most complete one followed by Ruby which is near the same state and finally JavaScript which needs some more tweaks to be as rock-stable as the other both implementations. | |||
Support for Java is on progress in playground/bindings/krossjava. | |||
For sure any interpreter-plugin you may like to implement is very welcome. At the end the open source world is about choices :) | |||
===Advantage of Kross over other solutions=== | |||
* Scripting interpreter independend. Kross does not implement an interpreter by it's own rather then providing access to already existing solutions. You don't have to decide for your user what scripting language he should use, let him decide and keep him happy and productive that way! | |||
* Real embedding. Rather then communicating with scripting backends e.g. over stdin/stdout or DBus Kross does come with plugins that really embed the interpreters and deal with them on the native C/C++ code level to gain optimal performance and flexibility. | |||
* Kross is damn fast. Kross had a bit of luck to be adopted by Krita so early since one of the top-priorities Krita had on a scripting backend was performance. During the years we got it managed to increase the speed to a level where it's hard to see a difference to native in C/C++ written code. | |||
* Kross was designed from the beginning with platform-independency in mind. This is the result of the origin of Kross which started initial as Kexi plugin and Kexi itself runs at least on Linux, Windows and Mac since it's early days. | |||
* Kross is stable and well proven cause before it finally landed in kdelibs it was in use in KOffice for 3 major releases and was that way "tested" by a wide range of users worldwide. | |||
* There exists a bunch of documentations, scripts and applications that are using Kross already today. The later one guaranteed that Kross offers the solution for a wide range of use-case scenarios. | |||
* Kross could be easily extended with a new scripting interpreter. There are no changes needed at the applications. So, whatever hype we are able to see next few years, we are ready to fullfit the possible user-requests for them without much pain. | |||
* The synergy-effect if multiple applications are sharing the same interpreter plugin implementations ensures that bugs are discovered and fixed as soon as possible. Also each app is able to profit if someone writes for example a plugin for the LUA-interpreter each app is able to use the new backend without any recompile. | |||
* Kross does reuse existing technologies as much as possible. There is no code-generation at compiletime needed and no own "bindings API" is introduced since we are reusing the Qt Meta-Object System. You are able to throw in a QObject and a script is able to connect with the signals, call slots as there are functions, access enumerations, get/set properties or pass QObject/QWidget instances around as there are classes. | |||
* Unlimit flexibility of the interpreter backend since each interpreter-plugin is able to decide by it's own how to implement the bridging functionality. If you like for example to have it more pythonic by using the with Python 2.4 introduced Decorators, you are free to implement it and each app that uses Kross will profit by beeing able to use decorators from now on. | |||
* Kross will stay backward-compatible cause as part of kdelibs it needs to follow strict policies and needs to provide backward-compatibility at least during the lifetime of KDE4. | |||
==The Interpreter plugins== | ==The Interpreter plugins== | ||
Kross offers a plugin-interface to integrate interpreter backends like Python, Ruby and | Kross offers a plugin-interface to integrate interpreter backends like Python, Ruby and KDE-Javascript. They are loaded on demand at runtime. Neither the application nor Kross needs to know any details about the backends. This clear separation between the application and scripting details enables at the one hand, to deal with scripting at an abstract level without being bound to only one backend, and at the other hand makes it easy to integrate scripting into an already existing application. | ||
KDE-Javascript. They are loaded on demand at runtime. Neither the application nor Kross | |||
needs to know any details about the backends. This clear separation between the application | |||
and scripting details enables at the one hand, to deal with scripting at an abstract level without | |||
being bound to only one backend, and at the other hand makes it easy to integrate scripting | |||
into an already existing application. | |||
Currently Kross comes with support for 3 scripting backends; | Currently Kross comes with support for 3 scripting backends; | ||
Line 30: | Line 52: | ||
([http://www.ruby-lang.org/ home] | ([http://www.ruby-lang.org/ home] | ||
[http://www.ruby-lang.org/en/documentation/ docs]) language.</li> | [http://www.ruby-lang.org/en/documentation/ docs]) language.</li> | ||
<li>The [http://websvn.kde.org/trunk/KDE/kdelibs/kross/kjs KDE JavaScript plugin] | <li>The [http://websvn.kde.org/trunk/KDE/kdelibs/kross/kjs KDE JavaScript plugin] uses the in kdelibs4 included Kjs | ||
([http://websvn.kde.org/trunk/KDE/kdelibs/kjs/README?view=markup readme] | ([http://websvn.kde.org/trunk/KDE/kdelibs/kjs/README?view=markup readme] | ||
[http://websvn.kde.org/trunk/KDE/kdelibs/kjs/ svn]) and KjsEmbed4 | [http://websvn.kde.org/trunk/KDE/kdelibs/kjs/ svn]) and KjsEmbed4 | ||
Line 41: | Line 62: | ||
Each interpreter plugin needs to implement two abstract classes; | Each interpreter plugin needs to implement two abstract classes; | ||
<ul> | <ul> | ||
<li>The [http://websvn.kde.org/trunk/KDE/kdelibs/kross/core/interpreter.h?view=markup Kross::Interpreter] | <li>The [http://websvn.kde.org/trunk/KDE/kdelibs/kross/core/interpreter.h?view=markup Kross::Interpreter] class is a singleton controlled by the [http://websvn.kde.org/trunk/KDE/kdelibs/kross/core/manager.h?view=markup Kross::Manager] and could be used to setup the interpreter or do other things to share functionality between instances of the Script class.</li> | ||
<li>The [http://websvn.kde.org/trunk/KDE/kdelibs/kross/core/script.h?view=markup Kross::Script] class handles exactly one script instance. An application is able to deal with multiple scripts at the same time where each of them has it's own instance of the Kross::Script class controlled by an instance of the more abstract [http://websvn.kde.org/trunk/KDE/kdelibs/kross/core/action.h?view=markup Kross::Action] class.</li> | |||
[http://websvn.kde.org/trunk/KDE/kdelibs/kross/core/manager.h?view=markup Kross::Manager] | |||
<li>The [http://websvn.kde.org/trunk/KDE/kdelibs/kross/core/script.h?view=markup Kross::Script] | |||
</ul> | </ul> | ||
Line 109: | Line 122: | ||
DESTINATION ${PLUGIN_INSTALL_DIR}) | DESTINATION ${PLUGIN_INSTALL_DIR}) | ||
</pre> | </pre> | ||
The following Python sample code accesses then the module at runtime and uses the QObject, calls it's slots and properties and connects a signal with a python function (e.g. save as file named "myobjecttest.py" and execute with "kross ./myobjecttest.py"); | The following Python sample code accesses then the module at runtime and uses the QObject, calls it's slots and properties and connects a signal with a python function (e.g. save as file named "myobjecttest.py" and execute with "kross ./myobjecttest.py"); | ||
<pre> | <pre> | ||
#!/usr/bin/env kross | #!/usr/bin/env kross | ||
Line 125: | Line 140: | ||
==Manager, GuiClient, Action== | ==Manager, GuiClient, Action== | ||
The [http://websvn.kde.org/trunk/KDE/kdelibs/kross/core/manager.h?view=markup Kross::Manager] | The [http://websvn.kde.org/trunk/KDE/kdelibs/kross/core/manager.h?view=markup Kross::Manager] class is a singleton that provides access to the interpreters, to actions and to the modules. The Kross::Manager is available within scripting code as module named "Kross". Following Python script uses the Kross module to create a new Kross::Action instance, fills it with JavaScript code and executes that JavaScript code. | ||
class is a singleton that provides access to the interpreters, to actions and to the modules. | |||
The Kross::Manager is available within scripting code as module named "Kross". Following Python | |||
script uses the Kross module to create a new Kross::Action instance, fills it with JavaScript | |||
code and executes that JavaScript code. | |||
<pre> | <pre> | ||
#!/usr/bin/env kross | #!/usr/bin/env kross | ||
Line 139: | Line 151: | ||
</pre> | </pre> | ||
The [http://websvn.kde.org/trunk/KDE/kdelibs/kross/core/guiclient.h?view=markup Kross::GuiClient] | The [http://websvn.kde.org/trunk/KDE/kdelibs/kross/core/guiclient.h?view=markup Kross::GuiClient] class implements KXMLGUIClient to provide GUI functionality, handling of XML configuration files on a more abstract level and offers some predefined actions that could be optionally used in your application. | ||
class implements KXMLGUIClient to provide GUI functionality, handling of XML configuration files | |||
on a more abstract level and offers some predefined actions that could be optionally used in | The [http://websvn.kde.org/trunk/KDE/kdelibs/kross/core/action.h?view=markup Kross::Action] class offers an abstract container to deal with scripts like a single standalone scriptfile. Each action holds a reference to the by the matching [http://websvn.kde.org/trunk/KDE/kdelibs/kross/core/interpreter.h?view=markup Kross::Interpreter] instance created by [http://websvn.kde.org/trunk/KDE/kdelibs/kross/core/script.h?view=markup Kross::Script] instance. Following Python script accesses the Kross module and the self variable which is our Kross::Action instance that provides the context for the running Python script. | ||
your application. | |||
<pre> | <pre> | ||
#!/usr/bin/env kross | #!/usr/bin/env kross |
Revision as of 20:54, 6 May 2007
Kross
Introduction
The Kross scripting framework provides full Python, Ruby and KDE JavaScript scripting support. The goal was to limit the work needed on applications to have them fully scriptable and to provide a modular way to transparently integrate additional interpreters and in that way extend your application with a new scripting-backend without any new line of code and even without any recompile. To achieve this internally Qt's introspection-functionality like signals, slots, properties, enums, QVariant, QObject, QMetaObject, QMetaType, etc. are used to deal with functionality at runtime.
What Kross is
Kross is a modular scripting framework that provides a complete framework to embed scripting interpreters like Python, Ruby and JavaScript transparently into native applications to bridge the static and dynamic worlds together.
Scripting interpreters are plugins that are loaded dynamicly on demand at runtime by the Kross framework. The application that uses Kross to provide scripting functionality to the user does not need to know anything about the interpreter itself or any other backend-details. All the application needs to know is, that there exists scripting code that should be executed. Kross will then take care of it.
What Kross is not
Kross does not implement an own scripting language rather then providing access to already existing solutions. Kross does not implement a complete Toolkit-binding rather providing access to already existing solutions like PyQt/PyKDE or TkInter for Python or Korundum/QtRuby for Ruby. Kross does not auto-generate code, it is not needed to maintain any configurationfiles and Kross does not come with yet another own bindings-API. All it does is to connect things transparently together and to offer optional access to already existing technologies.
Reuse and choice are the goals
There exist a wide range of solutions to deal with scripting. Languages like Python did enjoyed years of evolution, a large community around them with 3rd-party modules for nearly everything exist. Same goes for a bunch of other languages where sometimes a specialy functionality is only available for some of them and where each language comes with it's own way of doing things. Each user has an own preference for what scripting language he likes to use and maybe even already collected skills in some specific areas.
Rather then limiting us and our users to only one specific language with it's own way of doing things, Kross does offer a flexible way to be optional able to support all of them without additional code and maintaining work at the application level. To allow the user to choose what he likes and knows most does limit the entry-barrier to get his things done and even to be able to contribute own solutions significantly.
Supported interpreters
As of today Python, Ruby and JavaScript are supported. While all of them are passing the unittests, current state is, that Python is the most complete one followed by Ruby which is near the same state and finally JavaScript which needs some more tweaks to be as rock-stable as the other both implementations.
Support for Java is on progress in playground/bindings/krossjava.
For sure any interpreter-plugin you may like to implement is very welcome. At the end the open source world is about choices :)
Advantage of Kross over other solutions
- Scripting interpreter independend. Kross does not implement an interpreter by it's own rather then providing access to already existing solutions. You don't have to decide for your user what scripting language he should use, let him decide and keep him happy and productive that way!
- Real embedding. Rather then communicating with scripting backends e.g. over stdin/stdout or DBus Kross does come with plugins that really embed the interpreters and deal with them on the native C/C++ code level to gain optimal performance and flexibility.
- Kross is damn fast. Kross had a bit of luck to be adopted by Krita so early since one of the top-priorities Krita had on a scripting backend was performance. During the years we got it managed to increase the speed to a level where it's hard to see a difference to native in C/C++ written code.
- Kross was designed from the beginning with platform-independency in mind. This is the result of the origin of Kross which started initial as Kexi plugin and Kexi itself runs at least on Linux, Windows and Mac since it's early days.
- Kross is stable and well proven cause before it finally landed in kdelibs it was in use in KOffice for 3 major releases and was that way "tested" by a wide range of users worldwide.
- There exists a bunch of documentations, scripts and applications that are using Kross already today. The later one guaranteed that Kross offers the solution for a wide range of use-case scenarios.
- Kross could be easily extended with a new scripting interpreter. There are no changes needed at the applications. So, whatever hype we are able to see next few years, we are ready to fullfit the possible user-requests for them without much pain.
- The synergy-effect if multiple applications are sharing the same interpreter plugin implementations ensures that bugs are discovered and fixed as soon as possible. Also each app is able to profit if someone writes for example a plugin for the LUA-interpreter each app is able to use the new backend without any recompile.
- Kross does reuse existing technologies as much as possible. There is no code-generation at compiletime needed and no own "bindings API" is introduced since we are reusing the Qt Meta-Object System. You are able to throw in a QObject and a script is able to connect with the signals, call slots as there are functions, access enumerations, get/set properties or pass QObject/QWidget instances around as there are classes.
- Unlimit flexibility of the interpreter backend since each interpreter-plugin is able to decide by it's own how to implement the bridging functionality. If you like for example to have it more pythonic by using the with Python 2.4 introduced Decorators, you are free to implement it and each app that uses Kross will profit by beeing able to use decorators from now on.
- Kross will stay backward-compatible cause as part of kdelibs it needs to follow strict policies and needs to provide backward-compatibility at least during the lifetime of KDE4.
The Interpreter plugins
Kross offers a plugin-interface to integrate interpreter backends like Python, Ruby and KDE-Javascript. They are loaded on demand at runtime. Neither the application nor Kross needs to know any details about the backends. This clear separation between the application and scripting details enables at the one hand, to deal with scripting at an abstract level without being bound to only one backend, and at the other hand makes it easy to integrate scripting into an already existing application.
Currently Kross comes with support for 3 scripting backends;
- The Python plugin implements access to the Python (home docs) language.
- The Ruby plugin implements access to the Ruby (home docs) language.
- The KDE JavaScript plugin uses the in kdelibs4 included Kjs (readme svn) and KjsEmbed4 (home mailinglist svn) frameworks to provide access to the JavaScript language.
Each interpreter plugin needs to implement two abstract classes;
- The Kross::Interpreter class is a singleton controlled by the Kross::Manager and could be used to setup the interpreter or do other things to share functionality between instances of the Script class.
- The Kross::Script class handles exactly one script instance. An application is able to deal with multiple scripts at the same time where each of them has it's own instance of the Kross::Script class controlled by an instance of the more abstract Kross::Action class.
The Module plugins
Modules are plugins loaded on demand at runtime to provide additional functionality. They only provide a factory to create and return a QObject instance that is then exposed to the scripting backend.
Since Qt's introspection functionality is used, we are able to throw in just QObject's and have them act as classes/objects within a scripting backend. Slots are membermethods while properties and enumerations are membervariables. If your application also likes to offer DBus support it may be an idea to reuse the QDBusAbstractAdaptor implementations your application has also for scripting, like for example KSpread did.
Let's take a look at the following code that implements the MyObject class which inherits from QObject;
class MyObject : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName) public: MyObject(QObject* parent = 0) : QObject(parent) {} virtual ~MyObject() {} QString name() const { return objectName(); } void setName(const QString& name) { return setObjectName(name); } public slots: QObject* create(const QString& name, QObject* parent=0) { MyObject* obj = new MyObject(parent); obj->setObjectName(name); return obj; } QObject* parent() const { return QObject::parent(); } void setParent(QObject* parent) { QObject::setParent(parent); emit parentChanged(); } signals: void parentChanged(); }; extern "C" { QObject* krossmodule() { return new MyObject(); } }
At the bottom the krossmodule() function will be called by Kross and returns an instance of MyObject.
Then we just need to have our myobject.h and myobject.cpp files, filled with the content above, defined in the CMakeLists.txt file. The library needs to be named "krossmodule..." where the "..." is then the name the module is accessible as. For our example we use "krossmodulemyobjectmod" and therefore we are able to access the module if installed as "myobjectmod". The example does not depend on Kross, so you may like to replace ${KROSS_INCLUDES} with whatever else your module depends on.
include_directories(${CMAKE_SOURCE_DIR} ${KROSS_INCLUDES}) set(krossmodulemyobjectmod_PART_SRCS myobject.cpp) kde4_automoc(${krossmodulemyobjectmod_PART_SRCS}) kde4_add_plugin(krossmodulemyobjectmod ${krossmodulemyobjectmod_PART_SRCS}) target_link_libraries(krossmodulemyobjectmod ${KDE4_KDECORE_LIBS} ${KDE4_KROSSCORE_LIBS}) install(TARGETS krossmodulemyobjectmod DESTINATION ${PLUGIN_INSTALL_DIR})
The following Python sample code accesses then the module at runtime and uses the QObject, calls it's slots and properties and connects a signal with a python function (e.g. save as file named "myobjecttest.py" and execute with "kross ./myobjecttest.py");
#!/usr/bin/env kross import Kross m = Kross.module("myobjectmod") m.name = "MyObjectModuleName" obj1 = m.create("OtherObjectName") def myCallbackFunc(args): print "The parent of obj1 changed" obj1.connect("parentChanged()",myCallbackFunc) obj1.setParent(m) print "%s %s" % (obj1.name,obj1.parent().name)
Manager, GuiClient, Action
The Kross::Manager class is a singleton that provides access to the interpreters, to actions and to the modules. The Kross::Manager is available within scripting code as module named "Kross". Following Python script uses the Kross module to create a new Kross::Action instance, fills it with JavaScript code and executes that JavaScript code.
#!/usr/bin/env kross import Kross a = Kross.action("MyKjsScript") a.setInterpreter("javascript") a.setCode("println(\"Hello world from Kjs\");") a.trigger()
The Kross::GuiClient class implements KXMLGUIClient to provide GUI functionality, handling of XML configuration files on a more abstract level and offers some predefined actions that could be optionally used in your application.
The Kross::Action class offers an abstract container to deal with scripts like a single standalone scriptfile. Each action holds a reference to the by the matching Kross::Interpreter instance created by Kross::Script instance. Following Python script accesses the Kross module and the self variable which is our Kross::Action instance that provides the context for the running Python script.
#!/usr/bin/env kross import Kross print "objectName=%s" % Kross.objectName() print "interpreters=%s" % Kross.interpreters() print "objectName=%s" % self.objectName() print "text=%s" % self.text print "enabled=%s" % self.enabled print "currentPath=%s" % self.currentPath() print "interpreter=%s" % self.interpreter() print "description=%s" % self.description() print "code=%s" % self.code()
DBus and Kross
With the QtDBus module Qt provides a library that a Qt/KDE application is able to use to make Inter-Process Communication using the D-BUS protocol.
The QtDBus module uses the Qt Signals and Slots mechanism. Applications that like to provide parts of there functionality to the DBus world are able to do so by implementing classes that inherit from the QDBusAbstractAdaptor class.
Kross is able to reuse such by an application provided bindings. So, once your application supports dbus, Kross is able to reuse those already existing code and offers transparent access to the scripting-backends to them. How does this differ from e.g. the python-dbus package? Well, first method-calls don't go through the dbus-socket and then it's not dbus-related at all except, that we are able to reuse what your application offers anyway: a clean interface to the outside world. But that's not all, we are not limited to what's possible with dbus. We are also able to exchange instance-pointers to QObject or QWidget instances.
For an example you may like to take a look at how it was done in the KSpread ScriptingModule class.