Thomas Zander
KDE
Bindings Generator
Posted by Thomas Zander
 in KDE, Bindings Generator
 on Wednesday, June 11, 2008 @ 19:00

As many of you know, I’m an OpenDocument fan, I love working on making KOffice rock, which is something I do at every opportunity. The work I do is mostly outside of the KOffice repository nowadays and others are picking up the slack. One really cool development is that I found some people willing to put some corporate funding into making our ODF compliance rock!

If you want to write some software and you want to maintain it for various years the best way to do this is to have regular testing in place to ensure that with further development the features you released in a previous release didn’t break. This is a common practice and the quality of your release is directly linked to the amount of testing you do. In the open source world the many users tend to work as testers, which is a concept that generally works pretty well. A much better solution is to have auto-tests. This is basically a program that does a certain task and checks the outcome for the expected values.

In KOffice we are working on improving out ODF compliance by first writing a test, and then making sure that the feature gets implemented. Before committing all tests ever written are ran and only when all of them (still) pass we can say the new feature is done. It should become obvious to anyone reading this that writing new software like this will create better software that is easier to maintain over time. Especially in open source where new people come and go on a regular basis.

I’m therefor extremely happy to report that Girish Ramakrishnan has so far created and made passing 51 61 OpenDocument Format loading tests. This in effect means we have lots of ODF features we researched what they should do (by reading the ISO specification) and then make sure we actually do that. Now and years from now they will do what they are meant to do.

The features we are testing are, for example, lists. That a document that is meant to have a numbered list with any sort of complex numbering load and show correctly. But sometimes we have some quite different test, things that KOffice never did before. In that case new features have to be added to KOffice. One such feature is the dropcaps. This is a feature that people that write newspaper style documents will love. Let me show a screenshot which explains it best. :)

For this new feature we have yet to add some dialogs to configure this, so to try it out I started OOo and loaded the odt doc in KWord. Which I admit is a pretty cool way to show how far we have come in the interoperability area :D

Update; updated number of passing tests.

harald
KDE
Qt Script
Posted by harald
 in KDE, Qt Script
 on Wednesday, April 16, 2008 @ 15:55

One of the more heated points in the ongoing KDevelop team meeting here in Munich is the good old language war - while I love (and hate just as much) Perl, Alex likes Ruby, Roberto JavaScript and I’m sure we can find someone who prefers Python.

Since Revision 797621 of KDevPlatform, scripting via Kross is now supported. This allows everyone to write an extension to KDevelop in his favorite scripting language. Since I’m most familiar with QtScript (yes, I did forget all my Perl a moment ago), here’s an example KDevelop script:

function documentLoaded(doc) {
    print(KDevTools.toDocument(doc).url());
}

documentController = KDevTools.toDocumentController(KDevCore.documentController());
documentController.documentLoaded.connect(docLoaded);

This little script will listen to all documentLoaded signals from the document controller and outpout the URL of the file once it’s loaded.

So, let’s load a file:

documentController.openDocument("test.cpp");

and watch the URL being printed to command line. The available scripting backend could already be used to implement a simple “switch header/source” plugin, that opens the corresponding *.h/*.cpp file when invoked.

For the moment, the “casts” using KDevTools are required as long as I don’t understand how to auto-convert meta-types in a script-independent way :)

My goal for this meeting - implement a version control system plugin completely in a scripting language, so it can be deployed without headache for all supported OSes.

Kent
Qt
Qt Script
Bindings Generator
Posted by Kent
 in Qt, Qt Script, Bindings Generator
 on Friday, April 11, 2008 @ 09:07

As Benjamin mentioned in his blog entry, the WebKit API in Qt 4.4 has this cool feature that lets you embed any widget into a QWebView. My first idea was to use this feature to embed Qt Designer forms, using QUiLoader to load the form and Qt Script to script the form logic. After spending an hour or so to get everything set up, I’d like to highlight this as one area where “everything just comes together”(tm) — where the shiny new Qt 4.4 stuff is combined with the oldies-but-goldies features of yore to produce a synergistic consummation that tickles the brain. Hmm, anyway.

On the HTML side, I’m adding a tag like so:

<object type="application/x-qtform" width="500" height="400">
    <param name="form" value="http://chaos.troll.no/~khansen/calculator.ui"/>
    <param name="script" value="http://chaos.troll.no/~khansen/calculator.js"/>
</object>

The tag identifies a form and script that the plugin will download and use. Next, I create a QWebPluginFactory subclass that WebKit will invoke to create my plugin. Ordinarily I’d write the plugin-related classes in C++, but since the Qt Script Bindings Generator is getting into real good shape now (and because I’m a die-hard script kiddie at heart), I’ve written everything in Qt Script. The plugin factory looks as follows:

function MyWebPluginFactory(parent)
{
    QWebPluginFactory.call(this, parent); // call base class constructor
}

MyWebPluginFactory.prototype = new QWebPluginFactory();

MyWebPluginFactory.prototype.create = function(mimeType, url, argumentNames, argumentValues)
{
    if (mimeType != "application/x-qtform")
        return null;

    var formUrl = getArgumentValue("form", argumentNames, argumentValues);
    var scriptUrl = getArgumentValue("script", argumentNames, argumentValues);
    if (formUrl == undefined)
        return null;

    return new MyWebPlugin(new QUrl(formUrl), new QUrl(scriptUrl));
}

The QWebPluginFactory::create() function is reimplemented to handle the application/x-qtform mimetype. The URLs of the form and script are passed to the new MyWebPlugin instance; this is the widget that is actually embedded in the view. The MyWebPlugin proceeds to download the form and script and lazily initializes itself as the necessary data becomes available. Here’s the full MyWebPlugin implementation:

function MyWebPlugin(formUrl, scriptUrl, parent)
{
    QWidget.call(this, parent); // call base class constructor

    this.initialized = false;
    this.formReply = this.downloadFile(formUrl, this.formDownloaded);
    this.scriptReply = this.downloadFile(scriptUrl, this.scriptDownloaded);
}

MyWebPlugin.prototype = new QWidget();

MyWebPlugin.prototype.downloadFile = function(url, callback)
{
    if (this.accessManager == undefined)
        this.accessManager = new QNetworkAccessManager();
    var reply = this.accessManager.get(new QNetworkRequest(url));
    reply.finished.connect(this, callback);
    return reply;
}

MyWebPlugin.prototype.formDownloaded = function()
{
    var loader = new QUiLoader();
    this.form = loader.load(this.formReply);
    var layout = new QVBoxLayout(this);
    layout.addWidget(this.form, 0, Qt.AlignCenter);
    this.initialize();
}

MyWebPlugin.prototype.scriptDownloaded = function()
{
    var stream = new QTextStream(this.scriptReply);
    this.script = stream.readAll();
    this.initialize();
}

MyWebPlugin.prototype.initialize = function()
{
    if (this.initialized)
        return;
    if ((this.form == undefined) || (this.script == undefined))
        return;
    var ctor = eval(this.script);
    if (typeof ctor != "function")
        return;
    this.instance = new ctor(this.form);
    this.initialized = true;
}

The new networking API introduced in Qt 4.4 (QNetworkAccessManager and friends) is used to download the files. (In a more elaborate plugin, you’d probably want to show progress information while data is being downloaded, by using QNetworkReply’s downloadProgress() signal.) Also worth noting is that because QNetworkReply is a QIODevice, creating the form and preparing the script once the downloads are finished is strikingly elegant. The “protocol” used to apply the script to the form is this: The script is evaluated, and if the result is a function, that function is called with the form as argument. The function then hooks functionality to the form’s components. Time to create a view and try it out:

var view = new QWebView();
view.settings().setAttribute(QWebSettings.PluginsEnabled, true);

var factory = new MyWebPluginFactory();
view.page().setPluginFactory(factory);

view.load(new QUrl("script-calculator.html"));
view.show();

QCoreApplication.exec();

Enable plugins, set the plugin factory, load your page and GO!

I’ve taken the HTML for the documentation of the Qt Script Calculator example, which contains an image of the application in action, and replaced the image tag with an object tag that loads the form and script. So rather than staring at a screenshot, you can actually use the calculator right there in the documentation page; the form and the script can be the same as those that the stand-alone calculator app uses. Neat-o.

If you want to play with it, the code can be found in the examples folder of the Qt Bindings generator (WebKitPlugins.qs).

Kent
Labs
Qt Script
Bindings Generator
Posted by Kent
 in Labs, Qt Script, Bindings Generator
 on Monday, March 10, 2008 @ 14:37

Announcing a new project on Trolltech Labs: Qt Script Generator. It’s a tool that generates Qt bindings for Qt Script. Finally your scripts can go “new QPoint()” without you having to manually bind the world first! You not only get the Qt API, but also the possibility to “subclass” in script code, e.g.:

var w = new QWidget();
w.keyPressEvent = function(e) { print(e.key()); }
w.paintEvent = function(e) { ... }

To use the bindings in your own app, you just compile the bindings plugins, put them in your library path and then go like this:

QScriptEngine eng;
eng.importExtension("qt.core");
eng.importExtension("qt.gui");
// also available: qt.sql, qt.xml, qt.svg, qt.network, qt.opengl

Have a look at the examples to get an idea of what you can do. More examples to come.

The generator itself is essentially the Qt Jambi generator with a Qt Script backend. (Great job, Jambi guys!)

Although this project is still very young, we’re interested in hearing about any issues (functionality / performance / whatever) you have with the bindings. Have fun!

Kent
Labs
Qt Script Debug
Posted by Kent
 in Labs, Qt Script Debug
 on Friday, February 22, 2008 @ 10:19

Announcing a new project on Trolltech Labs: Qt Script Debug. This is a collection of classes and tools that add debugging capabilities to applications that use Qt Script (Qt 4.4+ is needed). Embedding a debugger directly into an application can be done like this:

QScriptEmbeddedDebugger debugger;
debugger.attachTo(/* Pointer to your QScriptEngine goes here*/);

Then just go on with your business as usual. When one of your scripts triggers an uncaught exception, a window like this will pop up. The debugger also adds a qs_break() function to your engine, so you can call that function from a script to bring up the debugger at a specific location. See the docs and examples (in particular the calculator_embed example) for details. For the graphically challenged, there’s a gdb-like debugger available.

One nice thing about the API is that it supports (actually, it’s designed for) out-of-process/remote debugging. This screenshot shows the stand-alone debugger running on a MacBook, debugging an application running on my Linux box. :-D

Feedback and suggestions on debugging-related features and API you’d like to see for Qt Script are welcome. Happy debugging!