Kent
Qt
Posted by Kent
 in Qt
 on Thursday, May 08, 2008 @ 08:50

For reasons of extreme bias, I mostly like the Qt Script C++ API. However, if I had to choose one thing I really dislike, it would be the form of the QScriptValue constructors. They all take an engine pointer as their first argument. This means you have to write code like

QScriptEngine engine;
QScriptValue object = engine.newObject();
object.setProperty("redundancyLevel", QScriptValue(&engine, “Unnecessarily high”));
object.setProperty(”QT_VERSION”, QScriptValue(&engine, 0×040500));

It sure would be a lot nicer if you could do it like this instead:

object.setProperty("redundancyLevel", "Comparatively low");
object.setProperty("QT_VERSION", 0x040500);

Well, with the latest Qt 4.5 snapshots, you can! The old-style constructors have been obsoleted (but continue to work as before, of course). The documentation and examples have been updated to use the new-style constructors, and it’s looking a whole lot nicer.

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!

Kent
Qt
Test
Posted by Kent
 in Qt, Test
 on Monday, November 05, 2007 @ 14:07

Qt comes with a nice tool for unit testing, called QTestLib. Inspired by QTestLib, I set out to create something similar that makes it easy to write tests in Qt Script. I’ve dubbed it QSTestLib. Hopefully you’ll find it useful for testing scripting APIs that your application exports.

Grab QSTestLib here. The documentation is in qstestlib.qs (yes, QSTestLib itself is implemented in Qt Script :-) ). There’s also an example test included (mytest.qs) that illustrates the main features.

Let’s make a simple test case that contains three test functions:

var myTestCase = {
  name: "My Test",

  firstTest: function() {
    qCompare(2+2, 4);
  },

  secondTest: function() {
    qVerify("chimp" != "chump");
  },

  thirdTest: function() {
    qCompare(1+2, 4);
  }
};

Pass the resulting object to QTest.exec() to execute all the test functions. You will get a report like so:

********* Start testing of My Test *********
PASS   : firstTest
PASS   : secondTest
FAIL!  : thirdTest : Compared values are not the same:
   Actual: 3
   Expected: 4
   Loc: mytestcase.qs(13)
Totals: 2 passed, 1 failed, 0 skipped
********* Finished testing of My Test *********

The other familiar C++ QTestLib functionality (qExpectFail(), qSkip(), etc.) is present in QSTestLib too. Defining data-driven tests with QSTestLib can be done like this:

toUpperCaseTest_data: {
    "foo": ["hello", "HELLO"],
    "bar": ["wOrLD", "WORLD"],
    "baz": ["Champ", "CHAMP"]
},

toUpperCaseTest: function(actual, expected) {
    qCompare(actual.toUpperCase(), expected);
},

In this case, QSTestLib will call the toUpperCaseTest() function three times, once for each property of the corresponding data object; the value of the data property becomes the arguments passed on to the function. The name of the data property is used to identify each entry during testing (i.e. so you know which particular data set(s) caused test failures). Also, a *_data property can be a function, in which case QSTestLib will call the function and expects it to return the test data, thus capturing the behavior of the C++ QTestLib too.

One more thing worth mentioning is that QSTestLib separates the back-end — the test logging — into a separate interface. You can implement this interface to receive notification whenever a test event occurs (e.g. a compare failed, a test passed). The default QSTestLib logger uses the built-in print() function to generate its output, but one could for example generate, oh, let’s say, some fancy XML from the test runs instead. To use a custom test logger, set your logger object as the value of the QTest.logger property before calling QTest.exec(). Details on the logger interface are found in the documentation in qstestlib.qs.

Happy unit testing to all!

Kent
QLALR
Posted by Kent
 in QLALR
 on Monday, October 08, 2007 @ 22:47

As Andreas already pointed out, we got lots of great feedback and suggestions from customers at Developers Days; thanks to everyone who participated and chatted with us. One question I got was “So, how do I use this QLALR thing?” As you might know, QLALR is a parser generator hosted on Trolltech Labs; just download it from there and (in theory you could then) start using it. In Qt, we use QLALR to generate the parser for QtScript and QXmlStream. Even though I’m still in a bit of daze due to jet lag, I’ll now attempt to explain how to get started using QLALR to create a parser for your own super-duper language (or existing language XYZ), and embed it into your Qt application, QtScript-style.

For those of you who haven’t read a few books on compilers and aren’t familiar with similar tools like bison/yacc (and the related flex/lex), the QLALR documentation might feel a bit lackluster. For the rest of you, it probably feels the same. I’m not saying the QLALR docs are bad; I’m just saying they don’t exist. One way to get your feet wet is to have a look at the QLALR grammar (.g) files in the Qt sources; src/script/qscript.g for QtScript, and src/xml/stream/qxmlstream.g for QXmlStream. However, both of these are non-trivial and part of a greater whole, and thus difficult to rip out and use as the basis for your own parser. So I’ve made a simple example that shows the basic setup.

The example is a parser for a small toy language I call Qbicle. You can grab the source code here. To build Qbicle, do the usual qmake and make (you don’t need QLALR installed to try it). Run the example to see the result of evaluating some Qbicle statements. To embed Qbicle in your own application (it’s super-useful, I promise!), include qbicle.pri in your QMake project (.pro) file.

The main Qbicle class is QbicleEngine, whose declaration looks like this:

typedef QHash<QString, QVariant> QbicleEnvironment;

class QbicleEngine : public QObject
{
public:
    QbicleEngine(QObject *parent = 0);

    QbicleEnvironment environment() const;
    void setEnvironment(const QbicleEnvironment &env);

    QVariant evaluate(const QString &program);

    int errorLineNumber() const;
    QString errorMessage() const;
};

The QbicleEngine class is not generated; it just provides a nice API for evaluating Qbicle programs. Internally it uses the parser generated by QLALR to actually implement the parsing and evaluation of Qbicle programs.

There is a single global environment (a mapping from identifiers to QVariants) that can be set on the engine, and Qbicle programs passed to the evaluate() function can access and change this environment. So you can do stuff like:

QbicleEngine engine;

// initialize environment
QbicleEnvironment env;
env["foo"] = 123;
engine.setEnvironment(env);

qDebug() << engine.evaluate("bar = foo + 3;").toDouble(); // 126

qDebug() << engine.environment(); // foo and bar

The Qbicle parser is defined in qbicle.g. This file is the input to QLALR; executing “qlalr qbicle.g” will generate qbiclegrammar.cpp, qbiclegrammar_p.h, qbicleparser.cpp and qbicleparser.h.The first part of qbicle.g contains definitions of the tokens used in the Qbicle language:

%token T_EQ "="
%token T_PLUS "+"
%token T_MINUS "-"
%token T_LPAREN "("
%token T_RPAREN ")"
%token T_SEMICOLON ";"

… and so on. The parser relies on a lexer (AKA scanner, AKA tokenizer) to break up the input stream (characters) into a sequence of such tokens. The %token definitions in the .g file result in an enum in the generated parser; the lexer uses this enum to communicate back to the parser that a certain token has been recognized. You have to provide the lexer yourself; you can use e.g. flex to generate a lexer from regular expressions, or you can hand-craft your own. Qbicle uses a simple hand-crafted lexer (see qbiclelexer.cpp). (Note that the string that you associate with a token in a %token definition is only used in error messages and for debugging purposes; your lexer is responsible for matching whatever input you want to associate with a token.)

The rest of qbicle.g contains the parser driver, the Qbicle language productions and the associated code to execute when a production has been recognized. The driver uses the tables generated by QLALR to actually implement the parsing. You can typically use the same (or very similar) driver for parsers of different languages (i.e. use qbicle.g as the starting point). The productions are what’s really interesting; they make up the grammar of your language (i.e. the legal form of input programs). Here are a couple of the Qbicle productions:

AdditiveExpression: AdditiveExpression T_MINUS PrimaryExpression ;
/.
case $rule_number: {
    sym(1) = sym(1).toDouble() - sym(3).toDouble();
} break;
./

LeftHandSideExpression: T_IDENTIFIER ;
/.
case $rule_number: {
    sym(1) = lexer->identifier();
} break;
./

The code between the /. and ./ markers will be output to the generated parser, and is executed when the parser has matched the preceding production. $rule_number will be replaced by the actual number that represents the production in the grammar; the rest of the code is passed through without modification. The sym() function is used to access the parser’s value stack; in this example, the value stack consists of QVariants. In the Qbicle example, no intermediate representation is generated; expressions are evaluated on the fly. I leave it as an exercise to the reader to implement abstract syntax tree (AST) generation, conditionals, bytecode compilation & interpretation, and Just-In-Time (JIT) compilation on all supported Qt platforms. (For hints on how to do everything except the JIT part, have a look at the QtScript sources.)

For completeness, here’s the grammar for Qbicle in BNF:

Program ::= Statement+

Statement ::= Expression ';'

Expression ::= AssignmentExpression

AssignmentExpression ::= LeftHandSideExpression '=' AssignmentExpression
                       | AdditiveExpression

LeftHandSideExpression ::= T_IDENTIFIER

AdditiveExpression ::= AdditiveExpression '+' PrimaryExpression
                     | AdditiveExpression '-' PrimaryExpression
                     | PrimaryExpression

PrimaryExpression ::= T_IDENTIFIER | T_NUMERIC_LITERAL | T_STRING_LITERAL | T_TRUE | T_FALSE
                    | '(' Expression ')'
Kent
Qt
Posted by Kent
 in Qt
 on Tuesday, July 17, 2007 @ 19:31

As we all know, connecting to the signals of a QObject from QtScript code is trivial; for example

bossButton.clicked.connect(function() { desktop.showFakeSpreadSheet(); employee.grin(); } );

However, what if you don’t intend to expose your QObject to script code? Let’s say you got this crazy idea, that you only intend for scripters to be able to code some script function (AKA “slot”) that you bring into your application, then you want to handle the actual signal-to-slot connections yourself, in C++ (god forbid!). What do you do then? Scream out in agony?

Well, if you’re using the daily Qt snapshots, there’s actually no need to do so; a couple of days ago we added the necessary functionality. Let’s have a look at the obligatory, completely useless example. First, we have a fair amount of bland code (sorry about that) that sets up a widget with two buttons:

#include <QtGui>
#include <QtScript>

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

    QWidget window;
    QVBoxLayout *vbox = new QVBoxLayout(&window);
    QPushButton *awakenButton = new QPushButton("Rise From Your Watery Grave");
    vbox->addWidget(awakenButton);
    QPushButton *exitButton = new QPushButton("Exit");
    vbox->addWidget(exitButton);

Then we create our script engine, and a script object, and give the object a suitable name:

    QScriptEngine engine;
    QScriptValue object = engine.newObject();
    object.setProperty("name", QScriptValue(&engine, "Megatron"));

We read our “slot” script function from a file:

    QFile file(":/slot.qs");
    file.open(QIODevice::ReadOnly);
    QScriptValue slot = engine.evaluate(file.readAll());
    file.close();

Nothing out of the ordinary so far. But now we’re actually ready to do a signal-to-slot connection:

    qScriptConnect(awakenButton, SIGNAL(clicked()), object, slot);

Wow. Just… Wow. How much easier, consistent and intuitive can it get? Both syntactically and semantically, it’s very similar to QObject::connect(). The main difference, of course, is that the last two arguments to qScriptConnect(), the receiver and the slot, are QtScript objects (one can be any type of object, the other must be a function object). When the QtScript “slot” function is executed in response to the signal, the receiver that you specified will act as the this-object. If you specify an invalid QScriptValue as the receiver, the this-object will be the interpreter’s Global Object; this even makes qScriptConnect() consistent with ECMAScript Function.prototype.call() and Function.prototype.apply(), for the purists who care about that stuff (hey, don’t look at me). There is of course also a qScriptDisconnect() function, which destroys a connection.

Finally, demonstrating the principle of “Why do it the easy way when you can do it the hard way”, let’s round off main() by using qScriptConnect() to connect to QApplication::quit() as well:

    QScriptValue scriptApp = engine.newQObject(&app);
    qScriptConnect(exitButton, SIGNAL(clicked()), scriptApp, scriptApp.property("quit"));

    window.show();
    return app.exec();
}

Mmm, beautiful. Last, but not least, the QtScript function loaded from slot.qs, which is executed when the first button is clicked:

return function() {
    var sender = __qt_sender__;
    sender.text = "YES! " + this.name + " is now alive and kicking!";
    sender.styleSheet = "background: lime";

    sender["clicked()"].disconnect(this, arguments.callee);

    sender.checkable = true;
    sender.toggled.connect(sender.hide);
}

First we use a QtScript extension to ECMAScript, __qt_sender__, to grab hold of the button that sent the signal (in general a dubious practice, if you ask me, whether it be script code or C++; but sometimes it’s just too tempting). We change the button’s text, give it a nice background color, then we break the connection that we established in C++ (arguments.callee to the rescue, as usual). Finally we do some more silly stuff, just to show that we can and aren’t afraid to do so.

Oh yeah, in case you were wondering, you can also do a connect from C++ with the QtScript 4.3 API, by doing this:

QScriptValue scriptButton = engine.newQObject(awakenButton);
QScriptValue scriptSignal = scriptButton.property("clicked()");
QScriptValueList args;
args << object << slot;
scriptSignal.property("connect").call(scriptSignal, args);

It’s simple to create a convenience function based on this code, having arguments identical to qScriptConnect(). Way to go 4.3, counterattack!

Kent
Qt
Posted by Kent
 in Qt
 on Monday, May 14, 2007 @ 10:25

Observe the following code snippet:

QScriptEngine engine;
engine.evaluate("x=0");
for (int i = 0; i < 42000; ++i)
    engine.evaluate("++x");

In the preceding code, the QScriptEngine::evaluate() function is called repeatedly with the same input; the engine will compile this input anew each time, and thus is doing a lot of redundant work. Wouldn’t it be nice if we could “compile once, execute any number of times”(tm)?

Well, one option would be to introduce a “CompiledScript” class, a QScriptEngine::compile() function that returns the compiled version of your script code, and an overload of evaluate() that takes a “CompiledScript” as argument. But do we really need all that stuff? Nah; you can easily achieve the intended effect with the current API, with one line of extra code. How? By using function expressions:

QScriptEngine engine;
engine.evaluate("x=0");
QScriptValue fun = engine.evaluate(”function() { ++x; }”);
for (int i = 0; i < 42000; ++i)
    fun.call();

Before entering the loop, we evaluate (=compile) a function expression, which has as its body the code we wish to evaluate (=execute) several times. In the loop, we simply call() the resulting function. You can deal with script exceptions the same way you do when using evaluate(); e.g. if some error occurs, the return value from call() will be an Error object.

The code using function expressions is about 20 times faster than the code in the first snippet.

Perhaps you have one or more variables that you want to substitute into a script before evaluating it. Maybe you’d call evaluate() like this:

for (int i = 0; i < 42000; ++i)
    engine.evaluate(QString::fromLatin1("x += %0").arg(i));

So when e.g. i is 123, "x += 123" would be passed to evaluate(). Well, you can use function expressions in cases like this too; simply define a function expression that manipulates its arguments in the desired way, then pass the actual arguments when calling the function:

QScriptValue fun = engine.evaluate(”function(a) { x += a; }”);
QScriptValue thisObject = engine.globalObject();
for (int i = 0; i < 42000; ++i) {
    QScriptValueList args;
    args << QScriptValue(&engine, i);
    fun.call(thisObject, args);
}

In this case, using a function expression is 11 times faster than plain calls to evaluate().

Another, not performance-centric, yet pleasant effect of using function expressions is that you can declare variables that are local to the function invocation, e.g. you avoid things unintentionally/unnecessarily ending up in the engine’s global environment:

engine.evaluate("function() { var foo = 123; }").call();

If you passed the body of the above function expression (var foo = 123) to evaluate(), you would end up with a global variable foo, overwriting whatever value was previously stored in that variable; perhaps not what you intended.

Comments Off
Kent
Posted by Kent
 in
 on Friday, January 05, 2007 @ 09:25

QtScript is a new module that will be introduced in the next minor release of Qt (4.3). It is a scripting environment that you embed in your C++ Qt application. While the release of Qt 4.3 is still a few months away, fortunately QtScript has just recently been integrated into the daily Qt snapshots, so you can start playing with it now. All you need to do is add

QT += script

to your .pro file, and

#include

in your source file(s), and you’re ready to rock.

QtScript conforms to the ECMA-262 ECMAScript scripting language standard, the core language that’s the basis of JavaScript and Flash ActionScript (to name two well-known languages). The key ECMAScript extension provided by QtScript is the QObject extension; QtScript leverages the introspection capabilities of the Qt Meta-Object System to provide dynamic bindings of the signals and slots and properties of Qt objects (QObject and subclasses thereof).

To give you an idea of what it looks like, here’s a small example showing how to register a widget in the scripting environment, and then manipulate it in script code:

#include
#include
#include 

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

QScriptEngine engine;
QScriptValue scriptButton = engine.scriptValueFromQObject(&button);
engine.globalObject().setProperty(”button”, scriptButton);

engine.evaluate(”button.text = ‘Hello World!’”);
engine.evaluate(”button.styleSheet = ‘font-style: italic’”);
engine.evaluate(”button.show()”);

return app.exec();
}

In script code, you can connect to signals of a Qt object. The target of the connection can either be a Qt slot, or a script function. Example of the latter:

function onButtonClicked(checked) {
print ("button was checked:", checked);
print ("button text:", this.text);
}

button.clicked.connect(onButtonClicked);

As you can see, the sender() of the signal is available as the this object in the script function. You can also use array-style member access:

button['toggled(bool)'].connect(app.quit);

You can disconnect from the signal again like so:

button.clicked.disconnect(onButtonClicked);

If you’re never going to explicitly disconnect from the signal again, the target function can be anonymous:

button.clicked.connect(
function (checked) {
print ("button was checked:", checked);
print ("button text:", this.text);
}
}

Child objects are accessible as members too, so long as the child has its objectName set. A natural application of this feature is when building user interfaces at runtime: Use QUiLoader to build the form, then manipulate the form from script code:

form.buttonBox.accepted.connect(/* some function */)
form.buttonBox.rejected.connect(/* some function */)
form.userNameEdit.text = "Ned Flanders";
form.exec();

There’s more interesting stuff you can do, such as registering your own functions for value conversion (marshalling), binding C++ functions (not just slots), and making slots “script-aware” (using a class called QScriptable). See the QtScript docs in the snapshots for more information.

And finally, some quick questions and answers:

Q: What about QSA?
A: For those of you familiar with QSA (Qt Script for Applications): The new QtScript module bears no relation to QSA. The API is new and the implementation is new. QSA will reach the end of its lifetime in 2008. We’re not touting QtScript as a “QSA replacement” per se, but QtScript clearly offers a lot of the same functionality as QSA, and will naturally offer even more in the future. Just don’t expect to be able to port your existing QSA projects to QtScript in under an hour; as already stated, QSA and QtScript are similar in concept, but very different in practice.

Q: What about performance?
A: QtScript offers far better performance than, say, QSA (by a couple of orders of magnitude). The performance is comparable to SpiderMonkey in most areas.

Q: What about bindings for the rest of Qt?
A: The emphasis is not on bindings for the 4.3 release, but rather on the core scripting environment; e.g. interpreter, ECMAScript compliance, QObject bindings. We offer an API you can use for writing your own bindings, in case the QObject bindings are not sufficient. Don’t expect to be able to write entire applications in only QtScript, at least not with the first release.

Well, have fun and let us know what you think. Remember, there’s still time for us to take your thoughts into account before the first official QtScript release.