[Qt-interest] QtScript, custom QObject subclasses, and hasOwnProperties()
Nathan Carter
nathancarter5 at gmail.com
Mon Feb 16 16:00:09 CET 2009
Since my previous post didn't go over so well (i.e., no responses :) )
I'll rephrase:
When I incorporate QObjects into script land in the method recommended
by Qt's documentation, I get objects with the C++-land methods and
properties exposed directly in the object, not factored out to a
common prototype object. Is this the intended behavior or am I doing
something wrong?
If it is the intended behavior, then how do I achieve my desired
behavior of putting all methods in the prototype object? Do I have to
create a custom C++ class for the prototype object that just forwards
all method calls to inside the thisObject? That seems like
reinventing the wheel, and that there must be a better way.
Nathan
On Feb 12, 2009, at 2:31 PM, Nathan Carter wrote:
>
> Dear list,
>
> When I follow the recommended procedures in the docs for getting my
> custom QObject subclass accessible from script, it works fine.
> However, I notice that all the functions and properties exposed in
> script land by such objects are not at the prototype level, but at
> the individual instance level. This is despite the fact that I've
> done what the docs suggest for creating a prototype object.
>
> I've created a small compilable example using a dummy QObject
> subclass called Foo. I include that example in this email message;
> it is three source files and a .pro file. Its output is below,
> slightly edited with explanations, followed by the files
> themselves. The part of the output to which my question pertains is
> marked with *******s, about half way down.
>
> My apologies for the enormous email, but I tried to make this post
> as useful/information-ful as possible.
>
> Thanks for any light users/trolls can shed.
>
> Nathan
>
>
>
> We can construct Foo objects with Foo() calls
> ----------------------------------------------------------
> Script code: Foo()
> Result: Foo object with data ""
> Script code: Foo('bing')
> Result: Foo object with data "bing"
> Script code: new Foo('hmm')
> Result: Foo object with data "hmm"
>
> It works the same if we use new Foo() calls
> ----------------------------------------------------------
> Script code: Foo() instanceof Foo
> Result: true
> Script code: Foo('bing') instanceof Foo
> Result: true
> Script code: new Foo('hmm') instanceof Foo
> Result: true
>
> Foo objects work okay in script
> ----------------------------------------------------------
> Script code: tmp = Foo()
> Result: Foo object with data ""
> Script code: tmp.get()
> Result:
> Script code: tmp.set('abc')
> Result: undefined
> Script code: tmp.get()
> Result: abc
>
> Foo objects have "own" keys that should be in prototype
> ----------------------------------------------------------
> Script code: keys( {} )
> Result:
> Script code: keys( Foo.prototype )
> Result:
> objectName
> ,destroyed
> (QObject*),destroyed(),deleteLater(),set(QString),get(),toString()
> Script code: keys( Foo() )
> Result:
> objectName
> ,destroyed
> (QObject*),destroyed(),deleteLater(),set(QString),get(),toString()
> Script code: keys( Foo('bing') )
> Result:
> objectName
> ,destroyed
> (QObject*),destroyed(),deleteLater(),set(QString),get(),toString()
> Script code: keys( new Foo('hmm') )
> Result:
> objectName
> ,destroyed
> (QObject*),destroyed(),deleteLater(),set(QString),get(),toString()
> **************************
> keys() is a function that lists just those strings for which
> the argument returns true when asked .hasOwnProperty(thatKey).
> (See code in main.cpp, below.)
> I would expect the above tests to list lots of stuff in
> Foo.prototype,
> but not list lots of stuff in Foo() or Foo('bing') or new Foo('hmm'),
> because they can simply refer to the prototype.
> This causes problems with a nontrivial class (unlike Foo) with lots
> of
> properties and methods, because routines that process all of them
> (especially in a recursive object hierarchy) slow down a lot, and
> unnecessarily.
>
> The following tests were to see if QtScript used the methods in the
> prototype object
> in any very clever way, but it doesn't seem so. It's just another
> object, and its
> methods only refer to itself ("this" does not get swapped in/out).
> **************************
>
> An experiment...
> ----------------------------------------------------------
> Script code: A = new Object()
> Result: [object Object]
> Script code: B = new Object()
> Result: [object Object]
> Script code: A.__proto__ = Foo.prototype
> Result: Foo object with data ""
> Script code: B.__proto__ = Foo.prototype
> Result: Foo object with data ""
> Script code: A.get()
> Result:
> Script code: B.get()
> Result:
> Script code: A.set('test1')
> Result: undefined
> Script code: B.set('test2')
> Result: undefined
> Script code: A.get()
> Result: test2
> Script code: B.get()
> Result: test2
>
>
> ===========================================source code
> follows=========================
>
> script_proto_test.pro:
>
> QT += script
> macx:CONFIG -= app_bundle
> win32:CONFIG += CONSOLE
> TEMPLATE = app
> TARGET =
> DEPENDPATH += .
> INCLUDEPATH += .
>
> HEADERS += foo.h
> SOURCES += foo.cpp main.cpp
>
> foo.h:
>
>
> #ifndef FOO
> #define FOO
>
>
> #include <QObject>
> #include <QVariant>
>
>
> class Foo : public QObject
> {
> Q_OBJECT
>
> public:
>
> Foo ( QString data = QString() );
> Foo ( const Foo& foo );
>
> Foo& operator= ( const Foo& other );
>
> public slots:
>
> void set ( QString data );
> QString get () const;
> QString toString () const;
>
> private:
>
> QString myData;
>
> };
>
>
> Q_DECLARE_METATYPE( Foo )
>
>
> #endif // FOO
>
> foo.cpp:
>
>
> #include "foo.h"
>
>
> Foo::Foo ( QString data )
> : QObject(), myData( data )
> {
> }
>
> Foo::Foo ( const Foo& foo )
> : QObject()
> {
> myData = foo.get();
> }
>
> Foo& Foo::operator= ( const Foo& other )
> {
> set( other.get() );
> return *this;
> }
>
> void Foo::set ( QString data )
> {
> myData = data;
> }
>
> QString Foo::get () const
> {
> return myData;
> }
>
> QString Foo::toString () const
> {
> return "Foo object with data \"" + myData + "\"";
> }
>
> main.cpp:
>
>
> #include <QScriptEngine>
> #include <QDebug>
> #include "foo.h"
>
>
> QScriptValue FooToScriptValue ( QScriptEngine* engine, const Foo& in )
> {
> Foo* foo = new Foo( in.get() );
> return engine->newQObject( foo, QScriptEngine::ScriptOwnership );
> }
>
> void FooFromScriptValue ( const QScriptValue& object, Foo& out )
> {
> Foo* ptr = qobject_cast<Foo*>( object.toQObject() );
> out = ptr ? *ptr : Foo();
> }
>
> QScriptValue FooScriptConstructor ( QScriptContext* context,
> QScriptEngine* engine )
> {
> if ( context->argumentCount() > 1 )
> return context->throwError( "Too many arguments to
> constructor" );
> QScriptValue object;
> Foo* result = new Foo;
> if ( context->isCalledAsConstructor() ) {
> // they've already built it for us, so start by promoting
> that to a QObject w/new Foo:
> object = engine->newQObject( context->thisObject(), result,
> QScriptEngine::ScriptOwnership );
> } else {
> // not called as "new Foo()", just "Foo()", so create our own
> new Foo:
> object = engine->newQObject( result,
> QScriptEngine::ScriptOwnership );
> object.setPrototype( engine-
> >defaultPrototype( qMetaTypeId<Foo>() ) );
> }
> if ( context->argumentCount() == 1 )
> result->set( context->argument( 0 ).toString() );
> return context->isCalledAsConstructor() ? engine-
> >undefinedValue() : object;
> }
>
> int main ( int /*argc*/, char** /*argv*/ )
> {
> QScriptEngine e;
>
> int FooTypeID = qRegisterMetaType<Foo>( "Foo" );
> qScriptRegisterMetaType( &e, FooToScriptValue,
> FooFromScriptValue );
>
> QScriptValue prototype = e.newQObject( new Foo,
> QScriptEngine::ScriptOwnership );
> e.setDefaultPrototype( qMetaTypeId<Foo>(), prototype );
> QScriptValue constructor = e.newFunction( FooScriptConstructor,
> prototype );
> e.globalObject().setProperty( "Foo", constructor );
>
> e.evaluate( "function test ( expr ) {\n"
> " print( 'Script code: ' + expr + '\\nResult:
> ' + eval( expr ) );\n"
> "}\n" );
> e.evaluate( "function keys ( obj ) {\n"
> " var result = [];\n"
> " for ( var key in obj )\n"
> " if ( obj.hasOwnProperty( key ) )\n"
> " result.push( key );\n"
> " return result;\n"
> "}\n" );
>
> qDebug() << "";
> qDebug() << "We can construct Foo objects with Foo() calls";
> qDebug() <<
> "----------------------------------------------------------";
> e.evaluate( "test( 'Foo()' )" );
> e.evaluate( "test( 'Foo(\\'bing\\')' )" );
> e.evaluate( "test( 'new Foo(\\'hmm\\')' )" );
> qDebug() << "";
> qDebug() << "It works the same if we use new Foo() calls";
> qDebug() <<
> "----------------------------------------------------------";
> e.evaluate( "test( 'Foo() instanceof Foo' )" );
> e.evaluate( "test( 'Foo(\\'bing\\') instanceof Foo' )" );
> e.evaluate( "test( 'new Foo(\\'hmm\\') instanceof Foo' )" );
> qDebug() << "";
> qDebug() << "Foo objects work okay in script";
> qDebug() <<
> "----------------------------------------------------------";
> e.evaluate( "test( 'tmp = Foo()' )" );
> e.evaluate( "test( 'tmp.get()' )" );
> e.evaluate( "test( 'tmp.set(\\'abc\\')' )" );
> e.evaluate( "test( 'tmp.get()' )" );
> qDebug() << "";
> qDebug() << "Foo objects have \"own\" keys that should be in
> prototype";
> qDebug() <<
> "----------------------------------------------------------";
> e.evaluate( "test( 'keys( {} )' )" );
> e.evaluate( "test( 'keys( Foo.prototype )' )" );
> e.evaluate( "test( 'keys( Foo() )' )" );
> e.evaluate( "test( 'keys( Foo(\\'bing\\') )' )" );
> e.evaluate( "test( 'keys( new Foo(\\'hmm\\') )' )" );
> qDebug() << "";
> qDebug() << "An experiment...";
> qDebug() <<
> "----------------------------------------------------------";
> e.evaluate( "test( 'A = new Object()' )" );
> e.evaluate( "test( 'B = new Object()' )" );
> e.evaluate( "test( 'A.__proto__ = Foo.prototype' )" );
> e.evaluate( "test( 'B.__proto__ = Foo.prototype' )" );
> e.evaluate( "test( 'A.get()' )" );
> e.evaluate( "test( 'B.get()' )" );
> e.evaluate( "test( 'A.set(\\'test1\\')' )" );
> e.evaluate( "test( 'B.set(\\'test2\\')' )" );
> e.evaluate( "test( 'A.get()' )" );
> e.evaluate( "test( 'B.get()' )" );
> qDebug() << "";
>
> return 0;
> }
>
>
More information about the Qt-interest-old
mailing list