Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Importing an existing QScriptExtensionPlugin to QML
QtWS25 Last Chance

Importing an existing QScriptExtensionPlugin to QML

Scheduled Pinned Locked Moved QML and Qt Quick
7 Posts 5 Posters 3.4k Views
  • 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.
  • T Offline
    T Offline
    topiolli
    wrote on last edited by
    #1

    This topic was touched previously in another thread ( http://developer.qt.nokia.com/forums/viewthread/2378 ), but I find the solution unsatisfactory.

    The problem is I have a rather big QScriptExtensionPlugin whose functionality I want to use in QML programs. QDeclarativeEngine internally uses a QScriptEngine for JavaScript evaluation, but it is not exposed. Why? Why cannot I use my existing QtScript extensions in QML programs?

    Technically this would be simple, I believe:

    @
    QScriptEngine* QDeclarativeEngine::scriptEngine() const
    {
    Q_D(const QDeclarativeEngine);
    return &d->scriptEngine;
    }
    @

    1 Reply Last reply
    0
    • M Offline
      M Offline
      mbrasser
      wrote on last edited by
      #2

      Hi,

      You are right, technically this would be quite simple, and we are hoping to improve this integration in the future (most likely in Qt Quick 2.0). The reason it hasn't yet been done is we aren't 100% sure of the best way to solve this. The easiest approach (basically what you have above) also has some downsides -- for example, QML uses a custom read-only global object for performance reasons, and it would currently be easy to accidentally replace this with raw QScriptEngine access.

      For reference, you can follow/comment http://bugreports.qt.nokia.com/browse/QTBUG-11942 for this task.

      Regards,
      Michael

      1 Reply Last reply
      0
      • T Offline
        T Offline
        topiolli
        wrote on last edited by
        #3

        I desperately needed this feature, so I hacked up an ugly but working solution that doesn't need modifications to Qt sources. I'm posting it in case someone will find it useful.

        First, this function hacks out the address of the private QScriptEngine in QDeclarativeEngine. I haven't tested it with anything else than Linux and GCC on an x86-64.

        @
        QScriptEngine* findScriptEngine(QDeclarativeEngine* engine)
        {
        struct FakeScriptEngineData
        {
        // we know this address (QScriptEngine::rootContext())
        void *rootContext;
        bool isDebugging;

        bool outputWarningsToStdErr;
        
        void *contextClass;
        void *sharedContext;
        void *sharedScope;
        void *objectClass;
        void *valueTypeClass;
        void *typeNameClass;
        void *listClass;
        void *globalClass;
        void *cleanup;
        void *erroredBindings;
        int inProgressCreations;
        QScriptEngine scriptEngine;
        

        };

        // skip vtable and assume each address in the structure is aligned
        // to sizeof(void*)
        void** pPrivateData = reinterpret_cast<void***>(engine)[1];
        void* pRootContext = engine->rootContext();
        for (int i=8; i<32; ++i)
        if (pPrivateData[i] == pRootContext)
        {
        FakeScriptEngineData* pData = reinterpret_cast<FakeScriptEngineData*>(pPrivateData + i);
        return &pData->scriptEngine;
        }
        return 0;
        }
        @

        To import my custom extension I'm doing this:

        @
        void importExtension(QDeclarativeEngine* engine)
        {
        QScriptEngine* pEngine = findScriptEngine(engine);
        QScriptClass* pOldClass = pEngine->globalObject().scriptClass();
        // This makes the read-only global object temporarily read-write
        pEngine->globalObject().setScriptClass(0);

        QScriptValue result = pEngine->importExtension("Into");
        if (pEngine->hasUncaughtException())
        qWarning("%s", qPrintable(result.toString()));

        // Make the object read-only again
        pEngine->globalObject().setScriptClass(pOldClass);
        }
        @

        My tests went fine, and I can use my JS extensions in QML. But I'm a bit afraid this may cause problems later. Why is the global object read-only in the first place? Am I breaking something by making it temporarily read-write?

        1 Reply Last reply
        0
        • S Offline
          S Offline
          Statix
          wrote on last edited by
          #4

          Thanks for this, also works in windows 7 w/mingw32. I automagically generate many toScriptValue/fromScriptValue definitions to define my OmniORB IDL structures for use in the QScriptEngine. I had to use this trick to access them from QML. Hopefully there will be easier access to the script engine or atleast duplicate the to/from semantics to generate objects easier than QDeclarative wrappers.

          1 Reply Last reply
          0
          • C Offline
            C Offline
            chriadam
            wrote on last edited by
            #5

            In QtQuick2.0 we have Module APIs - which can be either QObject module APIs, or QJSValue module APIs. It might not be applicable directly for your situation (where you're obviously integrating tightly with QtScript) but QJSValue module APIs should allow clients to provide arbitrary JS data / etc for use in QML. There are some limitations, however, so I guess it depends on what QJSValue allows.

            1 Reply Last reply
            0
            • T Offline
              T Offline
              TorontoBonito
              wrote on last edited by
              #6

              I'd like to second a request for a QScriptClass type of interface for QML. I'm looking through the current Qt 5 code and I don't see how QJSValue is going to be comparable at all.

              This sort of dynamic binding is trivial in V8, I hope to see some kind of pass through for V8 Interceptors in Qt 5.0.

              I must say, it's somewhat surprising this isn't a core feature in QML already.

              1 Reply Last reply
              0
              • T Offline
                T Offline
                topiolli
                wrote on last edited by
                #7

                Now that I'm finally porting our Qt4 code to Qt5 I need to solve this issue somehow.

                to QJS[X] seems to be mostly straightforward stuff. Still, solution to the original problem with integrating my custom extensions to QML applications isn't obvious. Could someone elaborate the stuff a bit?

                I don't believe our need is unique in any way: we just need to be able to use our JavaScript extensions from both JavaScript and QML. I'm really surprised how difficult it really is to extend JavaScript this way in Qt5. It may be due to the lack of documentation, and I may certainly have missed something, but this is how I see it:

                QtScript seems to be phased out, and it isn't compatible with QML anyway.

                There is no documented extension mechanism for QJSEngine.

                There is an extension mechanism for QML, but the JavaScript global object is read-only.

                The simple question is: how do I add my own JavaScript extensions to QML applications?

                This is already a bit out of topic, but I have to admit I'm bit lost regarding the integration of C++ and QML code. I cannot see QJSValue appear anywhere in QML documentation. Does this mean QML has its own set of reflection objects to the C++ side? Such as QQmlProperty?

                Let's assume I have this QML code:

                @
                // main.qml
                import QtQml 2.0

                QtObject
                {
                id: test

                function test() {}
                }
                @

                Assume I have created an object out of the "test" component:

                @
                QQmlEngine engine;
                QQmlComponent component(&engine, QUrl::fromLocalFile("main.qml"));
                QObject* test = component.create();
                @

                Now I have a QObject pointer in C++.

                How do I iterate the properties and functions the QML object has?

                How do I add another function, say test2() to the object?

                What about adding properties?

                How do I call test()?

                1 Reply Last reply
                0

                • Login

                • Login or register to search.
                • First post
                  Last post
                0
                • Categories
                • Recent
                • Tags
                • Popular
                • Users
                • Groups
                • Search
                • Get Qt Extensions
                • Unsolved