Jump to content

Development/Tutorials/SuperKaramba: Difference between revisions

From KDE TechBase
Dipesh (talk | contribs)
Neverendingo (talk | contribs)
m Text replace - "</code>" to "</syntaxhighlight>"
 
(122 intermediate revisions by 6 users not shown)
Line 3: Line 3:
===Intro===
===Intro===


[http://netdragon.sourceforge.net SuperKaramba] is a tool that allows one to easily create functionality enhancement modules on a [http://www.kde.org KDE desktop]. Such modules are interactive programs written in [http://www.python.org Python] or [http://www.ruby-lang.org/ Ruby] that are usually embedded directly into the background and do not disturb the normal view of the desktop.
[http://netdragon.sourceforge.net SuperKaramba] is a tool that allows one to easily create functionality enhancement modules on a [http://www.kde.org KDE desktop]. Such modules are interactive programs written in [http://www.python.org Python], [http://www.ruby-lang.org/ Ruby] or [http://xmelegance.org/kjsembed KDE JavaScript] that are usually embedded directly into the background and do not disturb the normal view of the desktop.


===Links===
===Links===


* [http://netdragon.sourceforge.net Homepage]
* [http://netdragon.sourceforge.net Homepage] The homepage of SuperKaramba.
* [http://netdragon.sourceforge.net/sinfo.html Basic Tutorial for Theme Creators]
* [[Projects/SuperKaramba|Project Page]] The projectpage on techbase.
* [http://netdragon.sourceforge.net/api.html API-Reference]
* [[Projects/SuperKaramba/API|API]] Reference of the scripting application programmer interface.
* [http://websvn.kde.org/trunk/KDE/kdeutils/superkaramba/examples/ Examples]
* [http://websvn.kde.org/trunk/KDE/kdeutils/superkaramba/examples/ Examples] Some examples SuperKaramba ships with. See also the [http://www.kde-look.org/index.php?xcontentmode=38 Themes] and [http://www.biodesign.com.ar/blog/?cat=2 More Themes].
* [http://www.kde-look.org/index.php?xcontentmode=38 Themes]
 
* [http://www.biodesign.com.ar/blog/?cat=2 More Themes]
[[Image:skplasmaapplet.jpg]]
This screenshot shows the  [http://www.kde-look.org/content/show.php/Aero+AIO?content=24626 Aero AIO] Karamba Theme running on KDE4 as [[Projects/Plasma|Plasmoid]] (aka Plasma Applet).
 
==Commands, Meters and Sensors==
 
SuperKaramba does differ between 3 kind of elements. Commands define something  that should be done (actions), meters are used to display something (GUI widgets) while sensors are to get something (data delivery).
 
* A list of [http://netdragon.sourceforge.net/sgeneral.html General Commands] that you can put in your theme file. This includes tags that define the widget shape, set default fonts, and create click-areas that launch programs.
* A list of [http://netdragon.sourceforge.net/smeters.html Meters] that you can put in your theme file. This includes meters display the values of sensors. Meters can auto-update.
* A list of [http://netdragon.sourceforge.net/ssensors.html Sensors] that you can put in your theme file. Sensors allow you to display system properties automatically. There are sensors to display the status of everything from memory use to the results of shell scripts. See here also the [[#Plasma::DataEngine_as_SuperKaramba_Sensor|Plasma::DataEngine as SuperKaramba Sensor]] chapter.
 
==Basic Tutorial for Theme Creators==
 
Themes in SuperKaramba consist of two parts. A theme file that defines the shape and size of the theme, and possibly many other things and a script file that adds interactivity to the theme. Basically, whenever an event happens, such as the user clicking the mouse on your theme, a function is called in your script file to let you react to the event.
 
===Creating the Theme File===
 
The theme files, those files with the fileextension *.theme, are text files that contain layout information and information about the values that will be shown.
 
====Theme File Samples====
 
The simplest form of Karamba is to load a file containing the word karamba. This will give you a empty transparent window with the size 300x300. A better idea is to enter something like this in a file:
<pre>
karamba x=30 y=30 w=400 h=200 interval=2000
</pre>
This will bring up a window at 30,30 with the size 400x200, and the window will refresh every 2000 milliseconds = 2 seconds.
 
The graphics elements of Karamba are called meters. The meters can be connected to sensors. A sensor can get, e.g., the current CPU load, the uptime, the date, and a lot of other things. It is also possible to display the output of an external program in a meter.
 
Creating a meter is not that hard. A text label can be created by putting this line in the file:
<pre>
text x=10 y=10 value="This is a test"
</pre>
To add some more spice we connect a sensor to a meter:
<pre>
text x=15 y=25 sensor=uptime
</pre>
A graph that logs the CPU load can be made like this:
<pre>
graph x=10 y=40 w=200 h=60 sensor=cpu
</pre>
Adding an image (the path is either relative to the configuration file or an absolute path):
<pre>
image x=10 y=270 path="picture.png"
</pre>
An introduction to the format parameter for a text meter:
<pre>
text x=10 y=330 sensor=memory format="You have %tm MB memory"
</pre>
Using of a the "time" Plasma::DataEngine:
<pre>
text x=0 y=0 sensor=plasma engine="time" source="Local" format="%Date %Time"
</pre>
 
Please note that only one meter per line in the configuration file is allowed. No spaces are allowed around the '=' characters.
 
====Notes====
 
In Karamba all the graphic elements inside one configuration file have the same refresh rate. So it could be wise to split up a configuration into smaller parts, if different parts need different refresh times. An area showing a log file, for example, probably does not need the same refresh rate as an area showing the CPU load. The refresh rate of the sensors can be set individually.
Also Karamba does not work with backgrounds without a picture, i.e., solid color, gradient, or pattern. One can get around this bug by creating a transparent PNG image (the size can be 1x1 pixel) and selecting this as the background picture.
 
===Creating the Script File===
 
The script file adds interactivity to the theme and can be written using the [http://www.python.org Python], [http://www.ruby-lang.org/ Ruby] or the [http://xmelegance.org/kjsembed KDE JavaScript] language.
 
====Super-Quick Start====
# Make a theme file like described at the "Creating the Theme File" chapter above.
# Download the [http://websvn.kde.org/trunk/KDE/kdeutils/superkaramba/examples/template.py template.py] or the [http://websvn.kde.org/trunk/KDE/kdeutils/superkaramba/examples/template.rb template.rb] or the [http://websvn.kde.org/trunk/KDE/kdeutils/superkaramba/examples/template.js template.js] script file, depending on what scripting language you like to use, and rename this file to match your theme file.
# Fill in all the callbacks in the script file that you want to handle. The template files just contain all callbacks, but you can safly remove those you don't need or just leave them in.
# Use the [[Projects/SuperKaramba/API|API Reference]] to know what commands to use to fill in the callbacks.
# Finally save your script file and open your theme file in SuperKaramba to test it!
 
====Regular Guide for "Mere Mortals"====
Now lets add interactivity to your theme file. If you have not written a theme file yet, you need to do that first like described at the "Creating the Theme File" chapter above. It will define the size, location on screen, and other basic elements of your theme.
 
====Event-based Programming====
 
Now that you have a basic theme file, load it with SuperKaramba. If there is nothing defined in the theme file other than size and location, you will have made an empty, transparent square that might be hard to find on the desktop.
Let's assume you have made a basic theme that just shows a few images in small area. To make your theme do anything interesting, it needs to be able to react to events.
 
Basically, an event is anything that can happen that SuperKaramba knows how to handle. Some examples are: the user moves or clicks the mouse, a new program is started on the desktop, a certain amount of time has passed, etc.
So to make your theme do something, you add Python or Ruby scripting code to the event callback that corresponds to the event to which you want to react. This is not nearly as hard as it sounds.
 
First, you need the [http://websvn.kde.org/trunk/KDE/kdeutils/superkaramba/examples/template.py template.py] or the [http://websvn.kde.org/trunk/KDE/kdeutils/superkaramba/examples/template.rb template.rb] or the [http://websvn.kde.org/trunk/KDE/kdeutils/superkaramba/examples/template.js template.js] template script file, depending on what scripting language you like to use. Download this file to where your theme file is. Now rename those just downloaded template file from template.py to mytheme.py or from template.rb to mytheme.rb where mytheme is the name of your theme. For example, if you wrote a coolbar.theme and like to use use Python, renamed the just downloaded template.py to coolbar.py and put it in the same directory as coolbar.theme.
 
Now, lets open up your coolbar.py (or coolbar.rb or whatever you named it to) scripting file. Look for the event callback that reacts to mouse movement. If you chose Python as your prefered scripting language, it may look like this:
 
<syntaxhighlight lang="python">
#This gets called everytime our widget is clicked.
#Notes
#  widget = reference to our widget
#  x = x position (relative to our widget)
#  y = y position (relative to our widget)
#  botton = button being held:
#      0 = No Mouse Button
#      1 = Left Mouse Button
#      2 = Middle Mouse Button
#      3 = Right Mouse Button, but this will never happen
#          because the right mouse button brings up the
#          Karamba menu.
def widgetMouseMoved(widget, x, y, button):
    #Warning: Don't do anything too intensive here since you don't
    #want to run complex piece of code everytime the mouse moves.
    pass
</syntaxhighlight>
 
Notice how the template gives your instructions on what data each callback gives you. In this case, you can see that you will know the x and y co-ordinates of the mouse, the button being held down (if any), and a reference to your widget.
 
Delete the line that says pass and add in some code. This code will run whenever the mouse is moved over your widget. But you say, "What in the world am I supposed to write to make something happen?". Good question! Go on to the next section below!
 
====How to "make stuff happen" from your script file====
 
SuperKaramba has a powerful API (Application Programmer's Interface). This is just a fancy way of saying SuperKaramba has a bunch of special commands you can run from your Python or your Ruby scripting code to make something happen in your theme. All of the commands are listed in one big list:
* [[Projects/SuperKaramba/API|The SuperKaramba API (or Function List)]]
 
Here is how the callback might look after you add some code to it:
<syntaxhighlight lang="python">
def widgetMouseMoved(widget, x, y, button):
    myText = karamba.getThemeText(widget, "mouseText")
    karamba.changeText(widget, myText, "mouse at %s:%s" % (x,y))
</syntaxhighlight>
 
This Python scripting code simply changes the text called "mouseText" (which we defined in our theme file) to tell us the current position of the mouse.
 
How did I know what parameters karamba.changeText() and karamba.getThemeText() used? How did I even know those functions existed in the first place? Well, thats what the [[Projects/SuperKaramba/API|API]] tells you. The API and the template.py or template.rb script file basically contain everything you could ever need to know to write your own theme.
 
===Testing!===
To test your new Python or Ruby scripting code, just open your theme file in SuperKaramba.
 
You DO NOT open your .py or .rb file in SuperKaramba! Open the .theme file and if a matching .py or .rb file is found, it will be automatically loaded.
 
You DO NOT need to compile your scripting code. SuperKaramba will do that automatically and tell you about any errors. In fact, you can't run your .py or .rb file directly in regular python because it doesn't know anything about the karamba.* functions. Those functions only exist inside of SuperKaramba's Python and Ruby interpreters. But you are for sure able to just use SuperKaramba as your interpreter direct from within the commandline. Just start SuperKaramba with something like:
<syntaxhighlight lang="bash">
superkaramba ./coolbar.theme
</syntaxhighlight>
 
To ease the development, SuperKaramba will automatically reload your theme when you save your changes to the theme file or the script file. This allows you to see your modifications immediately without reloading the theme manually.
 
Have fun making themes!
 
==FAQ==
 
===SuperKaramba on KDE4===
 
While we were able to see a lot of rumours and speculations regarding SuperKaramba and it's role on KDE4, it's planned to continue to support SuperKaramba and it's unique feature-set, the bunches of themes that do already exist and those that will be written. We will stay backward-compatible and rocking stable, improve the feature-set as needed and wish you fun with the result: the next generation of SuperKaramba :)
 
===SuperKaramba and Plasma===
 
SuperKaramba runs as standalonea-application - just run the program "superkaramba" - as well as [[Projects/Plasma|Plasma]] Applet embedded into the Plasma Workspace. Also SuperKaramba provides full and transparent access to the Plasma::DataEngine's with it's sensor functionality.
 
See also the [[Development/Tutorials/SuperKaramba#Plasma|Plasma script samples]] section.
 
===Scripting Interpreter Backends===
 
SuperKaramba uses the [[Development/Languages/Kross|Kross]] scripting framework to provide scripting with Python, Ruby and JavaScript as described at the [[Development/Tutorials/Kross/Introduction#The_Interpreter_plugins|Interpreter plugins]] section.
 
===Defining Python Source Code Encodings===
 
As described at the Python Enhancement Proposal [http://www.python.org/peps/pep-0263.html Defining Python Source Code Encodings] it is recommed to start a python script file with following both lines:
 
<syntaxhighlight lang="python">
#!/usr/bin/env superkaramba
# coding: utf-8
</syntaxhighlight>
 
The first line is the shebang that causes Unix-like operating systems to execute the script file using the SuperKaramba interpreter. You are then able to start the karamba script with;
<syntaxhighlight lang="text">
chmod 755 myKaramba.py
./myKaramba.py
</syntaxhighlight>
The second line defines that the script file is encoded with utf-8 or whatever the script file is actualy encoded in. This seems to be needed at least with Python >=2.5.


==Examples==
==Script Samples==


===A clock sample with Ruby and Python===
===A clock with Ruby, Python and JavaScript===


Let's take a look at one of them, the  
Let's take a look at one of them, the  
Line 22: Line 192:
theme written in Ruby. The theme just displays the current time in a RichText widget.
theme written in Ruby. The theme just displays the current time in a RichText widget.


<pre>
<syntaxhighlight lang="ruby">
require 'karamba'
require 'karamba'


Line 34: Line 204:


def widgetUpdated(widget)
def widgetUpdated(widget)
    puts  >> widgetUpdated"
     Karamba.changeRichText(widget, @richtext, Time.now.to_s)
     Karamba.changeRichText(widget, @richtext, Time.now.to_s)
     Karamba.redrawWidget(widget)
     Karamba.redrawWidget(widget)
end
end
</pre>
</syntaxhighlight>


The initWidget method will be called once if the widget got initialized. Here we setup the RichText widget where we display the current time in. The widgetUpdated will be called each second once (the interval is defined in the [http://websvn.kde.org/trunk/KDE/kdeutils/superkaramba/examples/rubyclock/clock.theme?view=markup clock.theme] themefile) and just updates the text display in the RichText widget with the new time.
The initWidget method will be called once if the widget got initialized. Here we setup the RichText widget where we display the current time in. The widgetUpdated will be called each second once (the interval is defined in the [http://websvn.kde.org/trunk/KDE/kdeutils/superkaramba/examples/rubyclock/clock.theme?view=markup clock.theme] themefile) and just updates the text display in the RichText widget with the new time.


Let's take a look at another theme. The [http://websvn.kde.org/trunk/KDE/kdeutils/superkaramba/examples/text/text.py?view=markup text.py] theme written in Python just displays some text widgets. We take this is example to create our own script, that does the same as the [http://websvn.kde.org/trunk/KDE/kdeutils/superkaramba/examples/rubyclock/clock.rb?view=markup clock.rb] above, that is to display the current time within a text widget.
Let's take a look at another theme. The [http://websvn.kde.org/trunk/KDE/kdeutils/superkaramba/examples/text/text.py?view=markup text.py] theme written in Python just displays some text widgets. We take this as example to create our own script, that does the same as the [http://websvn.kde.org/trunk/KDE/kdeutils/superkaramba/examples/rubyclock/clock.rb?view=markup clock.rb] above, that is to display the current time within a text widget.


<pre>
<syntaxhighlight lang="python">
import karamba, time
import karamba, time


text = None
text = None
def getTime():
    return time.strftime("%Y-%M-%d %H:%M.%S")


def initWidget(widget):
def initWidget(widget):
     text = karamba.createText(widget, 0, 20, 200, 20, "Text meter")
    global text
     text = karamba.createText(widget, 10, 10, 200, 120, getTime())
    karamba.moveText(widget, richtext, 10, 10)
    karamba.redrawWidget(widget)


def widgetUpdated(widget):
def widgetUpdated(widget):
     t = time.strftime("%Y-%M-%d %H:%M.%S")
     global text
     karamba.changeText(widget, text, t)
     karamba.changeText(widget, text, getTime())
</pre>
    karamba.redrawWidget(widget)
</syntaxhighlight>


In the initWidget method we create our text widget that is updated once per second (or per interval as defined in the matching theme file) at the widgetUpdated method to display the new current time.
In the initWidget method we create our text widget that is updated once per second (or per interval as defined in the matching theme file) at the widgetUpdated method to display the new current time.


===Forms===
The [http://websvn.kde.org/trunk/KDE/kdeutils/superkaramba/examples/JavaScriptClock/clock.js?view=markup clock.js] sample provides us with the same sample written using the JavaScript language.
 
There we have the currentTime() function that returns the current time as string. That function is used within the initWidget() and the widgetUpdated() functions to display the current time within a textwidget.
<syntaxhighlight lang="javascript">
var text = 0;
 
function currentTime() {
    var now = new Date();
    var hours = now.getHours();
    hours = hours < 10 ? '0' + hours : hours;
    var mins = now.getMinutes();
    mins = mins < 10 ? '0' + mins : mins;
    var secs = now.getSeconds();
    secs = secs < 10 ? '0' + secs : secs;
    return hours + ':' + mins + '.' + secs;
}
 
function initWidget(widget) {
    text = karamba.createText(widget,0,20,200,20,currentTime());
}
 
function widgetUpdated(widget) {
    karamba.changeText(widget,text,currentTime());
    karamba.redrawWidget(widget)
}
</syntaxhighlight>
 
See also...
* [http://www.kde-look.org/index.php?xcontentmode=38 Themes]
* [http://www.biodesign.com.ar/blog/?cat=2 More Themes]
 
===Forms and Modules===


To display just a simple messagebox or load widgets from a UI-file the Kross [http://websvn.kde.org/trunk/KDE/kdelibs/kross/modules/form.h?view=markup forms] module can be also used within all supported scripting languages.
Modules are libraries loaded on demand provided by Kross. One of them is the [http://websvn.kde.org/trunk/KDE/kdelibs/kross/modules/form.h?view=markup forms] module that implements some basic dialog and widget functionality. To display just a simple messagebox or load widgets from a UI-file those module can be used within all supported scripting languages.


The following sample Python script demonstrates how to display a messagebox.
The following sample Python script demonstrates how to display a messagebox.


<pre>
<syntaxhighlight lang="python">
import karamba
import karamba, Kross


def widgetClicked(widget, x, y, button):
def widgetClicked(widget, x, y, button):
    import Kross
     forms = Kross.module("forms")
     forms = Kross.module("forms")
     if button == 1: #left
     if button == 1: #left
         forms.showMessageBox("Information",
         forms.showMessageBox("Information","Caption","Message")
            "The Caption","The Message")
     elif button == 2: #middle
     elif button == 2: #middle
         forms.showMessageBox("Error",
         forms.showMessageBox("Error","Caption","Message")
            "The Caption","The Message")
</syntaxhighlight>
</pre>


While the next sample Python script displays a dialog with an embedded "Open File" widget.
While the next sample Python script displays a dialog with an embedded "Open File" widget.


<pre>
<syntaxhighlight lang="python">
import karamba
import karamba, Kross


def widgetClicked(widget, x, y, button):
def widgetClicked(widget, x, y, button):
    import Kross
     forms = Kross.module("forms")
     forms = Kross.module("forms")
     dialog = forms.createDialog("MyDialog")
     dialog = forms.createDialog("MyDialog")
     dialog.setButtons("Ok|Cancel")
     dialog.setButtons("Ok|Cancel")
     openpage = dialog.addPage(
     openpage = dialog.addPage("Open","Open File","fileopen")
        "Open","Open File","fileopen")
     openwidget = forms.createFileWidget(
     openwidget = forms.createFileWidget(
         openpage, "kfiledialog:///openfile")
         openpage, "kfiledialog:///openfile")
     openwidget.setMode("Opening")
     openwidget.setMode("Opening")
     openwidget.setFilter(
     openwidget.setFilter("*.txt|Text Files\n*|All Files")
        "*.txt|Text Files\n*|All Files")
     result = dialog.exec_loop()
     result = dialog.exec_loop()
     if result:
     if result:
         print openwidget.selectedFile()
         print openwidget.selectedFile()
</pre>
</syntaxhighlight>
 
For sure you are also able to use those handy UI-files the QtDesigner produces like demonstrated on the sample below.
 
<syntaxhighlight lang="python">
import karamba, os, Kross
 
def widgetClicked(widget, x, y, button):
    forms = Kross.module("forms")
    dialog = forms.createDialog("Sample")
    infospage = dialog.addPage("Title","Caption","iconname")
    # the ui-file is in the same directory our script is in
    uifile = os.path.join(karamba.getThemePath(),"my.ui")
    infoswidget = forms.createWidgetFromUIFile(infospage, uifile)
    if dialog.exec_loop():
        # here we print the content of the in the ui-file defined
        # lineedit-widget that has the name MyLineEdit
        print infoswidget["MyLineEdit"].text
</syntaxhighlight>
 
===Embed KHTML Webbrowser===
 
Starting with Qt 4.4 and KDE 4.1 SuperKaramba is able to embed any kind of QWidgets into the canvas that is used to display something. That means, you are able to add a QLabel, a QPushButton or any other widget just direct into your Karambas.
 
The following python sample code, which is also available as [http://websvn.kde.org/trunk/KDE/kdeutils/superkaramba/examples/WebBrowser/webbrowser.py?view=markup webbrowser.py], demonstrates this by embedding a full webbrowser into your Karamba by using the [[Projects/SuperKaramba/API#CanvasWidget|CanvasWidget]] functionality.
 
<syntaxhighlight lang="python">
#!/usr/bin/env superkaramba
# coding: utf-8
 
import karamba, Kross
khtml_part = None
 
def initWidget(widget):
    # the Kross forms module does provide methods to create
    # widgets for us. You are for example able to load a UI
    # file, create layouts or just any other supported widget.
    forms = Kross.module("forms")
    frame = forms.createWidget("QWidget")
    forms.createLayout(frame, "QHBoxLayout")
    # here we load the KHTML KPart that is our full powered
    # webbrowser widget.
    khtml_part = forms.loadPart(frame, "libkhtmlpart")
    khtml_part.javaScriptEnabled = True
    khtml_part.javaEnabled = False
    khtml_part.pluginsEnabled = False
    # now we add the frame to our widget and will earn a
    # proxywidget for it.
    proxywidget = karamba.createCanvasWidget(widget, frame)
    # the following lines demonstrate, how to control the
    # webbrowser using the JavaScript language. The KHTML
    # KPart does provide different signals we are able to
    # connect our own functions too. Just see here the
    # kdelibs/khtml/khtmlpart.h file.
    def selectionChanged():
        print khtml_part.executeScript("window.scrollBy(0,50);");
    khtml_part.connect("selectionChanged()", selectionChanged)
    # and finally we like to load an url and display something.
    khtml_part.openUrl("http://www.kde.org")
 
def widgetClosed(widget):
    # if the job is done, we may like to free our KPart again.
    if khtml_part:
        khtml_part.delayedDestruct()
</syntaxhighlight>


===Connect with KSpread===
===KSpread===


The following sample uses KSpread to read a OpenDocument spreadsheet file and to display it within a table.
The following sample uses KSpread to read a OpenDocument spreadsheet file and to display it within a table.


<pre>
For this we are loading the [[Development/Tutorials/KSpread_Scripting|KSpread Scripting]] library and control it. Compared to dbus we don't need a running KSpread instance but just load and use the library direct (so, you still need to have KSpread installed).
import karamba
 
While the sample is written in Python, the other supported backends like Ruby are able to access the whole same rich API.
 
<syntaxhighlight lang="python">
import karamba, Kross


# The OpenDocument spreadsheet file to read.
# The OpenDocument spreadsheet file to read.
filename = "/home/kde4/kspreaddocument.ods"
filename = "/home/kde4/kspreaddocument.ods"


#this is called when your widget is initialized
# This is called when your widget is initialized
def initWidget(widget):
def initWidget(widget):
     # Import Kross and fetch the KSpread module.
     # Fetch the KSpread module.
    import Kross
     kspread = Kross.module("kspread")
     kspread = Kross.module("kspread")
     if not kspread:
     if not kspread:
Line 121: Line 389:
     if not kspread.openUrl(filename):
     if not kspread.openUrl(filename):
         raise "Failed to open %s" % filename
         raise "Failed to open %s" % filename
     # Get the sheet we like to export to the CSV file.
     # Get the sheet we like to display.
     sheet = kspread.sheetByName( kspread.sheetNames()[0] )
     sheet = kspread.sheetByName( kspread.sheetNames()[0] )
     text = "<table>\n"
     text = "<table>\n"
Line 140: Line 408:
     text += "</table>\n"
     text += "</table>\n"


    # Create the richtext widget to display the text.
     richtext = karamba.createRichText(widget, text)
     richtext = karamba.createRichText(widget, text)
     karamba.moveRichText(widget, richtext, 10, 10)
     karamba.moveRichText(widget, richtext, 10, 10)
     karamba.setRichTextWidth(widget, richtext, 345)
     karamba.setRichTextWidth(widget, richtext, 345)
     karamba.redrawWidget(widget)
     karamba.redrawWidget(widget)
</pre>
</syntaxhighlight>
 
See also...
* [[Development/Tutorials/KWord_Scripting|KWord Scripting]]
* [[Development/Tutorials/KSpread_Scripting|KSpread Scripting]]
* [[Development/Tutorials/Krita_Scripting|Krita Scripting]]
 
===PyQt with Python===
 
The following python script shows how to use [http://www.riverbankcomputing.co.uk/pyqt/ PyQt] which provides the nice Qt-API pythonized. The sample just displays a "hello world" dialog if you click on the widget.
 
<syntaxhighlight lang="python">
import karamba
from PyQt4 import QtCore, QtGui
 
def initWidget(widget):
    karamba.resizeWidget(widget, 200, 200)
 
def widgetClicked(widget, x, y, button):
    class Dialog(QtGui.QDialog):
        def __init__(self):
            QtGui.QWidget.__init__(self)
            label = QtGui.QLabel(self)
            label.setText("Hello World")
    dialog = Dialog()
    dialog.exec_()
</syntaxhighlight>
 
See also...
* [http://www.riverbankcomputing.com/Docs/PyQt4/pyqt4ref.html PyQt4 Reference Guide]
* [[Development/Languages/Python|Python Development Language]]


===TkInter with Python===
===TkInter with Python===


While you are also able to use [http://www.riverbankcomputing.co.uk/pyqt/ PyQt4] which doesn't look only nicer but also provides the nice Qt-API pythonized, you are also able to use the default toolkit Python comes with, that is TkInter.
You are also able to use the default toolkit Python comes with, that is TkInter. The following sample just displays a TkInter "hello world" dialog if you click on the widget.


The following sample just displays a TkInter "hello world" dialog if you click on the widget.
<syntaxhighlight lang="python">
 
<pre>
import karamba
import karamba
from Tkinter import *


def widgetClicked(widget, x, y, button):
def widgetClicked(widget, x, y, button):
    from Tkinter import *
     root = Tk()
     root = Tk()
     w = Label(root, text="Hello, world!")
     w = Label(root, text="Hello, world!")
     w.pack()
     w.pack()
     root.mainloop()
     root.mainloop()
</pre>
</syntaxhighlight>
 
See also...
* [http://docs.python.org/lib/module-Tkinter.html Tkinter, Python interface to Tcl/Tk]
* [http://wiki.python.org/moin/TkInter Python Tkinter]
* [http://www.pythonware.com/library/tkinter/introduction/ An Introduction to Tkinter]


===QtRuby/Korundum with Ruby===
===QtRuby/Korundum with Ruby===
Line 167: Line 469:
The following sample script written in Ruby demonstrates that you are able to use [http://rubyforge.org/projects/korundum/ QtRuby/Korundum] within your Ruby scripts.
The following sample script written in Ruby demonstrates that you are able to use [http://rubyforge.org/projects/korundum/ QtRuby/Korundum] within your Ruby scripts.


<pre>
<syntaxhighlight lang="ruby">
require 'karamba'
require 'karamba'
require 'Qt'
require 'Qt'
Line 182: Line 484:
     dialog.exec
     dialog.exec
end
end
</pre>
</syntaxhighlight>


The script does implement only the widgetClicked function that got called if the user clicks on the widget. What we do within that function is to create an instance of the Dialog class that implements a QDialog using QtRuby and then execute that modal dialog.
The script does implement only the widgetClicked function that got called if the user clicks on the widget. What we do within that function is to create an instance of the Dialog class that implements a QDialog using QtRuby and then execute that modal dialog.
See also...
* [[Development/Tutorials/Qt4_Ruby_Tutorial|Qt4 Ruby Tutorial]]
* [[Development/Languages/Ruby|Ruby Development Language]]
===D-Bus===
The following sample script written in Python just prints a list with services available via [http://dbus.freedesktop.org/ D-Bus];
<syntaxhighlight lang="python">
import karamba, dbus
def initWidget(widget):
    bus = dbus.SystemBus()
    dbus_object = bus.get_object(
        'org.freedesktop.DBus','/org/freedesktop/DBus')
    dbus_iface = dbus.Interface(
        dbus_object, 'org.freedesktop.DBus')
    services = dbus_iface.ListNames()
    text = "".join( ["%s<br>" % s for s in services] )
    karamba.resizeWidget(widget, 520, 520)
    richtext = karamba.createRichText(widget, text)
    karamba.moveRichText(widget, richtext, 10, 10)
    karamba.setRichTextWidth(widget, richtext, 500)
    karamba.redrawWidget(widget)
</syntaxhighlight>
The following Python script uses [http://dbus.freedesktop.org/ D-Bus] to display a list with all the devices accessible with [http://www.freedesktop.org/wiki/Software/hal HAL];
<syntaxhighlight lang="python">
import karamba, dbus
def initWidget(widget):
    bus = dbus.SystemBus()
    dbus_object = bus.get_object(
        'org.freedesktop.Hal','/org/freedesktop/Hal/Manager')
    dbus_iface = dbus.Interface(
        dbus_object, 'org.freedesktop.Hal.Manager')
    devices = dbus_iface.GetAllDevices()
    text = "".join( ["%s<br>" % d for d in devices] )
    karamba.resizeWidget(widget, 520, 520)
    richtext = karamba.createRichText(widget, text)
    karamba.moveRichText(widget, richtext, 10, 10)
    karamba.setRichTextWidth(widget, richtext, 500)
    karamba.redrawWidget(widget)
</syntaxhighlight>
While those both samples are going the most easy way to just synchronously call dbus functionality, you are also able to asynchronously call methods or just write a dbus-server by using the with python dbus supported Qt main event-loop as shown in the dbus-python tutorial below - but don't wonder that the samples printed there may not run since it seems the dbus-python module got largely refactored while the with it shipped documentation did not :-/
See also...
* [http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html dbus-python tutorial]
* [https://trac.luon.net/data/ruby-dbus/tutorial Ruby D-Bus Tutorial]
* [http://webcvs.freedesktop.org/hal/hal/doc/spec/hal-spec.html?view=co HAL Specification]
===Plasma===
Following scripts are showing how to use the Plasma functionality in SuperKaramba.
See also...
* [[Projects/Plasma|Plasma Project]]
* [http://kross.dipe.org/skapplet.png screenshot]
* [http://kross.dipe.org/skapplet.mpeg screencast], 2.4MB, mpeg4
* [http://www.kdedevelopers.org/node/2907 SuperKaramba and Plasma, Part 2]
* [http://www.kdedevelopers.org/node/3305 SuperKaramba and Plasma Packages]
* [http://websvn.kde.org/trunk/KDE/kdeutils/superkaramba/examples/plasmaAppletClock/ AppletClock sample]
====Plasma::DataEngine as SuperKaramba Sensor====
Following Python sample demonstrates usage of the Plasma::DataEngine functionality by using the TimeEngine to display the current local time.
<syntaxhighlight lang="python">
import karamba
def initWidget(widget):
    karamba.resizeWidget(widget, 400, 400)
    text = karamba.createText(widget, 10, 10, 380, 380)
    engine = karamba.getPlasmaSensor(widget, "time", "Local")
    connector = engine.connectSource("Local", text)
    connector.setFormat('%Date %Time')
</syntaxhighlight>
You are also able to provide an own handle that defines what should be done if the Plasma::DataEngine has new or updated data for an widget or a meter.
<syntaxhighlight lang="python">
# Import the SuperKaramba module.
import karamba
# This method got called by SuperKaramba on initialization.
def initWidget(widget):
    karamba.resizeWidget(widget, 400, 400)
    # Create the text-widget (aka meter).
    text = karamba.createText(widget, 10, 10, 380, 380)
    # Create the Plasma::DataEngine sensor.
    engine = karamba.getPlasmaSensor(widget, "time", "Local")
    # Connect the text-widget with the engine.
    connector = engine.connectSource("Local", text)
    # An own handler for the connector.
    def func(source,data):
        # Only display the current time but not the data["Date"].
        karamba.changeText(widget, text, data["Time"])
    # Set empty source else the sourceUpdated() signal will not
    # be emitted.
    connector.setSource("")
    # Connect the sourceUpdated() signal with the handler.
    connector.connect("sourceUpdated(QString,QVariantMap)",func)
</syntaxhighlight>
====Plasma in a SuperKaramba Theme====
And here is the shortest version that needs only one single line of text. Just put following line into a SuperKaramba theme file;
<syntaxhighlight lang="text">
text x=0 y=0 sensor=plasma engine="time" source="Local" format="%Date %Time" poperties="reportSeconds:true,someOtherIntProperty:815"
</syntaxhighlight>
====Plasma and SuperKaramba====
Now lets take a more detailed look at a longer sample that uses SuperKaramba and the PlasmaApplet module. While the sample above runs in all cases, the following sample does not cause the used PlasmaApplet module will be only available if SuperKaramba is running as Plasma Applet. If you are running SuperKaramba as standalone application this wan't work (is there interest for this? - but both ways provide the same rich API functionality to work with Plasma::DataEngine's anyway).
<syntaxhighlight lang="python">
# Import the needed modules.
import karamba, PlasmaApplet
# Fetch the TimeEngine Plasma::DataEngine object.
engine = PlasmaApplet.dataEngine("time")
# We like to update it each second.
engine.setProperty("reportSeconds", True)
# Following lines are here to demonstrate that we are also able
# to dynamic handle the sources. It's not needed for this sample.
#def sourceAdded(source):
#    print "sourceAdded '%s'" % source
#def sourceRemoved(source):
#    print "sourceRemoved '%s'" % source
#def sourceUpdated(source,data):
#    print "sourceUpdated '%s' '%s'" % (source,data)
#engine.connect("sourceAdded(QString)", sourceAdded)
#engine.connect("sourceRemoved(QString)", sourceRemoved)
#engine.connect("sourceUpdated(QString,QVariantMap)", sourceUpdated)
# Connect with the TimeEngine's source named "Local"
engine.connectSource("Local")
# This is the richedit widget we use to display something.
text = None
# This method just returns some text to display.
def getText():
    t = "name: %s\n" % PlasmaApplet.name()
    t += "category: %s\n" % PlasmaApplet.category()
    t += "boundingRect: %s\n" % PlasmaApplet.boundingRect()
    t += "sources: %s\n" % engine.sources()
    t += "data: %s\n" % engine.query("Local")
    return t
# This method got called by SuperKaramba on initialization.
def initWidget(widget):
    global text
    karamba.resizeWidget(widget, 400, 400)
    text = karamba.createText(widget, 10, 10, 380, 380, getText())
    karamba.redrawWidget(widget)
# This method got called by SuperKaramba on update.
def widgetUpdated(widget):
    global text
    karamba.changeText(widget, text, getText())
    karamba.redrawWidget(widget)
</syntaxhighlight>
Following does the same as the sample above but connects the time Plasma::DataEngine with a meter-widget of SuperKaramba. The script will run once and compared to the sample above it's not needed to provide a widgetUpdated function but SuperKaramba will take care of updating the meter if the time Plasma::DataEngine provides new/updated data.
<syntaxhighlight lang="python">
# Import the needed modules.
import karamba, PlasmaApplet
# Fetch the TimeEngine Plasma::DataEngine object.
engine = PlasmaApplet.dataEngine("time")
# We like to update it each second.
engine.setProperty("reportSeconds", True)
# This method got called by SuperKaramba on initialization.
def initWidget(widget):
    global engine
    karamba.resizeWidget(widget, 400, 400)
    # Create the text-meter widget.
    text = karamba.createText(widget, 10, 10, 380, 380, "")
    # Connect the dataengine with the text-meter.
    connector = engine.connectSource("Local", text)
    # Set the formatting. This is how the data should be
    # displayed. The local time-engine source provides the
    # '%Date' and the '%Time' values.
    connector.setFormat('Date: %Date\nTime: %Time')
   
    # Following lines are here to demonstrate that you are also
    # able to implement an own handler for the connector.
    #def func(source,data):
    #    karamba.changeText(widget, text, data["Time"])
    # Set empty source else the sourceUpdated() signal will not
    # be emitted.
    #connector.setSource("")
    # Connect the sourceUpdated() signal with the handler.
    #connector.connect("sourceUpdated(QString,QVariantMap)",func)
   
    karamba.redrawWidget(widget)
</syntaxhighlight>

Latest revision as of 20:56, 29 June 2011

SuperKaramba

Intro

SuperKaramba is a tool that allows one to easily create functionality enhancement modules on a KDE desktop. Such modules are interactive programs written in Python, Ruby or KDE JavaScript that are usually embedded directly into the background and do not disturb the normal view of the desktop.

Links

This screenshot shows the Aero AIO Karamba Theme running on KDE4 as Plasmoid (aka Plasma Applet).

Commands, Meters and Sensors

SuperKaramba does differ between 3 kind of elements. Commands define something that should be done (actions), meters are used to display something (GUI widgets) while sensors are to get something (data delivery).

  • A list of General Commands that you can put in your theme file. This includes tags that define the widget shape, set default fonts, and create click-areas that launch programs.
  • A list of Meters that you can put in your theme file. This includes meters display the values of sensors. Meters can auto-update.
  • A list of Sensors that you can put in your theme file. Sensors allow you to display system properties automatically. There are sensors to display the status of everything from memory use to the results of shell scripts. See here also the Plasma::DataEngine as SuperKaramba Sensor chapter.

Basic Tutorial for Theme Creators

Themes in SuperKaramba consist of two parts. A theme file that defines the shape and size of the theme, and possibly many other things and a script file that adds interactivity to the theme. Basically, whenever an event happens, such as the user clicking the mouse on your theme, a function is called in your script file to let you react to the event.

Creating the Theme File

The theme files, those files with the fileextension *.theme, are text files that contain layout information and information about the values that will be shown.

Theme File Samples

The simplest form of Karamba is to load a file containing the word karamba. This will give you a empty transparent window with the size 300x300. A better idea is to enter something like this in a file:

karamba x=30 y=30 w=400 h=200 interval=2000

This will bring up a window at 30,30 with the size 400x200, and the window will refresh every 2000 milliseconds = 2 seconds.

The graphics elements of Karamba are called meters. The meters can be connected to sensors. A sensor can get, e.g., the current CPU load, the uptime, the date, and a lot of other things. It is also possible to display the output of an external program in a meter.

Creating a meter is not that hard. A text label can be created by putting this line in the file:

text x=10 y=10 value="This is a test"

To add some more spice we connect a sensor to a meter:

text x=15 y=25 sensor=uptime

A graph that logs the CPU load can be made like this:

graph x=10 y=40 w=200 h=60 sensor=cpu 

Adding an image (the path is either relative to the configuration file or an absolute path):

image x=10 y=270 path="picture.png"

An introduction to the format parameter for a text meter:

text x=10 y=330 sensor=memory format="You have %tm MB memory"

Using of a the "time" Plasma::DataEngine:

text x=0 y=0 sensor=plasma engine="time" source="Local" format="%Date %Time"

Please note that only one meter per line in the configuration file is allowed. No spaces are allowed around the '=' characters.

Notes

In Karamba all the graphic elements inside one configuration file have the same refresh rate. So it could be wise to split up a configuration into smaller parts, if different parts need different refresh times. An area showing a log file, for example, probably does not need the same refresh rate as an area showing the CPU load. The refresh rate of the sensors can be set individually. Also Karamba does not work with backgrounds without a picture, i.e., solid color, gradient, or pattern. One can get around this bug by creating a transparent PNG image (the size can be 1x1 pixel) and selecting this as the background picture.

Creating the Script File

The script file adds interactivity to the theme and can be written using the Python, Ruby or the KDE JavaScript language.

Super-Quick Start

  1. Make a theme file like described at the "Creating the Theme File" chapter above.
  2. Download the template.py or the template.rb or the template.js script file, depending on what scripting language you like to use, and rename this file to match your theme file.
  3. Fill in all the callbacks in the script file that you want to handle. The template files just contain all callbacks, but you can safly remove those you don't need or just leave them in.
  4. Use the API Reference to know what commands to use to fill in the callbacks.
  5. Finally save your script file and open your theme file in SuperKaramba to test it!

Regular Guide for "Mere Mortals"

Now lets add interactivity to your theme file. If you have not written a theme file yet, you need to do that first like described at the "Creating the Theme File" chapter above. It will define the size, location on screen, and other basic elements of your theme.

Event-based Programming

Now that you have a basic theme file, load it with SuperKaramba. If there is nothing defined in the theme file other than size and location, you will have made an empty, transparent square that might be hard to find on the desktop. Let's assume you have made a basic theme that just shows a few images in small area. To make your theme do anything interesting, it needs to be able to react to events.

Basically, an event is anything that can happen that SuperKaramba knows how to handle. Some examples are: the user moves or clicks the mouse, a new program is started on the desktop, a certain amount of time has passed, etc. So to make your theme do something, you add Python or Ruby scripting code to the event callback that corresponds to the event to which you want to react. This is not nearly as hard as it sounds.

First, you need the template.py or the template.rb or the template.js template script file, depending on what scripting language you like to use. Download this file to where your theme file is. Now rename those just downloaded template file from template.py to mytheme.py or from template.rb to mytheme.rb where mytheme is the name of your theme. For example, if you wrote a coolbar.theme and like to use use Python, renamed the just downloaded template.py to coolbar.py and put it in the same directory as coolbar.theme.

Now, lets open up your coolbar.py (or coolbar.rb or whatever you named it to) scripting file. Look for the event callback that reacts to mouse movement. If you chose Python as your prefered scripting language, it may look like this:

#This gets called everytime our widget is clicked.
#Notes
#  widget = reference to our widget
#  x = x position (relative to our widget)
#  y = y position (relative to our widget)
#  botton = button being held:
#      0 = No Mouse Button
#      1 = Left Mouse Button
#      2 = Middle Mouse Button
#      3 = Right Mouse Button, but this will never happen
#          because the right mouse button brings up the
#          Karamba menu.
def widgetMouseMoved(widget, x, y, button):
    #Warning: Don't do anything too intensive here since you don't
    #want to run complex piece of code everytime the mouse moves.
    pass

Notice how the template gives your instructions on what data each callback gives you. In this case, you can see that you will know the x and y co-ordinates of the mouse, the button being held down (if any), and a reference to your widget.

Delete the line that says pass and add in some code. This code will run whenever the mouse is moved over your widget. But you say, "What in the world am I supposed to write to make something happen?". Good question! Go on to the next section below!

How to "make stuff happen" from your script file

SuperKaramba has a powerful API (Application Programmer's Interface). This is just a fancy way of saying SuperKaramba has a bunch of special commands you can run from your Python or your Ruby scripting code to make something happen in your theme. All of the commands are listed in one big list:

Here is how the callback might look after you add some code to it:

def widgetMouseMoved(widget, x, y, button):
    myText = karamba.getThemeText(widget, "mouseText")
    karamba.changeText(widget, myText, "mouse at %s:%s" % (x,y))

This Python scripting code simply changes the text called "mouseText" (which we defined in our theme file) to tell us the current position of the mouse.

How did I know what parameters karamba.changeText() and karamba.getThemeText() used? How did I even know those functions existed in the first place? Well, thats what the API tells you. The API and the template.py or template.rb script file basically contain everything you could ever need to know to write your own theme.

Testing!

To test your new Python or Ruby scripting code, just open your theme file in SuperKaramba.

You DO NOT open your .py or .rb file in SuperKaramba! Open the .theme file and if a matching .py or .rb file is found, it will be automatically loaded.

You DO NOT need to compile your scripting code. SuperKaramba will do that automatically and tell you about any errors. In fact, you can't run your .py or .rb file directly in regular python because it doesn't know anything about the karamba.* functions. Those functions only exist inside of SuperKaramba's Python and Ruby interpreters. But you are for sure able to just use SuperKaramba as your interpreter direct from within the commandline. Just start SuperKaramba with something like:

superkaramba ./coolbar.theme

To ease the development, SuperKaramba will automatically reload your theme when you save your changes to the theme file or the script file. This allows you to see your modifications immediately without reloading the theme manually.

Have fun making themes!

FAQ

SuperKaramba on KDE4

While we were able to see a lot of rumours and speculations regarding SuperKaramba and it's role on KDE4, it's planned to continue to support SuperKaramba and it's unique feature-set, the bunches of themes that do already exist and those that will be written. We will stay backward-compatible and rocking stable, improve the feature-set as needed and wish you fun with the result: the next generation of SuperKaramba :)

SuperKaramba and Plasma

SuperKaramba runs as standalonea-application - just run the program "superkaramba" - as well as Plasma Applet embedded into the Plasma Workspace. Also SuperKaramba provides full and transparent access to the Plasma::DataEngine's with it's sensor functionality.

See also the Plasma script samples section.

Scripting Interpreter Backends

SuperKaramba uses the Kross scripting framework to provide scripting with Python, Ruby and JavaScript as described at the Interpreter plugins section.

Defining Python Source Code Encodings

As described at the Python Enhancement Proposal Defining Python Source Code Encodings it is recommed to start a python script file with following both lines:

#!/usr/bin/env superkaramba
# coding: utf-8

The first line is the shebang that causes Unix-like operating systems to execute the script file using the SuperKaramba interpreter. You are then able to start the karamba script with;

chmod 755 myKaramba.py
./myKaramba.py

The second line defines that the script file is encoded with utf-8 or whatever the script file is actualy encoded in. This seems to be needed at least with Python >=2.5.

Script Samples

A clock with Ruby, Python and JavaScript

Let's take a look at one of them, the clock.rb theme written in Ruby. The theme just displays the current time in a RichText widget.

require 'karamba'

def initWidget(widget)
    Karamba.resizeWidget(widget, 300, 120)
    @richtext = Karamba.createRichText(widget, Time.now.to_s)
    Karamba.moveRichText(widget, @richtext, 10, 10)
    Karamba.setRichTextWidth(widget, @richtext, 280)
    Karamba.redrawWidget(widget)
end

def widgetUpdated(widget)
    Karamba.changeRichText(widget, @richtext, Time.now.to_s)
    Karamba.redrawWidget(widget)
end

The initWidget method will be called once if the widget got initialized. Here we setup the RichText widget where we display the current time in. The widgetUpdated will be called each second once (the interval is defined in the clock.theme themefile) and just updates the text display in the RichText widget with the new time.

Let's take a look at another theme. The text.py theme written in Python just displays some text widgets. We take this as example to create our own script, that does the same as the clock.rb above, that is to display the current time within a text widget.

import karamba, time

text = None

def getTime():
    return time.strftime("%Y-%M-%d %H:%M.%S")

def initWidget(widget):
    global text
    text = karamba.createText(widget, 10, 10, 200, 120, getTime())
    karamba.moveText(widget, richtext, 10, 10)
    karamba.redrawWidget(widget)

def widgetUpdated(widget):
    global text
    karamba.changeText(widget, text, getTime())
    karamba.redrawWidget(widget)

In the initWidget method we create our text widget that is updated once per second (or per interval as defined in the matching theme file) at the widgetUpdated method to display the new current time.

The clock.js sample provides us with the same sample written using the JavaScript language.

There we have the currentTime() function that returns the current time as string. That function is used within the initWidget() and the widgetUpdated() functions to display the current time within a textwidget.

var text = 0;

function currentTime() {
    var now = new Date();
    var hours = now.getHours();
    hours = hours < 10 ? '0' + hours : hours;
    var mins = now.getMinutes();
    mins = mins < 10 ? '0' + mins : mins;
    var secs = now.getSeconds();
    secs = secs < 10 ? '0' + secs : secs;
    return hours + ':' + mins + '.' + secs;
}

function initWidget(widget) {
    text = karamba.createText(widget,0,20,200,20,currentTime());
}

function widgetUpdated(widget) {
    karamba.changeText(widget,text,currentTime());
    karamba.redrawWidget(widget)
}

See also...

Forms and Modules

Modules are libraries loaded on demand provided by Kross. One of them is the forms module that implements some basic dialog and widget functionality. To display just a simple messagebox or load widgets from a UI-file those module can be used within all supported scripting languages.

The following sample Python script demonstrates how to display a messagebox.

import karamba, Kross

def widgetClicked(widget, x, y, button):
    forms = Kross.module("forms")
    if button == 1: #left
        forms.showMessageBox("Information","Caption","Message")
    elif button == 2: #middle
        forms.showMessageBox("Error","Caption","Message")

While the next sample Python script displays a dialog with an embedded "Open File" widget.

import karamba, Kross

def widgetClicked(widget, x, y, button):
    forms = Kross.module("forms")
    dialog = forms.createDialog("MyDialog")
    dialog.setButtons("Ok|Cancel")
    openpage = dialog.addPage("Open","Open File","fileopen")
    openwidget = forms.createFileWidget(
        openpage, "kfiledialog:///openfile")
    openwidget.setMode("Opening")
    openwidget.setFilter("*.txt|Text Files\n*|All Files")
    result = dialog.exec_loop()
    if result:
        print openwidget.selectedFile()

For sure you are also able to use those handy UI-files the QtDesigner produces like demonstrated on the sample below.

import karamba, os, Kross

def widgetClicked(widget, x, y, button):
    forms = Kross.module("forms")
    dialog = forms.createDialog("Sample")
    infospage = dialog.addPage("Title","Caption","iconname")
    # the ui-file is in the same directory our script is in
    uifile = os.path.join(karamba.getThemePath(),"my.ui")
    infoswidget = forms.createWidgetFromUIFile(infospage, uifile)
    if dialog.exec_loop():
        # here we print the content of the in the ui-file defined
        # lineedit-widget that has the name MyLineEdit
        print infoswidget["MyLineEdit"].text

Embed KHTML Webbrowser

Starting with Qt 4.4 and KDE 4.1 SuperKaramba is able to embed any kind of QWidgets into the canvas that is used to display something. That means, you are able to add a QLabel, a QPushButton or any other widget just direct into your Karambas.

The following python sample code, which is also available as webbrowser.py, demonstrates this by embedding a full webbrowser into your Karamba by using the CanvasWidget functionality.

#!/usr/bin/env superkaramba
# coding: utf-8

import karamba, Kross
khtml_part = None

def initWidget(widget):
    # the Kross forms module does provide methods to create
    # widgets for us. You are for example able to load a UI
    # file, create layouts or just any other supported widget.
    forms = Kross.module("forms")
    frame = forms.createWidget("QWidget")
    forms.createLayout(frame, "QHBoxLayout")
    # here we load the KHTML KPart that is our full powered
    # webbrowser widget.
    khtml_part = forms.loadPart(frame, "libkhtmlpart")
    khtml_part.javaScriptEnabled = True
    khtml_part.javaEnabled = False
    khtml_part.pluginsEnabled = False
    # now we add the frame to our widget and will earn a
    # proxywidget for it.
    proxywidget = karamba.createCanvasWidget(widget, frame)
    # the following lines demonstrate, how to control the
    # webbrowser using the JavaScript language. The KHTML
    # KPart does provide different signals we are able to
    # connect our own functions too. Just see here the
    # kdelibs/khtml/khtmlpart.h file.
    def selectionChanged():
        print khtml_part.executeScript("window.scrollBy(0,50);");
    khtml_part.connect("selectionChanged()", selectionChanged)
    # and finally we like to load an url and display something.
    khtml_part.openUrl("http://www.kde.org")

def widgetClosed(widget):
    # if the job is done, we may like to free our KPart again.
    if khtml_part:
        khtml_part.delayedDestruct()

KSpread

The following sample uses KSpread to read a OpenDocument spreadsheet file and to display it within a table.

For this we are loading the KSpread Scripting library and control it. Compared to dbus we don't need a running KSpread instance but just load and use the library direct (so, you still need to have KSpread installed).

While the sample is written in Python, the other supported backends like Ruby are able to access the whole same rich API.

import karamba, Kross

# The OpenDocument spreadsheet file to read.
filename = "/home/kde4/kspreaddocument.ods"

# This is called when your widget is initialized
def initWidget(widget):
    # Fetch the KSpread module.
    kspread = Kross.module("kspread")
    if not kspread:
        raise "KSpread is not installed"
    # Try to open the file.
    if not kspread.openUrl(filename):
        raise "Failed to open %s" % filename
    # Get the sheet we like to display.
    sheet = kspread.sheetByName( kspread.sheetNames()[0] )
    text = "<table>\n"
    # Iterate now through all cells on the sheet.
    for row in range(1, sheet.lastRow() + 1):
        # Put the content of the row into the record-list.
        record = []
        for col in range(sheet.lastColumn() + 1, 1, -1):
            value = sheet.text(col, row)
            if value or len(record) > 0:
                record.insert(0,value)
        # If the record has at least one cell print it.
        if len(record) > 0:
            text += "<tr>"
            for r in record:
                text += "<td>%s</td>" % r
            text += "</tr>\n"
    text += "</table>\n"

    # Create the richtext widget to display the text.
    richtext = karamba.createRichText(widget, text)
    karamba.moveRichText(widget, richtext, 10, 10)
    karamba.setRichTextWidth(widget, richtext, 345)
    karamba.redrawWidget(widget)

See also...

PyQt with Python

The following python script shows how to use PyQt which provides the nice Qt-API pythonized. The sample just displays a "hello world" dialog if you click on the widget.

import karamba
from PyQt4 import QtCore, QtGui

def initWidget(widget):
    karamba.resizeWidget(widget, 200, 200)

def widgetClicked(widget, x, y, button):
    class Dialog(QtGui.QDialog):
        def __init__(self):
            QtGui.QWidget.__init__(self)
            label = QtGui.QLabel(self)
            label.setText("Hello World")
    dialog = Dialog()
    dialog.exec_()

See also...

TkInter with Python

You are also able to use the default toolkit Python comes with, that is TkInter. The following sample just displays a TkInter "hello world" dialog if you click on the widget.

import karamba
from Tkinter import *

def widgetClicked(widget, x, y, button):
    root = Tk()
    w = Label(root, text="Hello, world!")
    w.pack()
    root.mainloop()

See also...

QtRuby/Korundum with Ruby

The following sample script written in Ruby demonstrates that you are able to use QtRuby/Korundum within your Ruby scripts.

require 'karamba'
require 'Qt'

class Dialog < Qt::Dialog
    def initialize
        super()
        self.windowTitle = 'Hello World'
    end
end

def widgetClicked(widget, x, y, button)
    dialog = Dialog.new
    dialog.exec
end

The script does implement only the widgetClicked function that got called if the user clicks on the widget. What we do within that function is to create an instance of the Dialog class that implements a QDialog using QtRuby and then execute that modal dialog.

See also...

D-Bus

The following sample script written in Python just prints a list with services available via D-Bus;

import karamba, dbus

def initWidget(widget):
    bus = dbus.SystemBus()
    dbus_object = bus.get_object(
        'org.freedesktop.DBus','/org/freedesktop/DBus')
    dbus_iface = dbus.Interface(
        dbus_object, 'org.freedesktop.DBus')
    services = dbus_iface.ListNames()
    text = "".join( ["%s<br>" % s for s in services] )
    karamba.resizeWidget(widget, 520, 520)
    richtext = karamba.createRichText(widget, text)
    karamba.moveRichText(widget, richtext, 10, 10)
    karamba.setRichTextWidth(widget, richtext, 500)
    karamba.redrawWidget(widget)

The following Python script uses D-Bus to display a list with all the devices accessible with HAL;

import karamba, dbus

def initWidget(widget):
    bus = dbus.SystemBus()
    dbus_object = bus.get_object(
        'org.freedesktop.Hal','/org/freedesktop/Hal/Manager')
    dbus_iface = dbus.Interface(
        dbus_object, 'org.freedesktop.Hal.Manager')
    devices = dbus_iface.GetAllDevices()
    text = "".join( ["%s<br>" % d for d in devices] )
    karamba.resizeWidget(widget, 520, 520)
    richtext = karamba.createRichText(widget, text)
    karamba.moveRichText(widget, richtext, 10, 10)
    karamba.setRichTextWidth(widget, richtext, 500)
    karamba.redrawWidget(widget)

While those both samples are going the most easy way to just synchronously call dbus functionality, you are also able to asynchronously call methods or just write a dbus-server by using the with python dbus supported Qt main event-loop as shown in the dbus-python tutorial below - but don't wonder that the samples printed there may not run since it seems the dbus-python module got largely refactored while the with it shipped documentation did not :-/

See also...

Plasma

Following scripts are showing how to use the Plasma functionality in SuperKaramba.

See also...

Plasma::DataEngine as SuperKaramba Sensor

Following Python sample demonstrates usage of the Plasma::DataEngine functionality by using the TimeEngine to display the current local time.

import karamba
def initWidget(widget):
    karamba.resizeWidget(widget, 400, 400)
    text = karamba.createText(widget, 10, 10, 380, 380)
    engine = karamba.getPlasmaSensor(widget, "time", "Local")
    connector = engine.connectSource("Local", text)
    connector.setFormat('%Date %Time')

You are also able to provide an own handle that defines what should be done if the Plasma::DataEngine has new or updated data for an widget or a meter.

# Import the SuperKaramba module.
import karamba
# This method got called by SuperKaramba on initialization.
def initWidget(widget):
    karamba.resizeWidget(widget, 400, 400)
    # Create the text-widget (aka meter).
    text = karamba.createText(widget, 10, 10, 380, 380)
    # Create the Plasma::DataEngine sensor.
    engine = karamba.getPlasmaSensor(widget, "time", "Local")
    # Connect the text-widget with the engine.
    connector = engine.connectSource("Local", text)
    # An own handler for the connector.
    def func(source,data):
        # Only display the current time but not the data["Date"].
        karamba.changeText(widget, text, data["Time"])
    # Set empty source else the sourceUpdated() signal will not
    # be emitted.
    connector.setSource("")
    # Connect the sourceUpdated() signal with the handler.
    connector.connect("sourceUpdated(QString,QVariantMap)",func)

Plasma in a SuperKaramba Theme

And here is the shortest version that needs only one single line of text. Just put following line into a SuperKaramba theme file;

text x=0 y=0 sensor=plasma engine="time" source="Local" format="%Date %Time" poperties="reportSeconds:true,someOtherIntProperty:815"


Plasma and SuperKaramba

Now lets take a more detailed look at a longer sample that uses SuperKaramba and the PlasmaApplet module. While the sample above runs in all cases, the following sample does not cause the used PlasmaApplet module will be only available if SuperKaramba is running as Plasma Applet. If you are running SuperKaramba as standalone application this wan't work (is there interest for this? - but both ways provide the same rich API functionality to work with Plasma::DataEngine's anyway).

# Import the needed modules.
import karamba, PlasmaApplet
# Fetch the TimeEngine Plasma::DataEngine object.
engine = PlasmaApplet.dataEngine("time")
# We like to update it each second.
engine.setProperty("reportSeconds", True)

# Following lines are here to demonstrate that we are also able
# to dynamic handle the sources. It's not needed for this sample.
#def sourceAdded(source):
#    print "sourceAdded '%s'" % source
#def sourceRemoved(source):
#    print "sourceRemoved '%s'" % source
#def sourceUpdated(source,data):
#    print "sourceUpdated '%s' '%s'" % (source,data)
#engine.connect("sourceAdded(QString)", sourceAdded)
#engine.connect("sourceRemoved(QString)", sourceRemoved)
#engine.connect("sourceUpdated(QString,QVariantMap)", sourceUpdated)

# Connect with the TimeEngine's source named "Local"
engine.connectSource("Local")

# This is the richedit widget we use to display something.
text = None

# This method just returns some text to display.
def getText():
    t = "name: %s\n" % PlasmaApplet.name()
    t += "category: %s\n" % PlasmaApplet.category()
    t += "boundingRect: %s\n" % PlasmaApplet.boundingRect()
    t += "sources: %s\n" % engine.sources()
    t += "data: %s\n" % engine.query("Local")
    return t

# This method got called by SuperKaramba on initialization.
def initWidget(widget):
    global text
    karamba.resizeWidget(widget, 400, 400)
    text = karamba.createText(widget, 10, 10, 380, 380, getText())
    karamba.redrawWidget(widget)

# This method got called by SuperKaramba on update.
def widgetUpdated(widget):
    global text
    karamba.changeText(widget, text, getText())
    karamba.redrawWidget(widget)

Following does the same as the sample above but connects the time Plasma::DataEngine with a meter-widget of SuperKaramba. The script will run once and compared to the sample above it's not needed to provide a widgetUpdated function but SuperKaramba will take care of updating the meter if the time Plasma::DataEngine provides new/updated data.

# Import the needed modules.
import karamba, PlasmaApplet
# Fetch the TimeEngine Plasma::DataEngine object.
engine = PlasmaApplet.dataEngine("time")
# We like to update it each second.
engine.setProperty("reportSeconds", True)

# This method got called by SuperKaramba on initialization.
def initWidget(widget):
    global engine
    karamba.resizeWidget(widget, 400, 400)

    # Create the text-meter widget.
    text = karamba.createText(widget, 10, 10, 380, 380, "")

    # Connect the dataengine with the text-meter.
    connector = engine.connectSource("Local", text)

    # Set the formatting. This is how the data should be
    # displayed. The local time-engine source provides the
    # '%Date' and the '%Time' values.
    connector.setFormat('Date: %Date\nTime: %Time')
    
    # Following lines are here to demonstrate that you are also
    # able to implement an own handler for the connector.
    #def func(source,data):
    #    karamba.changeText(widget, text, data["Time"])
    # Set empty source else the sourceUpdated() signal will not
    # be emitted.
    #connector.setSource("")
    # Connect the sourceUpdated() signal with the handler.
    #connector.connect("sourceUpdated(QString,QVariantMap)",func)
    
    karamba.redrawWidget(widget)