Kent
Qt
QSA
Posted by Kent
 in Qt, QSA
 on Friday, August 29, 2008 @ 14:04

Been having some fun this week trying to add support for data bindings to Qt Script, and now I have something that seems to work. This code shows the basic usage:

QScriptEngine engine;
QPushButton button;
QScriptValue scriptButton = engine.newQObject(&button);
engine.evaluate("x = 'foo'");
scriptButton.setProperty("text", "x", QScriptValue::DataBinding);

This will cause the button’s text property to be updated whenever the script variable x changes, either from C++ (QScriptValue::setProperty()) or from
script. QObject properties can be part of the binding expression as well, as long as the NOTIFY attribute is specified in the Q_PROPERTY definition. Qt Script will discover that the property has a signal associated with it and use it to track changes. Qt’s classes already use NOTIFY for some properties (e.g. QLabel::text).

Below is a small example. The text property of a label is bound to the text property of a lineedit and the script variable verb; the final value is made by concatenating the lineedit’s text, the verb and a string literal.

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QWidget win;
    QVBoxLayout *vbox = new QVBoxLayout(&win);
    QLineEdit *lineEdit = new QLineEdit();
    QLabel *label = new QLabel();
    label->setStyleSheet("font-size: 24px");
    vbox->addWidget(lineEdit);
    vbox->addWidget(label);

    QScriptEngine engine;
    QScriptEngineDebugger debugger;
    debugger.attachTo(&engine);
    vbox->addWidget(debugger.widget(QScriptEngineDebugger::ConsoleWidget));

    QScriptValue scriptLineEdit = engine.newQObject(lineEdit);
    engine.globalObject().setProperty("lineEdit", scriptLineEdit);
    QScriptValue scriptLabel = engine.newQObject(label);
    engine.globalObject().setProperty("label", scriptLabel);

    engine.evaluate("verb = 'smack'");
    scriptLabel.setProperty("text", "lineEdit.text + ' ' + verb + 's you!'", QScriptValue::DataBinding);

    win.show();

    return app.exec();
}

The result, after typing something into the lineedit and assigning something to verb:

Qt Script data binding example

I’ve also added an extension to QScriptClass that enables you to send property change notifications for custom (non-QObject-based) script objects, i.e. you can use your own notify mechanism.

As I said, this isn’t in Qt yet, it’s just an experiment, but it’s looking pretty interesting.

Kent
Qt
QSA
Posted by Kent
 in Qt, QSA
 on Monday, August 25, 2008 @ 14:45

At Akademy (which was a great event by the way!) I had an interesting talk with Pino (the man with Okular vision 8-) ) about an almost equally interesting feature of the Adobe Acrobat JavaScript API. Quoting the reference:

Many of the JavaScript methods provided by Acrobat accept either a list of arguments, as is customary in JavaScript, or a single object argument with properties that contain the arguments. For example, these two calls are equivalent:

app.alert("Acrobat Multimedia", 3);

app.alert( { cMsg: "Acrobat Multimedia", nIcon: 3 } );

The second form uses JavaScript’s object literal syntax to provide arguments in a declarative style, which can make for more readable code, and relieves the script author of having to worry about the order of arguments. However, it does present a new challenge to the implementer of the API: Does he have to implement every function twice now, once per argument-passing style?

Short answer: No (and that’s the honest truth). Long answer: Read on.

What we’ll do is wrap a proxy function around the function that implements the API; the job of the proxy is to detect which particular argument-passing style is used in an invocation, and call the real function using a single argument-passing style regardless (i.e. convert the object literal to a list or vice versa). Essentially we are building our own little type system on top of barebones JS. Sure, this means that some additional glue code has to be written for initializing the JS bindings, and there will be a slight overhead involved with calling a public API function, but I believe in most cases that’s a small price to pay, considering the alternative. The solution presented works on a per-function basis anyway, so you don’t have to use it for everything.

I’ve written a small JavaScript function, called argumentative(), that does the work; you can download it here. To use it, you first prepare your own “private” implementations of the API in whatever call-style you wish, for example list-style:

function __alert(msg, icon, type, title, doc, checkbox)
{
    // For now we just dump the arguments, a real implementation
    // would display a message box.
    print("msg:", msg, "icon:", icon, "type:", type);
    print("title:", title, "doc:", doc, "checkbox:", checkbox);
}

Then you call argumentative(), passing it your function, and an array of argument descriptors. argumentative() will return a proxy function, which is the function you actually want to expose to script authors (i.e. your public API).

alert = argumentative(__alert,
    [ { name: 'cMsg', type: String },
      { name: 'nIcon', type: Number, defaultValue: 0 },
      { name: 'nType', type: Number, defaultValue: 0 },
      { name: 'cTitle', type: String, defaultValue: 'Adobe Acrobat' },
      { name: 'oDoc', type: Object, optional: true },
      { name: 'oCheckbox', type: Object, optional: true,
        properties: [ { name: 'cMsg', type: String, defaultValue: 'Do not show this message again' },
                          { name: 'bInitialValue', type: Boolean, defaultValue: false } ]
      }
    ]);

The purpose of the name property of an argument descriptor should be obvious. The type property is optional; if it is specified, the proxy function will check that the actual argument is of the specified type before invoking your function; this means you can potentially get rid of a lot of type checks in your own code. (The type-checking approach is inspired by John Resig’s strict() function, found in his book “Pro JavaScript Techniques”.) The defaultValue property is optional; if it is specified, its value will be used if the argument is missing in a call. Descriptors for object-based types can additionally specify a properties property, which is just another array of argument descriptors; the proxy function will recursively validate the properties of the object, and substitute in default values if appropriate, before calling your function.

Anyway, now the script author can either do:

alert("my message", 1, 2, "my title", null, { cMsg: "check", bInitialValue: false } );

or

alert( { cMsg: "my message", nIcon: 1, nType: 2, cTitle: "my title",
          oDoc: null, oCheckbox: { cMsg: "check", bInitialValue: false } } );

To your implementation, the two calls will appear identical. (By the way, the argumentative() function lets you control which style you want to receive the arguments in; if you pass 1 as the third argument, your function will receive arguments single-argument-object style, instead of as a list.) If the script author does this:

alert();
alert( { } );

in both cases he will get an error saying that the cMsg argument is missing, as expected. Similarly, if he does this:

alert( { cMsg: "my message", oCheckbox: { cMsg: "check", bInitialValue: "ciao" } } );

he will get an error saying that the argument oCheckbox.bInitialValue has the wrong type.

In the case of the Adobe JS bindings, the argumentative() function could also easily be augmented to support the special acrohelp argument (in which case the function should return a list of its own arguments, rather than call the real function); the function proxy already has all the information it needs.

OK, now for the Qt Script-related part (I almost forgot this is a Qt blog). Most, if not all, JS API functions like those for Acrobat (including alert()) have to be implemented as native functions. So how can you take advantage of the argumentative() functionality in this case? It’s actually pretty simple, as demonstrated by the following C++ snippet:

QScriptEngine engine;
/* evaluate argumentative.js
 ... ... */

QScriptValue descriptors = engine.evaluate(/* the same array of descriptors defined in an earlier snippet */);
QScriptValue fun = engine.newFunction(alert); // alert is a function pointer
QScriptValue proxy = eng.evaluate("argumentative")
                    .call(QScriptValue(), QScriptValueList() << fun << descriptors);
engine.globalObject().setProperty("alert", proxy); // install the public API function

The full example can be downloaded here; it’s a partial implemention of the alert() function using a proper Qt message box, and shows that the native function works the same regardless of which argument-passing style the script uses.

lorn
Uncategorized
Qt
Qtopia
KDE
QSA
Qt Jambi
News
Greenphone
Posted by lorn
 in Uncategorized, Qt, Qtopia, KDE, QSA, Qt Jambi, News, Greenphone
 on Wednesday, September 12, 2007 @ 01:08

Trolltech is presenting the fourth annual Developer Days events[1] in the US
and Germany. We invite free software developers to join without admittance
fee.

http://trolltech.com/company/newsroom/events/allevents/devdays2007

The events are taking place in Redwood City, California (October 3 & 4), and
Munich, Germany (October 16th & 17). If you are currently contributing to an
open source project, and would like to apply for free registration to the
event, please send us a short mail at devdays2007@trolltech.com, listing the
following:

Your Name:
Name of the open source project you actively contributing to:
URL for more project information:

We will review your entry and respond with registration information if
you qualify. We hope to see you at Developer Days!

Eskil Abrahamsen Blomfeldt
Qt
QSA
 in Qt, QSA
 on Wednesday, August 24, 2005 @ 08:57

QSA 1.2.0 and 1.1.3 were both released on friday, so I sat down and did some informal benchmarking (note the word informal, which basically indicates that the computer was playing music in the background while the tests were running.)

The benchmarks consisted of a set of generated scripts, designed to test parsing speed, as well as the execution speed, of certain basic language features (such as function calls, loops, dynamically declaring properties, comparisons, conditional blocks, dynamic type conversion and some arithmetic operations.) Since the generated scripts did nothing useful, I also threw in a small sqrt script to verify the speed of non-pointless code as well.

The timing is based on the high resolution performance counter in my work station, and I’ve arranged the results from longest execution time to shortest.

  • Parsing and executing a huge script: QSA 1.2.0 finished in 0.12 the time it took QSA 1.1.3 to finish.
  • Parsing and executing a big script (big := huge / 2): QSA 1.2.0 finished in 0.19 the time it took QSA 1.1.3
  • Just parsing the same big script: QSA 1.2.0 finished in 0.52 the time it took QSA 1.1.3 to finish.
  • Finding the square root of a big number: QSA 1.2.0 finished in 0.6 the time of QSA 1.1.3
  • The conclusion is: QSA 1.2.0 is faster.

    Also, the parts of the engine that were tested are essentially identical in the two versions of QSA, except that the 1.2.0 uses the Qt 4 API rather than the Qt 3 API.

    Hence, Qt 4 is faster; a port of QSA from Qt 3 to Qt 4 gave us a potential performance benefit of up to eighy-eight percent and probably more, and we didn’t have to alter almost any of the logic. Everybody needs to do that dance now.



    © 2008 Nokia Corporation and/or its subsidiaries. Nokia, Qt and their respective logos are trademarks of Nokia Corporation in Finland and/or other countries worldwide.
    All other trademarks are property of their respective owners.