Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass properties to a Loader created object?

Tags:

qt

loader

qml

I have a QML Loader which loads another qml

Loader { id: gaugeLoader }

PieMenu {
    id: pieMenu

    MenuItem {
        text: "Add Bar Gauge"
        onTriggered: gaugeLoader.source = "qrc:/Gauges/horizontalBarGauge.qml"
    }
    MenuItem {
        text: "Action 2"
        onTriggered: print("Action 2")
    }
    MenuItem {
        text: "Action 3"
        onTriggered: print("Action 3")
    }
}

How can I pass parameters to set the ID, width, height and so on of the loaded qml?

like image 773
Basti An Avatar asked May 30 '17 20:05

Basti An


2 Answers

Method 1: Loader::setSource

You can use the Loader::setSource(url source, object properties) function to set the properties during construction, for example:

gaugeLoader.setSource("qrc:/Gauges/horizontalBarGauge.qml", {"width": 100, "height": 100});

Note that you cannot set the id attribute in this way, because it is a not an ordinary property attribute:

Once an object instance is created, the value of its id attribute cannot be changed. While it may look like an ordinary property, the id attribute is not an ordinary property attribute, and special semantics apply to it; for example, it is not possible to access myTextInput.id in the above example.

Instead, you can create a property alias as follows:

property alias gauge: gaugeLoader.item

Method 2: geometry relative to Loader object

As an alternative, you can set the width and height on the Loader object and specify the width and height in horizontalBarGauge.qml relative to its parent, i.e. the Loader object.

property alias gauge: gaugeLoader.item
Loader { 
    id: gaugeLoader 
    width: 100
    height: 100
}

qrc:/Gauges/horizontalBarGauge.qml:

Item {
    anchors.fill: parent
}
like image 78
m7913d Avatar answered Nov 05 '22 21:11

m7913d


I would have never figured this out without @m7913d. I'm expanding on his answer (using Option 2) to help others that are trying to do something similar to me:

  • Pass delegate to GridView that displays data from an AbstractListViewModel
  • delegate SourceComponent injected by Loader

Note: I was struggling to understand the Loader was receiving the UserData returned by model::data(..).

Use Loader to pass UserData returned by model::data(.....)

GridView {
    id: trainingDataSelectGrid
    anchors.fill: parent

    cellWidth: parent.width/3
    cellHeight: parent.height/2

    model: trainingDataListModel
    delegate: Component {
        id: trainingDataDelegate
        Loader {
            sourceComponent: {
                if (index == 0) {
                    return trainingDataModel.addNewComp
                } else {
                    return trainingDataModel.dataComp
                }
            }
            // Loader binds properties to the source component
            // passed to the view delegate
            property string _name: name
            property string _author: author
            property string _author_email: author_email
            property string _created: created
            property string _updated: updated
            property int _net: net
            property int _img_width: img_width
            property int _img_height: img_height
            property int _index: index
        }
    }
    TrainingDataModel {
        id: trainingDataModel
        cardWidth: trainingDataSelectGrid.cellWidth
        cardHeight: trainingDataSelectGrid.cellHeight
    }
}

Both of my source components existed in trainingDataModel (QML: TrainingDataModel).

Before:

property Component dataComp: activeCard
property Component addNewComp: addNew
property string name
property string author
property string author_email
property string created
property string updated
property int net
property int img_width
property int img_height
property int index

property int cardWidth
property int cardHeight

property int paneWidth: cardWidth * 0.95
property int paneHeight: cardHeight * 0.95
property int materialElevate: 5
property string materialBgColor: "white"

id: cardBody

Component {
    id: addNew
    Rectangle {
        id: card
    }
}

Component {
    id: activeCard
        Rectangle{......}
    }
}

After: Move Variables to the Scope of the component that Loader will instantiate

property Component dataComp: activeCard
property Component addNewComp: addNew

property int cardWidth
property int cardHeight

property int paneWidth: cardWidth * 0.95
property int paneHeight: cardHeight * 0.95
property int materialElevate: 5
property string materialBgColor: "white"

id: cardBody

Component {
    id: addNew
}

Component {
    id: activeCard

    Rectangle {
        id: activeCardBackground
        width: cardWidth
        height: cardHeight
        color: "transparent"

        property string name: _name
        property string author: _author
        property string author_email: _author_email
        property string created: _created
        property string updated: _updated
        property int net: _net
        property int img_width: _img_width
        property int img_height: _img_height
        property int index: _index

        /// Use properties below
    }
}
  • Use temporary variables (i.e. _xxxx) to avoid naming collisions passing values from loader to delegate source component
like image 27
Alex Hendren Avatar answered Nov 05 '22 22:11

Alex Hendren