how to access properties of item inside loader object - qt

I am using a qml Loader component to push a page dynamically into view. Now the page I push has various properties and I would like to access them from the Loader component itself. However, I am unable to create aliases to these properties. So, I have something like:
Loader {
id: loginLoader
source: "qrc:/pages/IdPinLoginPage.qml"
property alias hasNavBar: loginLoader.item.hasNavBar
property alias hasStatusBar: loginLoader.item.hasStatusBar
}
This results in Invalid alias target location. How can I redirect these properties to the parent level in the Loader component?

I recommended you to use sourceComponent instead of source property of the Loader for assign/bind value to loaded item easily.
Also for access loaded item, you can use item property of the loader:
Loader {
id: loader
sourceComponent: Component {
SampleItem {
foo: 13 //assign values to loaded item properties
bar: "aleyk!"
}
}
}
Text {
text: loader.item.bar //Access to loaded item properties
}
SampleItem.qml
import QtQuick 2.6
Item {
property int foo: 0
property string bar: "salam";
}

Related

MapQuickItem loaded with Loader does not display

I am building a mapping program that eventually wants to display a different item on the map based on a value within the model. To facilitate this, I'm using Loader in my MapItemView delegate.
Unfortunately, when I tested this, the Loader method displayed nothing.
MapItemDelegate.qml
MapQuickItem {
id: waypoint
anchorPoint.x: image.width/2
anchorPoint.y: image.height/2
coordinate: task.waypoint //task extends QObject with members taskname and waypoint
sourceItem: Grid: {
columns: 1
rows:2
horizontalItemAlignment: Grid.AlignHCenter
Image {
id: image
source: "Waypoint.png"
height: 32
width: 32
}
Text {
id: text
text: task.taskname
}
}
}
main.qml
MapItemView {
id: taskview
model: tasklistmodel //Extends AbstractListModel to contain task objects
delegate: Component {
//Desired method, fails to display.
Loader {
source:"MapItemDelegate.qml"
// Will eventually be:
// source: task.typename+"MapItemDelegate.qml" to facilitate several types.
}
//Control method. Works fine, but does not meet my requirements.
MapItemDelegate {
//Nothing further needed.
}
}
}
How do I get MapItemView to actually display MapQuickItems loaded with Loader? Is there another method of dynamically loading delegates for display?
As it turns out, using a Loader within a delegate of MapItemView is not possible, as the API demands the Component contain a single mapping object. While Loader did load a mapping object, it is not in itself a mapping object, which caused the object to fail to display.
What I ended up doing is implementing a custom QSortFilterProxyModel that overrides filterAcceptsRow to filter tasklistmodel to match the task.typename property, then editing my MapItemView as follows:
MapItemView {
id:taskview
model: TaskListFilterModel { // custom extension of QSortFilterProxyModel
acceptType:"dothisthingtype"
sourceModel: tasklistmodel
}
delegate:MapItemDelegate {
}
}
With that, I can add a MapItemView for each type of task I plan to add in the future. While it's far less elegant and way more work than I had hoped to achieve with Loader, it does at least meet my requirements.

Re-using Component of TextField responding to wheel events, with signal handling

I enriched a TextField (displaying a float) so that it can be changed by mouse wheel, all while being still editable by hand.
I found the quirk with forceActiveFocus and onClicked here (I wish I could just let all events pass down the widget stack like in Qt) and use onWheel to change the value (please let me know if this is not the best way to do this):
TextField{
text: cxxObject.floatAttribute.toString()
onEditingFinished: { cxxObject.floatAttribute=parseFloat(text); }
MouseArea{
anchors.fill: parent
propagateComposedEvent: true
onClicked: { parent.forceActiveFocus(); }
onWheel: { parent.text=parseFloat(parent.text)-.5*wheel.angleDelta.y/120;
parent.editingFinished();
}
}
I would like to re-use this component instead of TextField in several places (without copy&paste), so I tried to declare the component like this:
Component{
id: wheeledFloatTextField
property real initValue: 0.
property real dWheel: 0.5
signal editingFinished(real value);
TextField{
text: parent.initValue.toString();
// re-emit signal to the component
// so that user-defined slot can be defined when re-used
onEditingFinished: parent.editingFinished(parseFloat(text));
// validator: ...
MouseArea{
anchors.fill: parent
propagateComposedEvents: true
onClicked: { parent.forceActiveFocus(); }
onWheel: {
parent.text=parseFloat(parent.text)-parent.parent.dWheel*wheel.angleDelta.y/120;
parent.editingFinished();
}
}
}
}
and re-use:
Loader{
sourceComponent: wheeledFloatTextField
initValue: cxxObject.floatAttribute;
onEditingFinished: { cxxObject.floatAttribute=value; }
}
I am however getting (at the line where Component is used):
Component objects cannot declare new properties.
What is wrong? I was some posts (like How do you assign a QML Item to a component property in QML and then use that object inside the component? and https://developer.blackberry.com/native/documentation/dev/custom_components/index.html) from which I am gathering I might need to wrap the inside of Component (which is as-if it were a separate .qml file and does not define a scope) in something like Item or Container but I am not sure what to do. Any hint?
I would like to keep the definition inline first, later move to a separate file.
If you have the component declared in a separate file, you can (should) omit the top-level Component. For maximum reusability of components, it is reccomended to declare them in a separate file.
A Component can not have any properties declared. It is basically stopping the object creation in a prototypical state. That is useful if you want to configure the object, for later creation, for example lazy initialization (delegates).
If you have a property of type Component and you use the myProp: SomeType {...} syntax, it will automatically just create a component from that.
I think the best solution is to put your TextField in a seperate file, and add the properties to the root-node so it is customizable.
File1 (e.g. "CustomTextField.qml")
TextField{
property real initValue: 0.
property real dWheel: 0.5
signal editingFinished(real value);
text: initValue.toString();
// re-emit signal to the component
// so that user-defined slot can be defined when re-used
onEditingFinished: editingFinished(parseFloat(text));
// validator: ...
MouseArea{
anchors.fill: parent
propagateComposedEvents: true
onClicked: { parent.forceActiveFocus(); }
onWheel: {
parent.text=parseFloat(parent.text)-parent.parent.dWheel*wheel.angleDelta.y/120;
parent.editingFinished();
}
}
}
You can then reuse the Component in all known ways like in a Loader:
Loader {
sourceComponent: CustomTextField { // Property type is component, so it automatically creates a Component instead of the full-blown object, until it is loaded.
initValue: 12
dWheel: 42
}
...
}
or without Loader
CustomTextField {
...
}
Of course you can keep it inline, but even then, you have to add the properties to the root-element inside the Component.
Component {
id: componentId // only thing you can set besides one Object in a Component
TextField{
id: componentRoot // You can't reference this id from outside the Component!!!
property real initValue: 0.
property real dWheel: 0.5
signal editingFinished(real value);
text: initValue.toString();
// re-emit signal to the component
// so that user-defined slot can be defined when re-used
onEditingFinished: editingFinished(parseFloat(text));
// validator: ...
MouseArea{
anchors.fill: parent
propagateComposedEvents: true
onClicked: { parent.forceActiveFocus(); }
onWheel: {
parent.text=parseFloat(parent.text)-parent.parent.dWheel*wheel.angleDelta.y/120;
parent.editingFinished();
}
}
}
}
This has the down-side that you will always need a separate object to instantiate the Component, like a Loader which adds overhead and complicates communication in the file between the objects, since to address it, you will need to use: loaderId.item.property which might be expensive in lookup, you need to ensure that item is defined e.t.c.

QML pass QtObject from one qml to another

I've been trying to wrap my head around this one for a while and came up with a few hacks, but none of them seem to be the Right Way. Hopefully this makes sense. Say I have three qml files.
First QML:
...
ListView {
id: patientList
model: patientModel
delegate: E3DPatientListItem {
onClicked: {
if (patientList.currentIndex !== index)
{
patientList.currentIndex = index
detailStackView.push("DetailPanel.qml",
{ "view": view, "ptInfo": model })
...
DetailPanel:
...
Page {
property QtObject ptInfo
Timeline {
ptInfo: ptInfo // <- how do I pass this to Timeline?
...
Timeline.qml
...
Item {
property QtObject ptInfo // <- always null :(
...
...
Page {
property QtObject ptInfo
Timeline {
ptInfo: ptInfo // <- how do I pass this to Timeline?
...
What do you suppose ptInfo: ptInfo is achieving? You are binding the property to its own identifier.
Maybe try not using the same identifier to avoid shadowing, or give the Page and id and then ptInfo: pageid.ptInfo. parent.ptInfo will also work as long as the object has the parent property exposed, keep in mind that QtObject does not.
However, you don't really need to have property QtObject ptInfo in Timeline.qml, as long as a timeline is always instantiated inside a page, you can access ptInfo directly from inside the timeline because of dynamic scoping. Note that dynamic scope properties will only work for properties that are defined in the root element of the particular qml file:
// Obj1.qml
Item {
property int test: 7
Obj2{}
}
// Obj2.qml
Item {
Component.onCompleted: console.log(test) // it will work
}

Reusing a delegate component in QML

I came across a weird problem when designing my application in QML. The following code works:
TableView {
itemDelegate: Item {
function a() {}
Loader {}
}
}
I have a bunch of functions, properties, and loaders in this item delegate which is an Item object. Problems arise when I try to reuse this delegate in a ListView. I can't reference it like this:
Item {
id: myitem
function a() {}
Loader {}
}
TableView {
itemDelegate: myitem
}
error: Unable to assign QQuickItem to QQmlComponent
This is because itemDelegate is a Component:
http://qt-project.org/doc/qt-5/qml-qtquick-controls-tableview.html#itemDelegate-prop
So QML can convert an Item to Component when it's embedded, but not when it's referenced.
And I can't make it a component, since components can't have functions, loaders, properties etc.
How can I reuse my delegate?
Solved by wrapping the item inside the component:
Component {
Item{...}
}

Navigating and addressing QMLComponents

I am working on a desktop application the uses a QML GUI with a lot of QML Components.
These are parts of the hierarchy:
main -> toolbar -> searchbar -> editfield
and
main -> resultlist -> header -> button1
I could not find a way to access the text contents of editfield in a signal handler for button1. Is it possible to do that in QML or Javascript?
I know I can access the element in the C++ part by using the objectName property.
Due to QML uses dynamic scoping (→ Doc), child elements can access the properties of all ancestors
and it doesn't matter if they are in different files.
So you could add an editFieldText property to main and bind the
text property of editfield to it. Then you can access editFieldText
from everywhere:
//=== main.qml ===
import QtQuick 1.0
Rectangle {
id: main
property string editFieldText
Toolbar {
// [...]
}
Resultlist {
// [...]
}
}
//=== EditField.qml ===
import QtQuick 1.0
TextInput {
// bind text property to main.editFieldText
Binding {
target: main;
property: "editFieldText";
value: text
}
}
//=== Header.qml ===
import QtQuick 1.0
Rectangle {
Button {
onClick: {
console.log(main.editFieldText);
// or simply
console.log(editFieldText);
}
}
}
You can use alias properties to have the editfield.text as a property of main. This property should be accessible from button1.

Resources