Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Call for Presentations - Qt World Summit

    Creating a newable QtScript object from C++ class and extend its functions via prototype

    General and Desktop
    1
    2
    2966
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • J
      JSchmidt last edited by

      Hi,
      i'm trying to wrap my head around QtScript. Right now, i'm trying to get a new-able QtScript object representation of a C++ class hierarchy, which works fine so far, using scriptValueFromQMetaObject<>() and Q_SCRIPT_DECLARE_QMETAOBJECT().

      From that point on, I want to extend this object represention with some additional functions via custom prototypes, similiar to the "Default Prototypes Example":http://doc.qt.digia.com/qt/script-defaultprototypes.html shown at Qt Ref. Doc.

      Last point doesn't work, i can compile but get the message "TypeError: Result of expression 'myFruit.eat' [undefined] is not a function" from my QtScriptEngine (according to the code snippets below).

      To show my setting...

      These is my simplified class hierarchy:
      @class Fruit{}

      class ExoticFruit : public Fruit {}

      class Cumquat : public ExoticFruit {

      public slots:
      void someNativeCppCumquatSlot();

      }

      Q_META_DECLARE(Cumquat*)
      Q_SCRIPT_DECLARE_QMETAOBJECT(Cumquat, QObject*)@

      Now I added a prototype class:
      @
      class EatableProto : public QObject
      {
      Q_OBJECT
      public:
      explicit EatableProto(QObject * parent = 0){};

      public slots:
      void eat(){ qDebug() << "yamm"; };
      }
      @

      this is my simplified scripting setup:
      @
      //ctor of scripting class
      class MyScripter::MyScripter()
      {
      m_engine = new QScriptEngine;

      //has no effect, as far as I can see
      m_eatablePrototype = new EatableProto();
      m_engine->setDefaultPrototype( qMetaTypeId<Cumquat*>(), m_engine->newObject(m_eatablePrototype) );

      // works fine
      QScriptValue scriptCumquat = m_engine->scriptValueFromMetaObject<Cumquat>();
      m_engine->globalObject().setProperty("SmallOrange",scriptCumquat);
      }@

      On QtScripting view, I want to achive something like this:
      @
      //QtScript Code:
      var myFruit = new SmallOrange();
      myFruit.someNativeCppCumquatSlot(); //works

      myFruit.eat(); //protoype slot, fails with error 'is not a function'
      @

      I'm working on the prototype approach because I want to have more ExoticFruits in near future.. lets say
      @
      class Lemon : public ExoticFruit {}
      class PineApple : public ExoticFruit {}
      ...
      @

      which are all eatable... but I want them to have eatable in scripting-world only.

      Can someone see my mistake? Any suggestions?
      JSchmidt

      1 Reply Last reply Reply Quote 0
      • J
        JSchmidt last edited by

        Okay, i could fix it by my own. My mistake was the usage of the out-of-box macro Q_SCRIPT_DECLARE_QMETAOBJECT() (Qt 4.8.1)

        There is nothing wrong with it, despite the fact, that behind this macro, there is a boiler plate constructor for script objects which does not set the default prototype! Maybe you could see this as a bug? I don't know.

        But more important, to use the defaultPrototype feature in the way I used it above, either you have to define your own constructor for script objects or, a more elegant way, define your own Q_SCRIPT_DECLARE_QMETAOBJECT() macro. Below there is my suggestion with defaultPrototype-Support:

        original Q_SCRIPT_DECLARE_QMETAOBJECT() macro:
        @
        #define Q_SCRIPT_DECLARE_QMETAOBJECT(T, _Arg1)
        template<> inline QScriptValue qscriptQMetaObjectConstructor<T>(QScriptContext *ctx, QScriptEngine *eng, T )
        {
        _Arg1 arg1 = qscriptvalue_cast<_Arg1> (ctx->argument(0));
        T
        t = new T(arg1);
        if (ctx->isCalledAsConstructor())
        return eng->newQObject(ctx->thisObject(), t, QScriptEngine::AutoOwnership);
        QScriptValue o = eng->newQObject(t, QScriptEngine::AutoOwnership);
        o.setPrototype(ctx->callee().property(QString::fromLatin1("prototype")));
        return o;
        }
        @

        improved suggestion with defaultPrototype-support:
        @
        #define Q_SCRIPT_DECLARE_QMETAOBJECT_DEFAULT_PROTOTYPE(T, _Arg1)
        template<> inline QScriptValue qscriptQMetaObjectConstructor<T>(QScriptContext *ctx, QScriptEngine eng, T )
        {
        _Arg1 arg1 = qscriptvalue_cast<_Arg1> (ctx->argument(0));
        T
        t = new T(arg1);
        if (ctx->isCalledAsConstructor()) {
        QScriptValue proto = eng->defaultPrototype(qMetaTypeId<T
        >());
        QScriptValue u = eng->newQObject(ctx->thisObject(), t, QScriptEngine::AutoOwnership);
        u.setPrototype(proto);
        return u;
        }
        QScriptValue o = eng->newQObject(t, QScriptEngine::AutoOwnership);
        o.setPrototype(ctx->callee().property(QString::fromLatin1("prototype")));
        return o;
        }
        @

        All needed voodoo is to ask the ScriptEngine for the defaultPrototype using the template parameter T (line 7) and setting it as prototype of the newly created script object (line 9)

        Maybe it is useful for someone out there!

        1 Reply Last reply Reply Quote 0
        • First post
          Last post