Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

QML - addressing an ancestor

I have a container which is being filled with dynamically created components during runtime. Each of those elements (let's call them DynamicObjects) has a tree of subelements (Nodes), also created dynamically. What is more, The Node components can be nested inside one another (like a tree structure).

Let the container have the id parameter set to "main_container" and every DynamicObject have it set to "dynamic_object".

Whenever I try to address the main_container from any of the nested Node elements it all works fine. The problem is when I try to address the dynamic_object from any Node other than the root one (the one being the direct descendant / child of the dynamic_object). It results in:

ReferenceError: dynamic_object is not defined

My question is: what might be the cause behind this behaviour? Can it have something to do with the fact those objects are dynamically created? (that was my first thought since I can always address the main_container and it is statically declared in the qml code).

Code example: (if there is anything missing please let me now in the comments)

// Container.qml

import "container_logic.js" as Logic

Rectangle {
  id: main_container

  Keys.onTabPressed: {
    Logic.createComponent("DynamicObject.qml", {/* some parameters */});
    var dynamic_object = Logic.object;

    Logic.createComponent("Node.qml",{labelText: "asdefg"}, undefined, dynamic_object)
    var asdefg = Logic.object;
    Logic.createComponent("Node.qml",{labelText: "tree A"}, undefined, dynamic_object)
    var tree_a = Logic.object;

    Logic.createComponent("Node.qml",{labelText: "a"}, undefined, asdefg)
    var a = Logic.object;
    Logic.createComponent("Node.qml",{labelText: "s"}, undefined, asdefg)
    var s = Logic.object;

    asdefg.subnodes = [a, s]

    Logic.createComponent("Node.qml",{labelText: "tree B", isInput: false}, undefined, dynamic_object)
    var tree_b = Logic.object;
    Logic.createComponent("Node.qml",{labelText: "xyz", isInput: false}, undefined, dynamic_object)
    var xyz = Logic.object;

    Logic.createComponent("Node.qml",{labelText: "x", isInput: false}, undefined, xyz)
    var x = Logic.object;
    Logic.createComponent("Node.qml",{labelText: "y", isInput: false}, undefined, xyz)
    var y = Logic.object;
    Logic.createComponent("Node.qml",{labelText: "z", isInput: false}, undefined, xyz)
    var z = Logic.object;

    xyz.subnodes = [x,y,z]

    dynamic_object.treeLeft = [asdefg, tree_a]
    dynamic_object.treeRight = [tree_b, xyz]
  }
}

// DynamicObject.qml

Rectangle {
  id: dynamic_object

  property alias treeLeft : tree_left.subnodes
  property alias treeRight: tree_right.subnodes

  Rectangle {
    id: content_area

    Node {
      id: tree_left

      labelText: "left"

      anchors.left: parent.left
    }

    Node {
      id: tree_right

      labelText: "right"

      anchors.right: parent.right
    }
  }
}

// Node.qml

ColumnLayout {
  id: node

  default property alias subnodes: subnodes_area.data
  property alias labelText: label.text

  Rectangle {
    id: header_area

    Text {
      id: label
    }

    MouseArea {
      id: mouse_area

      anchors.fill: parent

      hoverEnabled: true
      onHoveredChanged: {
        console.debug(main_container) // works fine
        console.debug(dynamic_object) // **generates the error for all nodes but the root one**
      }
    }
  }

  ColumnLayout {
    id: subnodes_area

    anchors.top: header_area.bottom
  }
}

// container_logic

var component = null
var owner = main_container
var object = null
var data = null

function createComponent(type, info, callback, container) {
  callback = callback || finishComponent
  owner = container || main_container

  if(component != null) {
    console.log("Error: a component is being loaded at this time");
    return;
  }

  component = Qt.createComponent(type)
  data = info

  if(component.status === Component.Ready) {
    callback()
  } else if(component.status === Component.Loading) {
    component.statusChanged.connect(callback)
  } else {
    console.log("Error loading component:", component.errorString())
  }
}

function finishComponent() {
  if(component.status === Component.Ready) {
    object = component.createObject(owner, data)
    if(object === null) {
      console.log("Error creating object")
    }
  } else if(component.status === Component.Error) {
    console.log("Error loading component:", component.errorString())
  }
  resetData()
}

function resetData() {
  component = null;
  data = null;
}
like image 800
Konrad Madej Avatar asked Oct 04 '22 07:10

Konrad Madej


1 Answers

According to http://qt-project.org/doc/qt-4.8/qdeclarativedynamicobjects.html :

  • If Qt.createComponent() is used, the creation context is the QDeclarativeContext in which this method is called
  • If a Component{} item is defined and createObject() is called on that item, the creation context is the context in which the Component is defined

The problem was that the createComponent() function for each subsequent Node was called from the context of the main_container, thus preventing the descendants from being able to resolve the dynamic_object id.

Solved it by moving the code responsible for creating the nested Nodes to the Node.qml file (actually to a javascript file imported by that qml document, but the result is the same).

like image 169
Konrad Madej Avatar answered Oct 13 '22 12:10

Konrad Madej