JAN
19
2007

QtScript 104

So far, I've only accessed QObjects, slots and UI files, we need to do better to be able to do anything useful as the Qt 4 API has a lot of methods that aren't accessible that way. To do this, we'll use Javascript prototypes. A prototype is basically the object we can consider an instance to be 'cloned' from (philosophy people can think of it as the platonic ideal of the object) - from a purely implementation point of view, it's enough to know that it's the first place the interpreter will look for something that isn't implemented directly by an object.

What we'll do is have a prototype property like the functionName property we had before that will store the prototype of our object:

fun.setProperty( QString("prototype"), createPrototype( engine, name.toString() ) );

Creating the prototype itself is like any other object:

QScriptValue UiUtils::createPrototype( QScriptEngine *engine, const QString &name )
{
	QScriptValue proto = engine->newObject();

	if ( name == QString("QListWidget") ) {
	    proto.setProperty( QString("addItem"), engine->scriptValue(qlistwidgetAddItem) );
	}

	return proto;
}

Here, we've added support for the addItem() method to the prototype. The implementation of the method is quite simple:

QScriptValue UiUtils::qlistwidgetAddItem(QScriptContext *context, QScriptEngine *engine)
{
    if ( context->argumentCount() != 1 )
	return context->throwError("Add item takes one argument");

    // Find our QListWidget
    // QListWidget *list = qscript_cast<QListWidget*>(context->thisObject()); // Not possible
    QWidget *w = qscript_cast<QWidget*>(context->thisObject());
    QListWidget *list = qobject_cast<QListWidget*>(w);

    list->addItem( context->argument(0).toString() );

    return engine->undefinedScriptValue();
}

The final bit of magic is called when we create an instance of a QObject and looks as follows:

    QScriptValue fun = engine->scriptValueFromQObject( w );
    fun.setPrototype( context->callee().property("prototype") );

We're now saying that the prototype for our instance is the object we defined earlier. In the case of QListWidget, this means we now get our addItem() method.

The end result is that we can now access the non-slot method addItem() and write JS code like this:

var list = new QListWidget();

list.addItem( 'One' );
list.addItem( 'Two' );
list.addItem( 'Three' );
list.addItem( 'Four' );

list.show();

UiUtils.exec();

The QtScript interpreter now has support for setters and getters, and also better support for prototypes, so we should be able to go further soon. In fact, the event handling code from kjsembed can now be implemented in qtscript, but that's for another time.

The code for this example can be found at http://xmelegance.org/devel/qscriptdemo4.tar.gz.