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. [SOLVED]Dynamic Binding of QML Object and Factory created C++ object

[SOLVED]Dynamic Binding of QML Object and Factory created C++ object

Scheduled Pinned Locked Moved QML and Qt Quick
5 Posts 1 Posters 3.9k 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.
  • L Offline
    L Offline
    lack
    wrote on 16 May 2014, 06:14 last edited by
    #1

    Hi all first post here. Been lurking a while though.

    I have successfully wrote and registered some cpp objects to QML env:

    Product
    and
    Products -> product 'factory' or 'repository' with Product * Products::get(QVariant id) method

    I've tested these, Product creation, saving, deletion works like a charm from QML side (imperative js)

    Now I'm progressing to building the UI.
    this is my 1st component (simplified) - master_item.qml
    @
    import QtQuick 2.0
    import QtQuick.Layouts 1.1
    import QtQuick.Controls 1.1

    Item {
    anchors.fill: parent
    property alias ref: ref.text
    property alias name: name.text

    GridLayout {
        id: gridLayout
        flow: GridLayout.TopToBottom
        rows:4
    
        Label { text: qsTr("REF")   }
        Label { text: qsTr("Name")  }
    
        TextField {
            id: ref
            placeholderText: qsTr("REF")
        }
        TextField {
            id: name
            placeholderText: qsTr("Name")
            Layout.fillWidth: true
        }
    }
    

    }
    @

    then on main.qml i do

    @
    import my.app.repositories 1.0

    ApplicationWindow {
    TabView {
    id: tabview
    }

    Button {
        text: "Add Tab"
        anchors.top: tabview.bottom
        onClicked: {
            var component = Qt.createComponent("master_item.qml");
            if (component.status == Component.Ready) {
                var tab = tabview.addTab("tab " + (tabview.count + 1), component);
                var item = products.get('CR2011W'));
                tab.item.ref = Qt.binding(function() { return item.ref; });
                tab.item.name = Qt.binding(function() { return item.name; });
            }
        }
    }
    

    @

    this worked for the first time I clicked the button(just the first tab), but subsequent calls yield Type Error.
    the component loaded, but the binding does not.
    What did I do wrong?

    My 2nd try involves embedding the Product object in the component directly:
    @
    import my.app.entities 1.0

    Item {
    anchors.fill: parent
    Product {
    id: data
    }

    GridLayout {
        id: gridLayout
        flow: GridLayout.TopToBottom
        rows:4
    
        Label { text: qsTr("REF")   }
        Label { text: qsTr("Name")  }
    
        TextField {
            id: ref
            placeholderText: qsTr("REF")
            text: data.ref
        }
        TextField {
            id: name
            placeholderText: qsTr("Name")
            Layout.fillWidth: true
            text: data.name
        }
    }
    

    }
    @

    so in main.qml I thought it's gonna be more intuitive:
    @
    onClicked: {
    var component = Qt.createComponent("master_item.qml");
    if (component.status == Component.Ready) {
    var tab = tabview.addTab("tab " + (tabview.count + 1), component);
    tab.item.data = products.get('CR2011W'));
    }
    }
    }
    @

    Now the bindings worked for subsequent calls, but tab.item.data = products.get('ID') doesn't seem to override the component's internal data property. so it's just a blank component with blank Product object.

    I've tried putting the component creation in separate js file, to no avail.

    What I need basically is bind a dynamically create QML component to an (also dynamically) Factory created cpp Object.

    How do I do this correctly?

    Thanks.

    Edit - Solution
    user-accesible property of the underlying cpp object's type in component's qml
    @
    import my.app 1.0

    Item {
    property Product obj
    obj: Product {}

    TextField {
        id: name
        text: obj.name
    }
    

    }
    @
    and in main.qml
    @
    import my.app 1.0

    ApplicationWindow {
    Products { id: products }
    TabView { id: tabview }

    Button {
        text: "Add Tab"
        anchors.top: tabview.bottom
        onClicked: {
            var component = Qt.createComponent("component.qml");
            if (component.status == Component.Ready) {
                var tab = tabview.addTab("tab " + (tabview.count + 1), component);
                var obj = products.get("ID HERE");
                if (tab.status == Loader.Ready) 
                    tab.item.obj = obj;
                else 
                    tab.loaded.connect(function() {
                        if (tab.status == Loader.Ready)
                            tab.item.obj = obj;
                    });
            }
        }
    }
    

    }
    @
    according to the docs, local components should not be lazy loaded so in this case the component status check might be alright if omitted.

    1 Reply Last reply
    0
    • L Offline
      L Offline
      lack
      wrote on 16 May 2014, 08:19 last edited by
      #2

      I think the problem lies in Tabview.addTab() only accepting Component.
      if it accepts instantiated object instead, I could probably do something like:
      @
      var component = Qt.createComponent("master_item.qml");
      if (component.status == Component.Ready) {
      var obj = component.createObject(tabview, {"data": Products.get("CR2001W")});
      tabview.addTab("CR2001W", obj);
      }
      @

      but no, tabview is basically a Loader...

      Any solution to this?

      1 Reply Last reply
      0
      • L Offline
        L Offline
        lack
        wrote on 16 May 2014, 09:12 last edited by
        #3

        I got my 2nd approach working as follows:
        @
        import my.app.entities 1.0

        Item {
            anchors.fill: parent
            property Product obj
            obj: Product {}
         
            GridLayout {
                flow: GridLayout.TopToBottom
                rows:4
         
                Label { text: qsTr("REF")   }
                Label { text: qsTr("Name")  }
         
                TextField {
                    id: ref
                    text: obj.ref
                }
                TextField {
                    id: name
                    text: obj.name
                }
            }
        }
        

        @
        and on main.qml
        @
        onClicked: {
        var component = Qt.createComponent("master_item.qml");
        if (component.status == Component.Ready) {
        var tab = tabview.addTab("tab " + (tabview.count + 1), component);
        tab.item.obj = products.get('CR2001W');
        }
        @

        but then I'm back to SQUARE ONE. first add it works, second adding of tab yields Type Error.

        WTH am I doing wrong??

        1 Reply Last reply
        0
        • L Offline
          L Offline
          lack
          wrote on 16 May 2014, 09:50 last edited by
          #4

          After a closer inspection after the call:
          var tab = tabview.addTab(...)

          tab var will not be ready if the corresponding Tab isn't on focus, that is, the Tab is lazily loaded. Now if only I could assign a callback once its status is Loader.Ready...

          1 Reply Last reply
          0
          • L Offline
            L Offline
            lack
            wrote on 16 May 2014, 10:05 last edited by
            #5

            GOT IT.
            this worked:
            @
            onClicked: {
            var component = Qt.createComponent("master_item.qml");
            if (component.status == Component.Ready) {
            var tab = tabview.addTab("tab " + (tabview.count + 1), component);
            if (tab.status == Loader.Ready) {
            tab.item.obj = products.get("CR2001W");
            } else {
            tab.loaded.connect(function() {
            console.log("this will be executed when corresponding tab is on focus");
            if (tab.status == Loader.Ready) {
            tab.item.obj = products.get("CR2001W");
            }
            });
            }
            }
            }
            @
            by extension, I should be putting callbacks on component.statusChanged as well... this gets ugly pretty fast. Refactor this on different JS file? how about my Products factory object? will it be available from js? Can I even put imports in plain js?

            1 Reply Last reply
            0

            1/5

            16 May 2014, 06:14

            • Login

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