Benjamin Meyer
Qt
WebKit
Posted by Benjamin Meyer
 in Qt, WebKit
 on Monday, April 28, 2008 @ 11:15

Arora

The demo browser in Qt 4.4 has obtained a lot of attention and interest. Now that 4.4 is almost out I have forked it and started a new project called Arora where development can continue, features can be added, and anyone who wants can contribute. The source code is in a Git repository and is currently hosted on github. Along with some improvements and features there are autotest and manual tests. Git (and GitHub) makes it pretty easy for new developers to a project to fork off a branch to develop features that can be later merged back into the project. Feel free to grab the source and add the feature that you miss the most or take one of the fun items off the TODO list. To any artists out there we need an icon to match the new name. Given that this is a fork of the demo future news on Arora will be on the project’s home page or on the Arora blog.

QtWebKit & WebKit trunk

In an effort to stabilize QtWebKit before the 4.4 release QtWebKit was forked from WebKit about six months ago. This is why the acid3 test score in Qt4.4 didn’t match the latest Safari. Now that 4.4 is almost out the door work has begun on integrating the changes into WebKit trunk for 4.5. Not everything is in yet, but already a lot works and it is very exciting. I’ll let this screen shot speak for itself:

acid3.png

The remaining failing tests mostly relate to SVG Fonts. If you are interested in helping out feel free to join #webkit or #qtwebkit on freenode. Instructions on how to build Arora with QtWebKit trunk can be found in the Arora source wiki page.

Thiago Macieira
Qt
Posted by Thiago Macieira
 in Qt
 on Monday, April 28, 2008 @ 08:04

You know that every toolkit and language that is more recent than C has its own string class. Some say it’s a feature of the C language not to have a string built-in, keeping it lean and simple. Others say it’s a drawback, thus making people have to roll out their own and make beginners always fall to the trap of comparing two strings for equality with == instead of strcmp.

C++ inherited that and its initial versions did not have a string class. It wasn’t until the Standard Template Library was standardised that the language got std::string. But that was too late for Qt: Qt 1.x had long been out and it included its own string class (QString). And for a long while, Qt could not rely on there being a sane implementation of STL in the user’s compilers.

Today that problem is solved, but QString is still there. For several reasons, including:

  • QString is implicitly-shared
  • QString is a Unicode string

The first of those innovations was already present in Qt 1.x, but the second is the one that interests us now. For Qt 2.0, QString was transformed from a simple array of bytes into a proper Unicode string. The old Qt 1 string class became the QCString class from Qt 2 and 3 fame and survives today in the form of Q3CString in Qt4, but implemented using the new QByteArray. (The old Qt 1 to 3 QByteArray class is still available as Q3MemArray today, using the almost identical class that used to be Qt 1.x’s QGArray).

As soon as you have Unicode strings in an 8-bit source code, a question always comes about: what encoding is your file? Even today, with the widespread use of UTF-8, we can’t rely on that fact (text editors in Windows being the worst example). And since Qt must convert from a given 8-bit encoding into the UTF-16 format used by QString, it has to know what you used. That’s why you can find the function QTextCodec::setCodecForCStrings() in the Qt API.

That’s fine for application developers, but how about library developers? If you want your library to be used by application developers who use other encodings in their applications, how do you solve the problem? One solution is to restrict yourself to ASCII codepoints only (that is, from 0 to 127), hoping that will be the correct thing. In most cases, that will work out just fine, but there are some weird encodings out there that are not fully compatible with ASCII.

(For example, did you know that the Shift-JIS encoding for Japanese does not have a backslash? Most Japanese users of Windows think that “C:¥Windows¥System32″ is what everyone sees)

There’s a drawback though: if the application developer set the codec to be something complex, all of your simple ASCII strings will still be parsed using that codec. You’ll then suggest using QString::fromLatin1, which appeared in Qt 2. That function converts a Latin 1-encoded (ISO-8859-1) string into UTF-16. That is, actually, a very simple operation because the ISO-8859-1 encoding matches exactly the first 256 codepoints in Unicode. Therefore, that’s a fast operation and you won’t have a performance penalty due to slow codecs.

The use of QString::fromLatin1 is widespread in Qt 2 and 3 applications and libraries, as well as Qt itself. In fact, you’re likely to find code like the following in many source files:

#define QFL1(x)     QString::fromLatin1(x)

That doesn’t eliminate all performance penalty, though. It creates a QString in any case and that could be a waste of processor time for some simple operations, especially on temporaries. For example, a comparison like:

    if (text == QString::fromLatin1("Qt"))
        doSomething()

will trigger the construction of a QString temporary and its internal data structures, will allocate memory on the heap to accommodate the UTF-16 version of “Qt”, execute the comparison and then delete all of that. Seems like a waste, right?

Well, we thought so too. For Qt 4.0, we introduced a very small new class called QLatin1String. I can summarise it like this:

class QLatin1String
{
public:
    inline explicit QLatin1String(const char *s) : chars(s) {}
    inline QLatin1String &operator=(const QLatin1String &other)
    { chars = other.chars; return *this; }

    // add here inlined operator==, operator!=, operator< , operator>, etc.
    // against QString and against const char*
private:
    const char *chars;
};

You can look up the complete version in Qt’s source code of today. But you can readily see that it’s small and almost trivial. This allows us to rewrite the comparison above as:

    if (text == QLatin1String("Qt"))
        doSomething()

And given a suitable operator== function that compares a QString against a QLatin1String without having to first convert any of them, we’re set. This is the best we can squeeze out of it.

Or is it?

Well, unfortunately, there are two problems with current Qt 4.4 code: first, there are many overloads missing in QString that could take a QLatin1String. Take, for example, QString::replace. It has overloads taking a QChar, QString and QRegExp. What that means is that a QString temporary is created if you were to call that function with a QLatin1String argument. We’re addressing this issue in Qt 4.5, along with a series of other optimisations. (the replace function itself will get an improvement)

The other issue is the signature of those functions. Let me take the simplest example from qstring.h, the QString constructor:

    inline QString(const QLatin1String &latin1);

As you can see, the function takes a constant reference to a QLatin1String object. And we saw in the definition above that QLatin1String is nothing more than a wrapper around a const char * pointer. That unfortunately means we’re telling the compiler to pass a reference to a pointer, or a double indirection to the actual data. You’d never write a function whose argument is of type const char * const &, would you?

In other words, in order to complete that call, the compiler must compute the address of the data, save it somewhere on a scratch area and then pass the address of that scratch area as argument to the callee. An analysis of the assembly code reveals our mistake:

C++ code x86 assembly x86-64 assembly IA-64 assembly
extern "C" function(const char*);
function("foo");
Non-PIC
        movl    $.LC0, (%esp)
        call    _function
        movl    $.LC0, %edi
        call    _function
Doesn’t exist
PIC code
        leal    .LC0@GOTOFF(%ebx), %eax
        movl    %eax, (%esp)
        call    _function
        leaq    .LC0(%rip), %rdi
        call    _function
        addl    out0 = @gprel(.LC0), r1;;
        br.call rp = _function
extern "C" function(const QLatin1String &);
function(QLatin1String("foo"));
Non-PIC code
        leal    -4(%ebp), %eax
        movl    $.LC0, -4(%ebp)
        movl    %eax, (%esp)
        call    _function
        movq    %rsp, %rdi
        movq    $.LC0, (%rsp)
        call    _function
Doesn’t exist
PIC code
        leal    .LC0@GOTOFF(%ebx), %eax
        movl    %eax, -8(%ebp)
        leal    -8(%ebp), %eax
        movl    %eax, (%esp)
        call    _function
        leaq    .LC0(%rip), %rax
        movq    %rsp, %rdi
        movq    %rax, (%rsp)
        call    _function
        addl r14 = @gprel(.LC0), r1
        adds out0 = 16, r12;;
        st8 [out0] = r14
        br.call rp = _function
    

The assembly generated for a calling with a character constant almost doesn’t involve memory operations: on the x86, function arguments are always passed on the stack, so there’s no avoiding that. However, on both the x86-64 and the IA-64, where the arguments are passed on registers, there are no memory operations at all. When the code gets assembled, all that will remain is an addition. However, the call with a contant reference always causes memory operations, in all architectures.

So what happens if we passed the QLatin1String object by value?

C++ code x86 assembly x86-64 assembly IA-64 assembly
extern "C" function(QLatin1String);
function(QLatin1String("foo"));
Non-PIC
        movl    $.LC0, (%esp)
        call    _function
        movl    $.LC0, %edi
        call    _function
Doesn’t exist
PIC code
        leal    .LC0@GOTOFF(%ebx), %eax
        movl    %eax, (%esp)
        call    _function
        leaq    .LC0(%rip), %rdi
        call    _function
        addl    out0 = @gprel(.LC0), r1;;
        br.call rp = _function

As you can see from the assembly output above, it’s exactly the same as the const char * version! So what should I do now? I should replace all of the arguments that take a const QLatin1String & in Qt’s API with a simple QLatin1String argument. And I have to do that now without breaking source or binary compatibility.

Some assembly explanation: PIC means “Position Independent Code”, meaning the code in question can be loaded at any address in memory. That’s the default and only mode for IA-64, whereas on the x86 and x86-64 platforms, it’s usually used in libraries in ELF-based systems. The .LC0 symbol is where the compiler put my “foo” NUL-terminated string. On the IA-64, the r1 register is called “global pointer” (sometimes seen as “gp” in the source code) and points to the center of the module’s data, whereas the r12 register is the “stack pointer” (sometimes seen as “sp”). On the x86, the compiler uses any available register to store the address to the GOT and, in this case, it chose the %ebx register; on the x86-64, it is not necessary since it used addressing relative to the instruction pointer. Those symbols starting with @ are linker flags: they tell the linker to generate some relocation information at that point in the code (GOTOFF is the offset to the GOT [Global Offset Table], @gprel is “relative to the gp”, which is the exact same thing under a different name, so the operation above was: “out0 = (.LCO - gp) + gp”).

The code above was generated with gcc -O2 -S, but I removed the instructions that weren’t relevant to the calling sequence.

espenr
Uncategorized
Posted by espenr
 in Uncategorized
 on Tuesday, April 22, 2008 @ 19:23

The cross-platform story for Qt becomes more and more true. Now we’re even going to be on Maemo it seems (kde people talk: here, osnews flamewar: here). However that was not really what I was going to talk about.

I have a little video for you here basically showing the Qt Everywhere demo running on both Windows Mobile and Embedded Linux side by side. We used the latest fancypants from HTC (Windows Mobile 6.0) and ran the Qt demos side by side to a Neo (Linux). Click the image below to check it out (opens new page, click play again):


video.jpg

The demo is basically just a small set of some of the Qt demos and examples that we launch and interact with in a more gesture inspired way. I love flicking those screengrabs around btw :D Skip ahead to 02:40 if you only want to see it running on the Neo…. Skip to 04:48 if you want to see the Neo die ;)

One thing to notice here is that this demo-set was originally developed and compiled for Embedded Linux. All that was needed to have it running out of the box for Windows CE/Mobile was to add a few deployment rules to the .pro file for the demo. Now - it goes without saying that these demos run out of the box on our desktop platforms too.

Btw. stay tuned for the Open Source Development awards. We have a winner - and we’ve got prizes!

lorn
Qt
Qtopia
Posted by lorn
 in Qt, Qtopia
 on Monday, April 21, 2008 @ 19:53

I dont usually plug other products, but I just bought a retro bluetooth handset, and I love it.
retro handset

The amazing thing is, by 2.5 year old son, who has never really said more than a few words on a phone before, took it and had a complete conversation with his granny! I was stunned. The retro handset is just the natural way a phone should be! It’s just more natural.

To stay on topic, this thing works great on my Openmoko phone running Qtopia!

Andreas
Qt
KDE
Posted by Andreas
 in Qt, KDE
 on Saturday, April 19, 2008 @ 12:59

Kevin and I are chilling out in the speakers’ room in FISL. Later today we’ll head back to Thiago’s place in Sao Paulo before we start heading home tomorrow. It’s been a fun week, and I’ve learned a lot. I can’t wait to come home and get my hands into code again.

I had the pleasure of meeting some fellows from INDT here, they showed my some amazing stuff they’d done, and I’m bringing home a bunch of impressions for those who’ll be working on improving our UI. Since this research is going to be interesting for so many, Kevin also asked me to submit a proposal for this year’s aKademy. I think I’ll submit one or two proposals and we’ll see how that goes. Maybe one about our UI research, and one about Qt’s widget internals. We’ll see.

Now there are only three legs left of my journey. Sao Paulo, Frankfurt, then Oslo. Travelling north, it’ll first get much warmer, then much colder again ;-). It’s around 12 degrees C at home, and my favorite season (Spring in Norway is quite amazing). Looking forward to coming home :-).

Thiago Macieira
Qt
KDE
Greenphone
Posted by Thiago Macieira
 in Qt, KDE, Greenphone
 on Friday, April 18, 2008 @ 13:42

I am writing now from the Trolltech booth at FISL, where the KDE contributors have also taken up shop. It’s the second of three days and I’m already very tired. I was very surprised by the toll that booth duty takes in such a large conference!

FISL (International Free Software Forum, in its acronym in Portuguese) is held yearly in the city of Porto Alegre, of the southern state of Rio Grande do Sul in Brazil. It is probably the largest South American event and one of the largest in the world. Yesterday I’m told there were 7000 people walking by and today it’s probably going to be even more, since it’s Friday. This is the first time that Trolltech has a booth here, but technically not the first that it has had presence.

At several points in time yesterday, we had our 3m x 3m booth packed with people listening to my ramblings (or Knut’s, or Helio’s, or Josef’s more constructive speeches). Questions ranged from people who wanted to know what Qt was and if it could be used for their application purposes (”Does Qt have forms?” or “Do I have to know C++?”), to people who were asking about Qt 4.4 new features, such as WebKit and Phonon.

There were also people asking about KDE 4 and its current state. Hélio had a nice presentation yesterday showing the state of things. Today, Kévin Ottens from KDAB is holding an introductory course to Qt4 development. Later on, there’ll be a KDE-Edu presentation by Maurício Piacentini (the KDE Games coordinator) and one by Andreas Hanssen on the future of graphics development in Qt. He says it’s Qt 4.6 material…

We also had people asking about Java and other languages. I was pleased to be able to tell them about Jambi, but I think I need to learn I lot more about it. Thankfully, Helio downloaded and installed Qt/Jambi on one of the demo computers here. It’s packaged by Mandriva, so it was a matter of a few commands only.

What amazed me was how much crowd two books and one Greenphone attract. Lots of people asked us when the Greenphone would be on the market — to which we had to answer that it’s already over. They also kept on asking if we were selling the C++ GUI Programming with Qt4 books we had on display.

Things to remember for next time:

  • Bring A4 or A5 leaflets with information on Trolltech
  • Bring a set of small loudspeakers so that the Qt4 Dance video can be heard
Comments Off
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.

AndyS
Qt
Posted by AndyS
 in Qt
 on Wednesday, April 16, 2008 @ 13:34

Having been a support engineer for over seven years now at Trolltech, I’ve seen my fair share of inquiries about qmake and since Qt 4.x, qmake has become quite powerful and useful. There are a couple of lesser known variables that can be used which can make integrating your own compilers and targets into a makefile a lot easier to achieve.

Let’s say you want to have your own pre-processor to run over some files before building occurs. This is where you can use QMAKE_EXTRA_COMPILERS. In this case, all our preprocessor does is rename the file from .cxx to .cpp.

    PREPROCESS_FILES = preprocess.cxx foobar.cxx more.cxx
    preprocess.name = Renaming .cxx files to .cpp files
    preprocess.input = PREPROCESS_FILES
    preprocess.output = ${QMAKE_FILE_BASE}.cpp
    preprocess.commands = ren ${QMAKE_FILE_IN} ${QMAKE_FILE_BASE}.cpp
    QMAKE_EXTRA_COMPILERS += preprocess

Doing qmake followed by make will show that it is renaming the files, however on inspection of the makefile it will show that its going to consider these files as objects. Since they are still C++ source code files we want to be able to have them compiled first. With the addition of the line:

    preprocess.variable_out = SOURCES

then it indicates that the output should be added to the SOURCES variable which qmake already knows how to handle. In more advanced use cases we could always indicate that the output shouldn’t be put in with the objects because it is used for other reasons and that can be done by adding:

    preprocess.CONFIG += no_link

The other lesser known variable is QMAKE_EXTRA_TARGETS, this is useful when you want to be able to add a custom build target to your Makefile. For example, if you wanted to have a target to update your qm file for your application. Then you would have something like:

    updateqm.commands = lrelease myapp_no.ts
    updateqm.target = updateqm
    QMAKE_EXTRA_TARGETS += updateqm

It is as simple as that, then you can cause it to be invoked by:

    make updateqm

If you have more than one configuration (as is the default on Windows) then if you want to have it inside the configuration specific makefiles (i.e. Makefile.Debug and Makefile.Release) then simply add:

    updateqm.CONFIG += recursive

then you can do:

    make -f Makefile.Debug updateqm

The documentation for these variables is unfortunately minimal for Qt 4.3 and Qt 4.4.0. However, if you check out the snapshots after it is updated tonight then you will see some in-depth documentation on what is available for both these variables.

Andreas
Qt
KDE
Posted by Andreas
 in Qt, KDE
 on Monday, April 14, 2008 @ 14:35

Greetings from Helsinki airport. I came back from my visit to Milan (thanks Riccardo for arranging the sprint, it was great to be there!) at around 02:30am this morning. Now I’m waiting for my connection through Frankfurt to go to Brazil.

I chatted with Thiago, who’s there already, and he said it’s a good 28C degrees in Sao Paolo right now. Here I am with my suede leather jacket, long jeans and a sweater, my suitcase has only black t-shirts in it, no sun glasses and no sunscreen, feeling pretty silly. He-he! ;-)

Milano was fun, I think I’ll want to sign up for more of those events in the future. My idle task was optimizations in QGraphicsScene’s sorting and transform code (I’ve managed a 15-30x speed-up so far, will submit changes to main/4.5 some time when I come back, see patch below). There’s still more optimizations to come, I think I’m just in the flow with this stuff now. Fun to help out with the port to using QGraphicsWidget, and also with some work we did to speed up the SVG cache. Sorry that I couldn’t stay for the API review. :-/

I’m bringing some feedback home to our team in Oslo:

* QtSvg could use some optimization work
* It would be very nice if QtSvg covered the entire SVG 1.1 Tiny standard
* I should write a blog or a Qt Quarterly article (or both) about tricks to make graphics fast / like device space pixmap caching for static content.

Now I’ve been in Helsinki, holding a presentation about our ideas for improving widgets, theming, animations and the like. Fun and interesting! And now I’m headed to Brazil to do the same. Wee!

PS: Apply to Qt it you have problems with performance with deeply nested structures in Graphics View: http://www.andreas.hanssen.name/scene-speed.txt (no warranty!)

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).