PDA

View Full Version : QML App with toolbar and subView?



ChriD
20th January 2016, 16:24
Hi,

I am creating my first QML application and i am a Little bit confused.
i want to have an application window with a toolbar and a Container where i can Show "SUB" views (maybe StackView as container) which will Show me other views which i want to load via C++

So i am able to create an Application Window with the QQmlApplicationEngine (with QQuickView i can't create a Toolbar because the view itself creates an application window) and to load my main.qml file.
The Toolbar is shown. Thats good.
Now i want to add the "Container view" which should show qml files i want to set/load with C++
That means first i will show a ListView, after a list view item is clicked i want to show another view, aso...

Each qml view should have a C++ Class derived from QObject and this class will handle the signals from the current qml file loaded in the "Container view"

an example with pseudo code:



QML
ApplicationWindow {
toolBar: Rectangle {
...
}

ContentView {
id: containerView
}

}





C++
QQmlApplicationEngine engine
appEngine.load(QUrl(QStringLiteral("qrc:/main.qml")));
// is it possible to have something like a Container view ant to get it from the appEngine?
containerView = appEngine.getObject("containerView");

// ist it possible to set the dataContext for the load qml for the view model?
context = new DateContext();
containerView.setSource("qml", context);

// is it possible to connect the signals from the qml to a C++ object ??
QObject::connect(qtVewObject, SIGNAL(sigTest(QString)), classObjectWithReceivesSignals, SLOT(slotSignalTest(QString)));




Is there a nice way to achieve this.
I've looked around in the Internet but i dont really understand if there is such thing like a "Container view" which i can embedd in the main QML and where i can load other qml files.
Maybe i can use a StackView for the Container view but i do not Need the ability of pushing an popping views (i will do that by my own)

Are there any suggestions for me?
THANKS!

anda_skoa
20th January 2016, 18:26
A Loader element can load any QML file and will resize that QML file's root item to its own size.

You can easily export a C++ object with a property that holds that QML file name and bind the loader's source property to it.
The Loader will load the new file whenver the C++ object changes the property.

Cheers,
_

ChriD
20th January 2016, 19:16
Hi, Thank You for your answer!
So it should be something like this?!




C++
class View : public QObject
{
Q_OBJECT
Q_PROPERTY( QString qmlViewPath READ getQmlViewPath )

public:
explicit View(QObject *parent = 0);
virtual QString getQmlViewPath();
};
}



void main()
{
View firstView;
View secondView;
QQmlApplicationEngine appEngine;
appEngine.load(QUrl(QStringLiteral("qrc:/main.qml")));
// Show view 1
appEngine.rootContext()->setContextProperty("viewClassObject", (QObject *)&firstView);
// ...
// Show view 2
appEngine.rootContext()->setContextProperty("viewClassObject", (QObject *)&secondView);
// ...

}




QML
ApplicationWindow {
visible: true

toolBar: Rectangle{
....
}

Loader {
id: loaderViewContainer
anchors.fill: parent
focus: true
source: viewClassObject.qmlViewPath
}

}



Would the change of the context of the root force the loader to load/reload the qml?
Or would it be better to create a "ViewController" class which contains all the viewsClasses with their contexts so i only have to set the root context (ViewController) once and then set the current view context for each view
e.g.:



void main()
{
ViewController viewController;
QQmlApplicationEngine appEngine;
appEngine.load(QUrl(QStringLiteral("qrc:/main.qml")));
// Show view which is provided by the viewControllerClass as property
appEngine.rootContext()->setContextProperty("viewControllerObject", (QObject *)&viewController);
}


changeViewSignalReceipt()
{
// view will be changed automatically by property of the view Controller, but we have to set the context for the loaded view
appEngine.rootContext()->setContextProperty("viewClassObject", (QObject *)&secondView);
// or do i have to set the "view class" context to the loaderObject?
}



Is it possible to set multiple ContextProperties to the rootContext, or do i have to get the Loader Object and set the view Context there?
And how would i get the loader Object from the QML to set its context. Or can i bind the context of the loader object from the viewController?

anda_skoa
21st January 2016, 06:56
So it should be something like this?!

Not quite.
Instead of setting a second object with the same id, you would change the value of the property of the already exported object.
The Q_PROPERTY will need a NOTIFY signal so that the C++ object can tell teh QML engine that the property has to be re-read.



Is it possible to set multiple ContextProperties to the rootContext, or do i have to get the Loader Object and set the view Context there?

You can set many objects as long as their "names" are unique (basically like id values in QML).
You can also export a list of objects or even a model.



And how would i get the loader Object from the QML to set its context. Or can i bind the context of the loader object from the viewController?

I don't quite get what you mean there.
What you do internally in your exported object when it changes the view path is up to you.

Cheers,
_

ChriD
21st January 2016, 17:53
Thank you very much for the info

I managed to create a "viewController" Class which will be the context of the main qml and where the loader source is binded to an Attribute of the class and it works nice.





And how would i get the loader Object from the QML to set its context. Or can i bind the context of the loader object from the viewController?

I don't quite get what you mean there.
What you do internally in your exported object when it changes the view path is up to you.

Due the fact that i can set multiple context on a qml view i do not have to get the Loader element to set the data context to the "subView"
So this question is outdated :)

But now i have another Problem :(
I have a QML which is loaded in the LOader element and this contains a ListView with a Delegate and a Model which is given from C++ Class
This works. Now i am trying to get a Signal from the listView to the C++ Object

i have added a Signal in the ListView-Delegate and i emit the signal when i am doing a click on an item



QML FILE: StandardListViewDelegate
Item {
id: listViewDelegateRoot
width: parent.width
height: 88

property alias itemId: itemId.itemId

signal sigItemClicked(var _itemId)

...

Item {
id: itemId
property string itemId: modelData
}

MouseArea {
id: mouse
anchors.fill: parent
onClicked: listViewDelegateRoot.sigItemClicked(itemId)

}

}


Do i have to connect to the Signal in the main QML file so that i can connect it to the C++ Object Slot like eg.:
(But that code throws me the error: "Cannot assign to non-existent property "sigItemClicked""


Rectangle {
Id: subView

signal sigModuleSelected(var _moduleId)

ListView {
model: moduleListObject
anchors.fill: parent
delegate: StandardListViewDelegate {
itemId: model.modelData.moduleId
itemText: model.modelData.name
sigItemClicked: sigModuleSelected(_itemId)
}
}
}



Or can i connect the Delegate Signal directly to a c++ Slot with Object::connect

And the main question is... How do i get the rootObjec(Qobject) where i have to connect to?!
I have the "QQmlApplicationEngine" object where i can get all Objects but should i do something like?



C++
QObject *subViewObject= object->findChild<QObject*>("subView");
QObject::connect(subViewObject, SIGNAL(sigModuleSelected(QString)), this, SLOT(onModuleSelected(QString)));



i know i am not very good in explaining things that i want to do but i hope you can understand my question :)

Added after 1 16 minutes:

Okay,

I managed to do some stuff by myself :)
The only Problem that persists is that i want to Redirect a Signal from a ListView delegate to ist parent Container
I don't know how to do that

Added after 23 minutes:

Ahhh damit :)
I've found my Problem.

Instead of doing this



Rectangle {

signal sigModuleSelected(string _moduleId)

ListView {
id: moduleListViewObject
model: moduleListObject
anchors.fill: parent
delegate: StandardListViewDelegate {
id: moduleListViewDelegate
itemId: model.modelData.moduleId
itemText: model.modelData.name
sigItemClicked: { sigModuleSelected(_itemId) }
}
}


i have to use



Rectangle {

signal sigModuleSelected(string _moduleId)

ListView {
id: moduleListViewObject
model: moduleListObject
anchors.fill: parent
delegate: StandardListViewDelegate {
id: moduleListViewDelegate
itemId: model.modelData.moduleId
itemText: model.modelData.name
onSigItemClicked: { sigModuleSelected(_itemId) }
}
}

anda_skoa
21st January 2016, 19:24
In any case you don't need to connect any signal.
A signal automatically gets a signal handler on the element it can be emitted from.
You can easily call the C++ slot from there.

Cheers,
_

ChriD
22nd January 2016, 19:04
Thank you very much!
Is there a way to Close the Thread or to mark it as Solved?

anda_skoa
22nd January 2016, 19:20
No, just leave it as it is.

Cheers,
_