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.

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!