PDA

View Full Version : Access QML Object between QML & Javascript



scgrant327
7th September 2016, 13:59
I have the following structure for my project:

--rootFolder
main.qml
header.qml
footer.qml
functions.js
----subFolder
page1.qml
page2.qml

main.qml imports functions.js and subFolder to get access to all my javascript functions and the objects in page1 and page2. main.qml uses a StackView to load the pages as necessary.

page1 & page2 also import functions.js.

My question is... How can I access objects in page1 & page2 from my javascript? I have tried using 'property alias page1: page1' in my pages, which appears to be seen from functions.js...but at runtime is undefined.

So... How can I access objects in page1 and page2 from my javascript?

anda_skoa
7th September 2016, 15:16
If you load "page1.qml", for example, using a Loader, then you can access that item as the loader's "item".

But in general it might be wiser to just pass the object to the function when it is being called, as this makes the caller responsible for the correct input/context data not the callee.

This has the advantage of making the call more explicit, i.e. what it actually needs, and easier testable, i.e.no hidden dependencies that a test will also have to provide.

Cheers,
_

scgrant327
7th September 2016, 16:22
The issue with accessing it via the Loader is that the page may NOT be loaded when I want to update the data...

I am leaning toward using a signal fired from JavaScript. Will update this post if it works.

anda_skoa
7th September 2016, 16:50
I don't see a problem there.

If your data updates but some UI that would display the data is not loaded, then that UI doesn't need updating.
It will get the current data when it has been loaded.

In QML both cases are usually automatic anyway, as displayed values come from properties and elements will update themselves it they exist or get the current value when coming into existance.

Cheers,
_

scgrant327
8th September 2016, 14:47
Well, I tried using the signal/slot method... but without any luck.

In my functions.js I have:


var graphNotifier = Qt.createQmlObject('import QtQuick 2.0; QtObject { signal graphDataChanged }', Qt.application, 'GraphNotifier');

and in my GraphWin.qml (which imports functions.js), I have:


Component.onCompleted: {
loadGraphs();
Funcs.graphNotifier.graphDataChanged.connect(loadG raphs);
console.log("loaded: graphWin");
}

Where "loadGraphs()" is a function in javascript that populates the QML XYSeries with the correct (live) data. Calling loadGraphs() works, I get my data... no issues there.

I call/fire the signal in javascript like so:


graphNotifier.graphDataChanged();

once I receive new data... However, the signal does not fire... loadGraphs() does not get called.

Any idea why?

anda_skoa
8th September 2016, 15:02
Well, I tried using the signal/slot method... but without any luck.

Ah, yes, I remember you.
Still trying to make it as complicated as possible just to avoid the native declarative way?



I call/fire the signal in javascript like so:


graphNotifier.graphDataChanged();

Why the indirection? I.e. why not just call loadGraphs() right there?

Cheers,
_

scgrant327
8th September 2016, 17:24
Definitely NOT trying to make things complicated at all... Just trying to make things work!

LoadGraphs is a function within my GraphWin.qml. GraphWin.qml is in a folder called SubPages. Functions.js is in the root project folder. Functions.js is imported into all my QML files, including GraphWin.qml.

My root-level object in GraphWin.qml is a Rectangle with an id of 'graphWin'. I have it aliased out as: property alias graphWin: graphWin.

Now, when I *try* to call loadGraphs() from functions.js (which was my very first option) as such: graphWin.loadGraphs()... I get the autocomplete within QTCreator that appears to indicate that everything is ok... However, at runtime, I get an error indicating that 'graphWin' is undefined when called from within functions.js.

So... HOW do I make that work? I could not figure it out, so I decided to ask this question... NOT trying to complicate anything.

wysota
9th September 2016, 09:52
How about posting a minimal example reproducing the problem? Maybe it will be easier to resolve the issue while looking at real code.

anda_skoa
9th September 2016, 10:10
Definitely NOT trying to make things complicated at all...

Yes you do.
Maybe not conciously but there have now been at least two other threads where you insist on doing things in a very complicated way.
E.g. calling JavaScript functions for "updating the UI" instead of letting the UI update itself, even calling such a function from C++, etc.



Just trying to make things work!

Yes, and it seems with as much work arounds as possible.



My root-level object in GraphWin.qml is a Rectangle with an id of 'graphWin'. I have it aliased out as: property alias graphWin: graphWin.

What does that mean, "aliased out"?
The root level element is accessible in the using context without any alias and its id inside the file doesn't matter.



Now, when I *try* to call loadGraphs() from functions.js (which was my very first option) as such: graphWin.loadGraphs()... I get the autocomplete within QTCreator that appears to indicate that everything is ok... However, at runtime, I get an error indicating that 'graphWin' is undefined when called from within functions.js.

So you are calling the function from a context that knows an object with id "graphWin"?
Earlier you wrote something about a loader.

Does your "aliased out" mean that you have an alias property "graphWin" that is aliased to the "item" property of the loader?



So... HOW do I make that work? I could not figure it out, so I decided to ask this question... NOT trying to complicate anything.

In comment #5 you create a dummy object with a signal, connect a single function to that signal and then call a function on the dummy object to emit the signal, instead of calling the single target function directly.

So instead of


someObject.someFunction()

you want to do


dummy.someFunction() -> dummy.signal -> someObject.someFunction()

And you don't think the second one is more complicated than the first one?

Cheers,
_

scgrant327
9th September 2016, 13:49
Ugh... Gee Thanks.

What I did in the end was push through the headache of figuring out a prior solution Anda had recommended... using removeSeries and createSeries. That works pretty well.

However, I have a new issue that has popped up on iOS. When calling createSeries, I get errors on iOS about AbstractSeries not existing..