Multi 'page' QML application issues with external QML/Javascript
I am working on a multi-page Qt/QML/C++ app.
The structure is as follows:
In root folder:
main.cpp ====> contains the 'main' startup code
fileio.cpp ====> contains all my file I/O routines
gpsio.cpp ====> contains my GPS on/off/update routines
main.qml ====> 'main' window, contains the HeaderRow & FooterRow, with an empty Rectangle ('winRect') filling the available space between the two... includes a 'property alias' for itself as 'appWin'
HeaderRow.qml ====> contains app title, version, etc... static data
FooterRow.qml ====> contains navigation buttons: Home, Settings, Upload, Status, Quit
functions.js ====> contains 'common' javascript functions utilized in all QML files
"SubPages" folder under the root:
SubPages/MainWin.qml ====> contains the 'main' interface with GPS start/stop button, various feedback text fields
SubPages/InfoWin.qml ====> contains 'status' text fields
SubPages/SettingsWin.qml====> contains application 'settings'
SubPages/UploadWin.qml ====> contains file list for uploading to server
How it works:
main.qml uses 'import' on the functions.js and the SubPages folder to get access to all functions and all 'child' windows.
FooterRow buttons 'load' the required 'page' into the 'winRect' on pressed using a javascript 'loadPage' function:
Code:
function loadPage(page){
appWin.currentPage = "%1".arg(page);
}
and in main.qml:
Code:
// Set this property to another file name to change page
property string currentPage : "SubPages/MainWin";
// Put the name of the QML files containing your pages (without the '.qml')
property variant pagesList : [
"SubPages/MainWin",
"SubPages/InfoWin",
"SubPages/SettingsWin",
"SubPages/UploadWin"
];
Rectangle {
id: winRect
visible: true
anchors.right: parent.right
anchors.left: parent.left
anchors.top: header.bottom
anchors.bottom: footer.top
color: "black"
Repeater {
model: pagesList;
delegate: Loader {
active: false;
asynchronous: true;
anchors.fill: parent;
visible: (currentPage === modelData);
source: "%1.qml".arg(modelData)
onVisibleChanged: { loadIfNotLoaded(); }
Component.onCompleted: { loadIfNotLoaded(); }
function loadIfNotLoaded () {
// to load the file at first show
if (visible && !active) {
active = true;
}
}
}
}
}
The issues I am having...
1. Each child page is set up with all its components spaced according to the overall size of the 'parent', which is 'winRect'. However, on loading a child page, I get warnings about not being able to anchor to winRect, as it's not really the parent of the child. So, how can I make winRect the parent of each child page?
2. In main.qml, I use "import 'SubPages'" to get access to the child pages. But, if I try to do anything in main.qml with a page by name (ie. MainWin), I get an error: 'SubPages not defined'... Why?
3. My javascript functions modify objects on my QML child pages. Or at least, they are supposed to... I get errors about each page not being defined within the javascript function. To get around this, I tried to pass the child page into the function as an argument, but then I get #2 above.
4. Am I crazy? Is there a better way to create such an application? Do I need to have ALL my child pages defined in the main.qml somehow?
5. HELP!
--Sam
Re: Multi 'page' QML application issues with external QML/Javascript
Quote:
Originally Posted by
scgrant327
1. Each child page is set up with all its components spaced according to the overall size of the 'parent', which is 'winRect'. However, on loading a child page, I get warnings about not being able to anchor to winRect, as it's not really the parent of the child. So, how can I make winRect the parent of each child page?
The Loader will resize its item to its own size, so just bind the width/height of each Loader to the rectangle's properties.
Quote:
Originally Posted by
scgrant327
2. In main.qml, I use "import 'SubPages'" to get access to the child pages. But, if I try to do anything in main.qml with a page by name (ie. MainWin), I get an error: 'SubPages not defined'... Why?
The import gives you access to the elements defined in the QML files in that directory, e.g.
If you need namespacing use the "import as" syntax
Code:
import "SubPages" as SubPages
SubPages.MainWin {
}
Not sure why you need the import at all since you are using Loaders to load them, not direct instantiation.
Quote:
Originally Posted by
scgrant327
3. My javascript functions modify objects on my QML child pages. Or at least, they are supposed to... I get errors about each page not being defined within the javascript function. To get around this, I tried to pass the child page into the function as an argument, but then I get #2 above.
I am afraid I didn't understand that one.
Quote:
Originally Posted by
scgrant327
4. Am I crazy? Is there a better way to create such an application? Do I need to have ALL my child pages defined in the main.qml somehow?
As far as I can tell it looks like a good approach for "loading pages on first use"
Cheers,
_
Re: Multi 'page' QML application issues with external QML/Javascript
Doing either import "SubPages" or import "SubPages" as SubPages causes the same error, saying that each page I want to access is not defined.
As I see it, I have to import the pages, because on application startup, my gpsio.cpp function gets the users current location, and then updates the text fields in the MainWin page... BEFORE MainWin is even loaded... via a javascript function that is called from gpsio.cpp:
Code:
void gpsio::stopOneShotGPS()
{
sourceOneShot->stopUpdates();
QObject *root
= myFileIO
->engine.
rootObjects().
first();
QMetaObject::invokeMethod(root,
"gpsOneShotUpdate",Q_RETURN_ARG
(QVariant, returnedVal
));
====> calls javascript function to update text fields
root->setProperty("myFilePath",myFileIO->myFilePath);
root
->setProperty
("myProdType",
QSysInfo::productType());
root
->setProperty
("myProdVers",
QSysInfo::productVersion());
}
"gpsOneShotUpdate" is exposed to the C++ via main.qml and defined in functions.js:
Code:
function gpsOneShotUpdate() {
mainWin.timeLabel.text = "";
mainWin.latLabel.text = appWin.currentLat;
mainWin.lonLabel.text = appWin.currentLon;
mainWin.accyLabel.text = "---";
mainWin.hdgLabel.text = "---";
mainWin.spdLabel.text = "---";
mainWin.cntLabel.text = "---";
mainWin.statusImage.source = "status_64x128_grey.png";
loadPage("SubPages/MainWin");
}
Re: Multi 'page' QML application issues with external QML/Javascript
Quote:
Originally Posted by
scgrant327
Doing either import "SubPages" or import "SubPages" as SubPages causes the same error, saying that each page I want to access is not defined.
You haven't used any of the elements of that directory in the code you provided so far, so it is hard to tell what could be wrong.
Quote:
Originally Posted by
scgrant327
As I see it, I have to import the pages, because on application startup, my gpsio.cpp function gets the users current location, and then updates the text fields in the MainWin page... BEFORE MainWin is even loaded... via a javascript function that is called from gpsio.cpp:
Ah, I remember now. You are trying to create an artifically complex situation. Any specific reason for that?
Cheers,
_
Re: Multi 'page' QML application issues with external QML/Javascript
Quote:
Originally Posted by
anda_skoa
You haven't used any of the elements of that directory in the code you provided so far, so it is hard to tell what could be wrong.
Fair enough. To get where my UI is updated as detailed below, I was trying to pass the QML to my javascript function. So, I import the SubPages folder and pass the MainWin.qml to my javascript, but that results in the MainWin undefined error:
Code:
function gpsOneShotUpdate() {
Funcs.gpsOneShotUpdate(MainWin)
}
Quote:
Originally Posted by
anda_skoa
Ah, I remember now. You are trying to create an artifically complex situation. Any specific reason for that?
Not trying to complicate things...
This is for use on mobile devices... On startup, my app needs to get the users location (which is done via a gpsio.cpp function). Once that location is determined, the app needs to update the UI with that location. This initial location needs to be determined before the initial 'Main Window' is visible.
So... C++ gets location and sets main.qml variables. Then C++ calls main.qml function to update UI on SubPage qml. main.qml function in turn calls javascript function which does the real Text object updates on SubPage qml.
Re: Multi 'page' QML application issues with external QML/Javascript
Quote:
Originally Posted by
scgrant327
Fair enough. To get where my UI is updated as detailed below, I was trying to pass the QML to my javascript function. So, I import the SubPages folder and pass the MainWin.qml to my javascript, but that results in the MainWin undefined error:
Code:
function gpsOneShotUpdate() {
Funcs.gpsOneShotUpdate(MainWin)
}
You are not passing MainWin.qml but MainWin.
Did you want to pass an instance of MainWin?
I.e. you are trying the C++ equivalent of passing a class name as a function argument.
Quote:
Originally Posted by
scgrant327
Not trying to complicate things...
Well, exporting the values is far easier than to call into QML.
Quote:
Originally Posted by
scgrant327
This is for use on mobile devices... On startup, my app needs to get the users location (which is done via a gpsio.cpp function). Once that location is determined, the app needs to update the UI with that location. This initial location needs to be determined before the initial 'Main Window' is visible.
So just gather the value and set it as a context property before loading main.qml?
Quote:
Originally Posted by
scgrant327
So... C++ gets location and sets main.qml variables.
Which is not jsut very uncommon (creating a C++ -> QML dependency instead of just the other way around), it is also more complicated.
Quote:
Originally Posted by
scgrant327
Then C++ calls main.qml function to update UI on SubPage qml. main.qml function in turn calls javascript function which does the real Text object updates on SubPage qml.
Quite complicated, isn't it?
Even if values would change after loading the QML, exposing them via properties on an object would still be easier by far.
The only thing that it is not so easy is to get the equivalent of the return value.
But you don't seem to either return something from the function nor do you try to use the returned value. Quite puzzling why you even retrieve it.
Cheers,
_
Re: Multi 'page' QML application issues with external QML/Javascript
Quote:
Did you want to pass an instance of MainWin?
Yes! I guess?
Quote:
So just gather the value and set it as a context property before loading main.qml?
Going to mod my code and load main.qml (and additionally MainWin.qml inside main.qml), and then use the gpsio function to get and set the values. I am already exposing the necessary variables from main.qml to C++, so the gpsio function can update them as needed.
Quote:
Which is not jsut very uncommon (creating a C++ -> QML dependency instead of just the other way around), it is also more complicated.
...and 100% necessary. In the end, there will be a gpsio function that continuously updates the QML UI with new values.
Quote:
The only thing that it is not so easy is to get the equivalent of the return value.
But you don't seem to either return something from the function nor do you try to use the returned value. Quite puzzling why you even retrieve it.
The return value is not necessary. It is left over from me just trying to get everything to work....and copy/pasting code from online examples. The return value can and will be deleted.
Re: Multi 'page' QML application issues with external QML/Javascript
Quote:
Originally Posted by
scgrant327
Yes! I guess?
Then you need to pass the id of the instance or the name of the property holding the instance.
In C++ you would also pass the name of the variable that hold the pointer to the object, not the class name, right? :)
Quote:
Originally Posted by
scgrant327
I am already exposing the necessary variables from main.qml to C++, so the gpsio function can update them as needed.
I was talking about exposing the C++ values to QML.
Quote:
Originally Posted by
scgrant327
...and 100% necessary. In the end, there will be a gpsio function that continuously updates the QML UI with new values.
Not necessary at all, that is what property bindings are for.
Quote:
Originally Posted by
scgrant327
The return value is not necessary. It is left over from me just trying to get everything to work....and copy/pasting code from online examples. The return value can and will be deleted.
I see.
Then it is really unnecessary to call a JavaScript function from C++. Getting a return value is basically the only use case for that.
Cheers,
_
Re: Multi 'page' QML application issues with external QML/Javascript
Ok...some progress...
Property bindings look like they will work fine for my purpose.
Issues:
When calling my javascript functions, why can I not access my QML objects? I get errors about the object being undefined. I've tried by using the alias for the object, the id for the object and the QML file name that houses the object....all give the same error.
AND... do I need to 'import' the parent and SubPages folders into each of my SubPages/*.qml files?
I ask because if I try to reference an object in SubPages/Page1.qml from SubPages/Page2.qml, I get an error about that object being undefined.
For instance, when I load SubPages/Page2.qml using the loader I showed earlier. Page2 has a Label that when clicked on will change a value on Page1. But, if I try this I get an error:
In Page2:
Code:
MouseArea {
anchors.bottom: parent.bottom
anchors.right: parent.right
Label {
id: page2Label
text: qsTr("ClickME")
horizontalAlignment: Text.AlignRight
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
font.pointSize: 24
color: "lime"
}
onClicked: {
Page1.page1Label.text = page2Label.text
Page1.page1Label.color = "lime"
page2Label.color = "darkred"
}
}
Re: Multi 'page' QML application issues with external QML/Javascript
Quote:
Originally Posted by
scgrant327
Property bindings look like they will work fine for my purpose.
Excellent, bindings on C++ properties is definitely the way to go.
Way more elegant than making C++ code dependent on QML code.
Quote:
Originally Posted by
scgrant327
When calling my javascript functions, why can I not access my QML objects? I get errors about the object being undefined. I've tried by using the alias for the object, the id for the object and the QML file name that houses the object....all give the same error.
Can you show such a function and where the objects are defined?
For example your gpsOneShotUpdate() function should be fine if there is an object with id mainWin in the same file or a property with the name mainWin in the object the function is in.
Quote:
Originally Posted by
scgrant327
AND... do I need to 'import' the parent and SubPages folders into each of my SubPages/*.qml files?
import allows you access to types, just like includes in C++.
The current directory, i.e. the directory the current QML file is in, is always implicitly imported.
In your case you don't need to import SubPages in main.qml since you are using a Loader, you are not instantiating objects of these types directly in QML code.
Quote:
Originally Posted by
scgrant327
I ask because if I try to reference an object in SubPages/Page1.qml from SubPages/Page2.qml, I get an error about that object being undefined.
Access to objects has nothing to do with imports.
Quote:
Originally Posted by
scgrant327
For instance, when I load SubPages/Page2.qml using the loader I showed earlier. Page2 has a Label that when clicked on will change a value on Page1. But, if I try this I get an error:
In Page2:
Code:
onClicked: {
Page1.page1Label.text = page2Label.text
Page1.page1Label.color = "lime"
page2Label.color = "darkred"
}
}
Page1 is not a valid id nor a valid property name (both need to start with a lower case letter).
Cheers,
_
Re: Multi 'page' QML application issues with external QML/Javascript
Ok, I ended up declaring non-visible instances of the SubPages in my main.qml. Works pretty well with my custom 'loader' javascript function to show/hide each page on demand.
Thanks for the help!
Re: Multi 'page' QML application issues with external QML/Javascript
You really like doing things imperatively :-)
However, you might want to look into doing things declaratively for future projects, it really pays off and makes things easier.
Cheers,
_