dynamically change the model used as base for a qml item - qml

Im starting to work with qtquick 1.1. And i have designed a component consisting mainly out of a pathview.
Rectangle {
id: pathViewElement
PathView {
id: pathView
pathItemCount: 4
preferredHighlightBegin: 0.5
preferredHighlightEnd: 0.5
highlightRangeMode: PathView.StrictlyEnforceRange
model: myModel
delegate: Item {
width: valueText.width
height: 50
scale: 1.0-2*Math.abs(pathViewElement.width/2-(x+width/2)) / pathViewElement.width
opacity: scale
smooth: true
Text {
id: valueText
anchors.centerIn: parent
text: myModel.value
font.pointSize: 35
}
}
path: Path {
startX: 0; startY: 25
PathLine { x: pathViewElement.width; y: 25;}
}
}
}
This PathView is using a model called myModel. Which might be located in any other file.
The question now is the following:
I'm using the same component to be able to change different values. Each of these values is coming with another QML ListModel.
So how can i dynamically change the model used in the PathView (myModel)?
Also, while creating the PathView i can statically set the model using
model: MyListModel{}
where MyListModel is a qmlFile consisting only of a ListModel {} declaration. But when i dynamically create the PathView from within a third file, say MyApplication.qml I cannot set pathViewElement.model: MyListModel{} as the compiler is expecting a ";" instead of {}. Why is this?

So how can i dynamically change the model used in the PathView
(myModel)?
On the occurrence of respective event, you can directly change the model assigned for your view.
eg. Assuming you want this change to be done on click of some mouse button:
onClicked:
{
pathView.model = myNewModel
}
Here, myNewModel is the id for your new model to replace with.
But when i dynamically create the PathView from within a third file,
say MyApplication.qml I cannot set pathViewElement.model:
MyListModel{} as the compiler is expecting a ";" instead of {}. Why is
this?
Can you state this part more clearly ?

Related

How to define QML component inline and override a property?

I'm trying and failing at something seemingly simple: define a simple text formatting component inline, then instantiate it multiple times with different text. Here's the code
Item {
.
.
.
Component {
id: favButtonLabelText
Text {
text: "Blah!"
color: StyleSingleton.xNavPrimaryText
font.family: StyleSingleton.xNavTextFont
font.pointSize: 28
}
}
.
.
.
Loader { sourceComponent: favButtonLabelText; text: "Diameter" }
At the Loader line, the text property is invalid. Attempts to define a property or alias on the component are rejected with "Component objects cannot declare new properties".
The only example I find in the docs, shows overriding the x property of a Rectangle defined in an inline component. It seem to me overriding the text property of a Text element is analogous.
How can I do this?
Since Loader sets itself as the context object for the component it is loading, you could define a property in it and use it in the loaded Item.
However, you have to use a property name not used by your item, otherwise it will be shadowed by your item's own property and there's no easy way to access a context property explicitly.
Component {
id: favButtonLabelText
Text {
text: foobar
}
}
Loader {
sourceComponent: favButtonLabelText
property string foobar: "Diameter"
}
As GrecKo already said, it is possible to use a custom property of the Loader that has another name like in his example foobar.
If you don't do any fancy reparenting of the loaded Item it is also possible to use the same name, and reference it with parent.property
Component {
id: textComponent
Text {
// Use "parent" to reference the property of the parent,
// which is by default the Loader
text: parent.text
}
}
Column {
Repeater {
model: ['hallo welt', 'hello world', 'Bonjour monde', '你好世界']
delegate: Loader {
property string text: modelData
sourceComponent: textComponent
}
}
}
As of Qt 5.15, a new feature has been added: inline Components
As the name suggests, it allows to define an component inline, with the benefits of:
You can create an instance of the component, without the overhead of using a Loader.
You can use the component type in property declarations.
You can refer to the component in other files than the one it is defined in.
Item {
.
.
.
component FavButtonLabelText: Text {
property int aCustomProp: 0
text: "Blah!"
color: StyleSingleton.xNavPrimaryText
font.family: StyleSingleton.xNavTextFont
font.pointSize: 28
}
.
.
.
FavButtonLabelText { text: "myNewText"; aCustomProp: 5 }

How to assign a context-variable to a property with the same name in QML?

This is the result of the following code:
main.qml
import QtQuick 2.8
Item {
Reusable {
index: 1234 // reusable with a custom index
}
ListView {
anchors { fill: parent; margins: 20; topMargin: 50 }
model: 3
// Reusable with an index given by the ListView
delegate: Reusable {
index: index // <- does not work, both 'index' point to
// the index property
}
}
}
Reusable.qml
import QtQuick 2.8
Text {
property int index
text: "Line " + index
}
 Problem description:
The ListView assigns 0, 1, 2, etc. to the variable index on every iteration. However, because I am assigning this to a property, this variable is shadowed and I cannot access it.
If I remove property int index from Reusable.qml, the ListView works, but using Reusable outside of the ListView does not work anymore.
Is there a way to assign index: index?
(I could rename the property which would work, but I would like to avoid that.)
You can address the model related data through a model prefix.
ListView {
model: 3
delegate: Reusable { index: model.index }
}
My recommendation would to do that even if there is no ambiguity, as means to readability. I.e. a developer reading the code can immediately see which data is a local property and which data is provided by the model.

How to get JS eval() to operate in the parent scope of a QML element

If I have some JS code as text and I call it in eval(), I can then use the functions defined by this statement only in the scope of where the eval() occurred. As far as I can tell, this means that any new JS definitions created from the eval are invisible to the rest of QML. Consider this QML signal handler:
Item {
id: testitem
onThesourceChanged: {
eval(thesource)
testfunction()
}
}
The testfunction() was defined in the text “thesource” and does indeed work in the signal handler above, printing out some test text; but it is not in the scope of the QML element and therefore cannot be called ever again, as far as I can see. I’ve tried various ideas like putting the eval() in a function, or inside Component.onCompleted, etc, but I can’t seem to figure out how to get anything created by the eval() to be recognized as part of the parent QML element.
For example I want to be able to call testitem.testfunction() — but it is not defined outside the scope of this handler.
You need to declare the variable in the scope where you want to access it later. Use eval() to initialize it in a child-scope:
import QtQuick 2.1
Rectangle {
width: 360
height: 360
MouseArea {
anchors.fill: parent
onClicked: {
testitem.thesource = "myfunction = function(text) { return text.toLowerCase() }"
}
}
Text {
id: testitem
property var myfunction: function(text) { return text.toUpperCase() }
property var thesource;
anchors.centerIn: parent
text: myfunction("Kullo – Secure Messaging")
onThesourceChanged: {
eval(thesource);
text = myfunction("Kullo – Secure Messaging")
}
}
}

Dynamically set Label's "id" property

I'm developing an application for SailfishOS using the QML language.
I want to dynamically set the id property of a Label by using an if condition.
This is my code:
Label {
id: {
if(myBool == false) {
thisText()
} else {
notThatText()
}
}
width: parent.width
horizontalAlignment: Text.AlignRight
text: ""
font.pixelSize: Theme.fontSizeLarge
}
This code is placed into my CoverPage.qml file, the one that display things on the application's cover while in background.
By doing this, the cover is simply black, nothing is displayed.
Is it possible in QML to do this?
Thanks in advance!
The Qt doc says this.
While it may look like an ordinary property, the id attribute is not an ordinary property attribute, and special semantics apply to it;
You cannot set the id of a QML component at runtime.(Correct me if I am wrong). You might find objectName property useful. But I don't understand why you are trying to assign dynamic id.
I have a use case where use a dynamic/specific id could be useful. The id could be view with Gammaray, it can help for debugging.
GridLayout {
id: layout
Repeater {
model: foo
Bar {
id: bar_X_Y // bar_{model.row}_{model.column}
}
}
}
But as far as I know, it's not possible.

Reading n random files from a folder in Qt Quick

I'm writing my first Qt Quick application and I'm having lots of trouble doing things that are simple in other environments. I want to display n random images from a folder. For this purpose I'm using FolderListModel, but the trouble is that I don't see any methods to access list of files directly. So here's my hackish approach:
Use FolderListModel to read files
Use Text component as delegate
onTextChanged of Text component gets the filename (this part works) and adds it to some ListModel
randomize the ListModel and then use it to display files
I have many problems and questions, but first of all, what is the sane way to do this (please make it so that the list part doesn't have to be written in c++)?
There are 2 problems for now - I can't figure out how to access the ListModel from Text component; and I can' t figure out how to make the ListModel public/accessible from another component that will display the images.
Below is the code:
import QtQuick 1.0
import Qt.labs.folderlistmodel 1.0
ListView {
width: 200; height: 300
FolderListModel {
folder: "file:///C:/somefolder"
id: folderModel
nameFilters: ["*.jpg"]
}
Component {
id: fileDelegate
Text { id: intext
text: fileName
//the next line fails, Can't find variable: anotherModel
onTextChanged: anotherModel.append([{name: intext.text}]
)
}
}
model: folderModel
delegate: fileDelegate
ListModel {
id: anotherModel
}
}
Your code almost works for me. I get the error "QML ListModel: append: value is not an object". This is because you append arrays.
If you remove the brackets in line
anotherModel.append([{name: intext.text}])
it works. To make anotherModel a public property write it as
property ListModel anotherModel: ListModel {}