QML: Finding object by `objectName` in JavaScript

I’m updating a project of mine from a complicated C++ & Common Lisp mess to just QML and JavaScript. Inspired by @attah’s S’Play and @poetaster’s dwd.

Now JavaScript has many advantages: no complicated build process, no architecture limitations, well-known, etc. but QML’s support for it is lacking. Especially the version Sailfish is using.

With that out of the way. I’d like to know how, given a UI element has an objectName defined, I can toggle an attribute on that object and have it work. More specifically:

How can I set busy_rect.visible to true from another component:

I’ve been spending too much time on this and there’s little to be found online (besides “fall back to C++”). As you can see in the code top-level properties can be manipulated. So I’ve also tried tying visible to a property but didn’t succeed.

Have a look at Whisperfish’s MainPage.qml and/or harbour-whisperfish-main.qml files, there’s some usage of objectName IIRC.

1 Like

There was some useful info there and thanks to your comment and a typo I’m starting to think it’s a different issue.

It looks like I can actually access that property without any difficulties but I set visible first to true and then to false in the same onAccepted and apparently UI events do not happen during that time.

If I only set visible to true I see the rectangle appear after the onAccepted is done!

edit: ah, qt - How can I trigger an explicit UI update in qml? - Stack Overflow

Right, this trick works, but it’s just an ugly hack…

Not an expert in anything at all, but the reason you can toggle busy_rect.visible is not that it has an object_name, but it has an id property.

So what you can do in pusfofefe/harbour-pusfofefe.qml is either setting an alias:

ApplicationWindow { id: app
...
// variant A:
property alias b_rect: busy_rect
// variant B:
property alias b_visible: busy_rect.visible
Rectangle { id: busy_rect; visible: false }
...
}

and in your Dialog:

Dialog {
...
onSomethingChanged: app.b_rect.visible = true
onSomethingElseChanged: app.b_visible = true

}

You can also do it the other way around, defining a “global” property in app, and binding that to the visible attribute. This way more than one component can react to the value of that property, and you don’t need to know the id of the child object:

ApplicationWindow { id: app
...
property bool show_blue_rects: true
property bool show_green_rects: false

Rectangle { 
    color: "blue"
    visible: app.show_blue_rects
}

Rectangle { 
    color: "green"
    visible: app.show_green_rects

}

Label { 
    text: "Both are visible!"
    visible: app.show_blue_rects && app.show_green_rects
}
2 Likes

It is important to note that these examples work specifically by using app.foo.
properties defined in the ApplicationWindow are visible to (almost) all other components, because all components are children of app.

In the case of other components, the scope of the id property is different depending on where these objects have been defined.

E.g. here, dialog_a can not “see” the properties of dialog_b

ApplicationWindow { id: app

onEventA: pageStack.push("Dialog.qml", { "id": dialog_a })
onEventB: pageStack.push("Dialog.qml", { "id": dialog_b })

}

Or here, the button can not toggle the color of the rectangle:

ApplicationWindow { id: app

    Item { id: content
        Rectangle { id: bluerect; color: "blue" }
    }
    Item { id: buttons
        Button { onClicked: bluerect.color = "red" } // does not work
    }

}

As you may have seen, i don’t use objectName at all. I had no idea it existed.

Instead i assign the accepted action in the enclosing component.
Or more specifically, connect that signal(?) to a a function defined there, like this:

Though i really have no formal understanding of how scoping is supposed to work - i have been told that similar things shouldn’t actually work.

Sometimes i have resorted to defining a random signal somewhere way up in the ApplicationWindow and triggering (on) that.

One more thing to note is that assigning a property through JavaScript destroys any binding that property may have.

E.g.:

Rectangle { id: rect
    property bool colored: true
    color: colored ? "blue" : "transparent"
}

Button {
   onClicked: rect.colored = !rect.colored // toggles blue and transparent
}
Button {
   onClicked: rect.color = "red" // makes it red but removes the binding to "colored"
}

Thanks for all the comments.

@attah I just figured out I’m getting at the attributes through the object’s id, something I also thought was not possible but perhaps because I’ve declared this specific components at the top level it’s no issue :person_shrugging: (For my previous version in Common Lisp (which used C++ wrappers) I did very much need objectName.)

@nephros Thanks for the info on scoping and bindings. It’s pretty all confusing. edit: ah your initial reply has way more info, I missed that initially for some reason!

It is a pity the QML / QT access from JS is still pretty limited. At least in the QT version we’re using. One would think easily getting at another object would be one of the first things to be supported.

There are actually a lot of ways you can create and interact with QT objects in JS. I’m no expert, but a number of methods can be seen in tooter:

and look at how those ListModel objects are used in Main.qml and the ‘MyList.qml’ component.

Using notifications, signals and QtObjets you can do anything you want, but you have to get your head around the model. Which I need to redo every quarter :0

1 Like

Right, last time for me was 3 years ago :wink:

1 Like

@attah pssst, it’s Devember BTW :wink:

Yes, arrgh. I’m doing a thermal camera app with opencv apparently.

3 Likes