Can you create QML from a string/byte array?



  • I am trying to create run-time dynamically created QML content for use in a QQuickView or QQmlApplicationEngine.

    I am doing this because I am writing a host application that users will be able to extend with their own QML extension plugins. I will not know the names of these components so there's no way for me to create a *.qml file that references their components.

    I have been able to dynamically discover, load, and register their types currently (and am able to supply a base URI for these plugins when registering) - but I have to be able to instantiate their components and add them to my base 'host' QML (or replace that QML entirely with theirs.)

    I am at a loss as how to do this.

    I was hoping I could query the plugin for the names it registers and then programmatically generate QML that imports the correct components.

    Suggestions? Thanks :)





  • Doesn't that require me to have my C++ code call into JavaScript in my host QML and then have the JavaScript call into Qt and on the C++ again - create QML components?

    Surely there's a way to access the same Qt functionality that "Qt.createQmlObject" uses from C++ (and thereby avoiding the call into JavaScript and then the call back out into C++)?



  • Oh, I see. I thought you wanted to do this from the QtQuick side. You can do this from C++ like this:

    QQmlEngine engine;
    QQmlComponent component(&engine);
    component.setData("import QtQuick 2.4\nText { text: \"Hello world!\" }", QUrl());
    QQuickItem *item = qobject_cast<QQuickItem *>(component.create());
    // add item to view, etc ...
    

    See also: QQmlEngine, QQmlComponent.



  • Too simple... Just kidding! lol. Thanks :)



  • @Wieland Sorry to bother you because this is probably a very simple answer but I'm beating my head against the wall over this.

    I'm trying to create a very simple test of what you mentioned above, but I cannot get the text to show up in my main window. My sample code is a simple QGuiApplication.

    I have a method (shown below) that loads a very simple QML file, creates the component you wrote above, and tries to add it to the Window from the QML file.

    void loadUI( QQmlApplicationEngine* in_pEngine )
    {
        Q_ASSERT( in_pEngine != NULL );
    
    	// Load the main.qml which contains only a "Window" Qml component
        in_pEngine->load( QUrl(QStringLiteral("qrc:/main.qml")) );
    
    	// Let's create a trivial component and try to add that to the Window
        QQmlComponent l_oComponent( in_pEngine );
    
        l_oComponent.setData("import QtQuick 2.4\nText { text: \"Hello world!\" }", QUrl());
    
        QObject* l_pObject = l_oComponent.create();
    	Q_ASSERT( l_pObject != NULL );
    	
        QQuickItem* l_pItem = qobject_cast<QQuickItem*>( l_pObject );
        Q_ASSERT( l_pItem != NULL );
    
    	// How to add our new component to the Window?
        l_pItem->setParentItem( qobject_cast<QQuickItem*>( in_pEngine->rootObjects().first() ) );
    }
    

    The empty window shows up just fine, and the text component creates just fine as far as I can tell, but I can never see it.

    How should I be adding new components to an existing QML window?

    I did validate that in_pEngine->rootObjects().first() has the object name of the Window I have in my QML file...

    Thanks!



  • Your code is ok, the problem lies in a dark secret of QtQuick.Window. Have a look at the following snippet:

    Window {
        id: myWindow
        width: 800
        height: 600
        visible: true
    
        Rectangle {
            id: myRect
            color: "orange"
            anchors.fill: parent // This doesn't work with Window !
        }
    }
    

    Here myWindow is the parent of myRect but it is not its parentItem (aka visual parent). This is a speciality of Window. For stuff like Item, Rectangle, etc, parent and parentItem are always the same. For Window the parentItem is contentItem. So the following code is correct:

    Window {
        id: myWindow
        width: 800
        height: 600
        visible: true
    
        Rectangle {
            id: myRect
            color: "orange"
            anchors.fill: contentItem // This works !
        }
    }
    

    With this in mind it should be easy to understand why the following does what you want:

    main.qml

    import QtQuick 2.5
    import QtQuick.Window 2.2
    
    Window {
        width: 800
        height: 600
        visible: true
    
        Rectangle {
            id: myContent
            objectName: "myContentObject" // Needed for findChild() in C++
            anchors.fill: contentItem
            color: "orange"
        }
    }
    

    main.cpp

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    
    #include <QQuickItem>
    #include <QQmlContext>
    #include <QDebug>
    
    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
    
        QQmlApplicationEngine engine;
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
        QQmlComponent component(&engine);
        component.setData("import QtQuick 2.4\nRectangle{color:\"black\";width:100;height:100}", QUrl());
        QQuickItem *item = qobject_cast<QQuickItem *>(component.create());
    
        QObject * rootObject = engine.rootObjects().at(0);
        QObject * targetItem = rootObject->findChild<QObject*>("myContentObject");
    
        item->setParentItem( qobject_cast<QQuickItem *>(targetItem) );
    
        return app.exec();
    }
    

    Hope it helps!



  • Bingo! Thanks! I'll note this carefully so I don't ever run into this again ;).

    Cheers.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.