Passing a variable into dynamic object creation in QML - qt

I'm trying to create multiple chartviews dynamically in QML. The chartviews are added to a gridview when created but I'm having an issue with the creation of the lineseries.
My code is
CustomButton
{
id:addSeriesChartButton
text: "Add Series"
onClicked: {
var name = chartsModel.get(chartSelector.currentIndex).name
var chart = mGridViewId.getTopDelegateInstance().children[0];
var index = chartSelector.currentIndex // Combobox of different charts
var axisX = Qt.createQmlObject('import QtCharts 2.15;
DateTimeAxis {
min: new Date(dataManager.dataModel[index].xMin)
max: new Date(dataManager.dataModel[index].xMax)
format: "hh:mm:ss:zzz"
}', chart, {index: index});
var axisY = Qt.createQmlObject('import QtCharts 2.15;
ValueAxis {
min: gLinkMin
max: gLinkMax }', chart);
var mapper = Qt.createQmlObject('import QtCharts 2.15;
VXYModelMapper {
firstRow: 1
xColumn : 0
yColumn: 1
model: dataManager.dataModel[index]}', chart, {index: index});
var line = chart.createSeries(ChartView.SeriesTypeLine, name, axisX, axisY);
line.useOpenGL = true
mapper.series = line;
}
}
My Issue is I can't seem to pass index into the creation of axisX and mapper. This returns an error "TypeError: Passing incompatible arguments to C++ functions from Javascript is not allowed". If I pass chartSelector.currentIndex, this works, however, when I change the index of the combobox, all existing chartviews update the lineseries with the new index. I don't want this to happen. I simply just want a chartview that exists as is at the time of creation. I can;t seem to find any reference on how to pass this value. Or even if this is the right approach. I'm assuming the index won't subsequently change. Any tips?

Since you're using dynamic object creation, consider using Loader, setSource, and data-uri. Here's a generic example, sorry, I haven't adapted it for QtCharts:
import QtQuick
import QtQuick.Controls
Page {
id: page
Repeater {
model: [
{ qml: "Rectangle { }",
obj: { width: 100,
height: 100,
color: "red" } },
{ qml: "Rectangle { radius: height * 0.5 }",
obj: { width: 50,
height: 50,
color: "orange" } },
{ qml: "Button { }",
obj: { text: "Click Me" } }
]
delegate: Loader {
x: Math.random() * page.width
y: Math.random() * page.height
Component.onCompleted: setSource(
`data:text/plain,import QtQuick
import QtQuick.Controls
` + modelData.qml,
modelData.obj
);
}
}
}
You can Try it Online!

Eventually ended up doing something like this:
CustomButton
{
id:addSeriesChartButton
text: "Add Series"
onClicked: {
var name = chartsModel.get(chartSelector.currentIndex).name
var chart = mGridViewId.getTopDelegateInstance().children[0];
var index = chartSelector.currentIndex // Combobox of different charts
var axisX = Qt.createQmlObject('import QtCharts 2.15;
DateTimeAxis {
min: new Date(dataManager.dataModel[' + index + '].xMin)
max: new Date(dataManager.dataModel[' + index + '].xMax)
format: "hh:mm:ss:zzz"
}', chart, {index: index});
var axisY = Qt.createQmlObject('import QtCharts 2.15;
ValueAxis {
min: gLinkMin
max: gLinkMax }', chart);
var mapper = Qt.createQmlObject('import QtCharts 2.15;
VXYModelMapper {
firstRow: 1
xColumn : 0
yColumn: 1
model: dataManager.dataModel[' + index + ']}', chart, {index: index});
var line = chart.createSeries(ChartView.SeriesTypeLine, name, axisX, axisY);
line.useOpenGL = true
mapper.series = line;
}
}
Seemed to fix it

Related

QML need to create component with bool property all list elements were within limits

I was thinking I need a component similar to ListModel, but I need to extend it to expose a readonly bool property such as "all list elements were within minimum and maximum limit" so I can do logic outside the component the determine certain things. How should I go about doing this extending a boolean property based on model's contents?
I guess naive way is to just add the qml property and do javascript loop on QML side to check all model contents but that might not be so good performance
Have you considered DelegateModel? It allows you to create "views" on your ListModel so you can control what you want to be displayed via the filterOnGroup property.
It is rather difficult to comprehend, but, in the following example, I have a ListModel containing 5 cities. When you start changing the RangeSlider the 5 cities will be filtered based on the minimum/maximum population selected. This works by updating the boolean function filter on the DelegateModel to reflect the cities that are now visible.
property var filter: model => model.pop >= rangeSlider.first.value
&& model.pop <= rangeSlider.second.value
Here's the full code snippet:
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQml.Models 2.15
Page {
anchors.fill: parent
ColumnLayout {
anchors.fill: parent
Label { text: qsTr("States") }
ListView {
Layout.fillWidth: true
Layout.fillHeight: true
model: DelegateModel {
id: filterDelegateModel
property int updateIndex: 0
property var filter: model => model.pop >= rangeSlider.first.value
&& model.pop <= rangeSlider.second.value
onFilterChanged: Qt.callLater(update)
model: us_states
groups: [
DelegateModelGroup {
id: allItems
name: "all"
includeByDefault: true
onCountChanged: {
if (filterDelegateModel.updateIndex > allItems.count) filterDelegateModel.updateIndex = allItems.count;
if (filterDelegateModel.updateIndex < allItems.count) Qt.callLater(update, filterDelegateModel.updateIndex);
}
},
DelegateModelGroup {
id: visibleItems
name: "visible"
}]
filterOnGroup: "visible"
delegate: Frame {
id: frame
width: ListView.view.width - 20
background: Rectangle {
color: (frame.DelegateModel.visibleIndex & 1) ? "#f0f0f0" : "#e0e0e0"
border.color: "#c0c0c0"
}
RowLayout {
width: parent.width
Text {
text: (frame.DelegateModel.visibleIndex + 1)
color: "#808080"
}
Text {
Layout.fillWidth: true
text: model.state
}
Text {
text: qsTr("pop: %1 M").arg((pop / 1000000).toFixed(2))
}
}
}
function update(startIndex) {
startIndex = startIndex ?? 0;
if (startIndex < 0) startIndex = 0;
if (startIndex >= allItems.count) {
updateIndex = allItems.count;
return;
}
updateIndex = startIndex;
if (updateIndex === 0) {
allItems.setGroups(0, allItems.count, ["all"]);
}
for (let ts = Date.now(); updateIndex < allItems.count && Date.now() < ts + 50; updateIndex++) {
let visible = !filter || filter(allItems.get(filterDelegateModel.updateIndex).model);
if (!visible) continue;
allItems.setGroups(updateIndex, 1, ["all", "visible"]);
}
if (updateIndex < allItems.count) Qt.callLater(update, updateIndex);
}
Component.onCompleted: Qt.callLater(update)
}
}
Label { text: "Population Range" }
RangeSlider {
id: rangeSlider
Layout.fillWidth: true
from: 0
to: 100000000
first.value: 1
first.onMoved: Qt.callLater(filterDelegateModel.update)
second.value: 100000000
second.onMoved: Qt.callLater(filterDelegateModel.update)
stepSize: 1000000
}
Label { text: qsTr("Minimum %1 M").arg((rangeSlider.first.value / 1000000).toFixed(2)) }
Label { text: qsTr("Maximum %1 M").arg((rangeSlider.second.value / 1000000).toFixed(2)) }
}
ListModel {
id: us_states
ListElement { state:"California"; pop: 39350000 }
ListElement { state:"Texas"; pop: 28640000 }
ListElement { state:"New York"; pop: 8380000 }
ListElement { state:"Nevada"; pop: 3030000 }
ListElement { state:"Las Vegas"; pop: 644000 }
}
}
You can Try it Online!
I have refactored the above into a FilterDelegateModel reusable component. Feel free to check it out:
https://github.com/stephenquan/qt5-qml-toolkit
https://github.com/stephenquan/qt5-qml-toolkit/wiki/FilterDelegateModel

QML Shape: How can I force data property to update in a clean way?

I am trying to make a window where I can draw triangles and delete any of them with a Shape{}. In my example below, I can draw 2 types of triangle:
Up triangle: green and filled
Down triangle: yellow and not filled
Basically, I choose the type of triangle (with a button on the right-bottom corner) then I click anywhere on the window to get a triangle.
Once I click a triangle is created dynamically and it is stored in the property triangleList. Then I call the function shape.update() to update data property of shape. This part works well.
Here the function update I use in the Shape (since data is a list, I have to reassign to a new list.):
function update()
{
data = [];
var d = [];
for (var i = 0; i < canvas.triangleList.length; i++)
{
d.push( canvas.triangleList[i] );
}
data = d;
}
My problem appears when I try to delete a triangle. In my example, I can delete the first, the last or all triangles. When I delete a triangle, first I delete the value in triangleList then I call again shape.update(). It works when I delete all triangles or the last one.
However, when I try to delete the first triangle, data doesn't update its objects even if I give it a new list. In fact, it always deletes the last triangle. Below an example:
data property understands there is one less triangle but it doesn't update the other triangles. The only solution I found is to change a property then come back to the original value. This way, it forces the data to update.
But I have to do that for every property that can be different (colors and positions). Hence, my update() function looks like that:
for (var i = 0; i < canvas.triangleList.length; i++)
{
d.push( canvas.triangleList[i] );
////// Change properties one by one to force the refresh
// Force path redraw. Otherwise only the last path can be deleted
d[i].startX++;d[i].startX--;
// Force line color update
d[i].strokeColor = "red"
d[i].strokeColor = d[i].isUp ? "green" : "yellow";
// Force fill color update
d[i].fillColor = "red";
d[i].fillColor = d[i].isUp ? "green" : "transparent";
data = d;
}
I invite you to comment in/out these lines to see the difference.
I could use this trick to force the update but my real code is really bigger than this example and I use bindings.
So my question is: Is there a way to force the update without having to change each property?
Here the full code if you want to test it:
import QtQuick 2.9;
import QtQuick.Controls 2.2;
import QtQuick.Shapes 1.0;
ApplicationWindow {
visible: true; width: 640; height: 480;
Rectangle {
id: canvas;
anchors.fill: parent;
color: "black";
property var triangleList: [];
property bool triangleUp: true;
MouseArea {
anchors.fill: parent;
onClicked: {
var triangle = componentTriangle.createObject(componentTriangle, {
"isUp" : canvas.triangleUp,
"startX" : mouse.x,
"startY" : mouse.y,
}, canvas);
canvas.triangleList.push(triangle);
shape.update();
}
} // MouseArea
Shape {
id: shape;
anchors.fill: parent;
function update()
{
data = [];
var d = [];
for (var i = 0; i < canvas.triangleList.length; i++)
{
d.push( canvas.triangleList[i] );
///////////// HOW TO AVOID THE PART BELOW? /////////////
////// Change properties one by one to force the refresh
// Force path redraw. Otherwise only the last path can be deleted
d[i].startX++;d[i].startX--;
// Force line color update
d[i].strokeColor = "red"
d[i].strokeColor = d[i].isUp ? "green" : "yellow";
// Force fill color update
d[i].fillColor = "red";
d[i].fillColor = d[i].isUp ? "green" : "transparent";
//////////////////////////////////////////////////////
}
data = d;
// I make sure data has at least one path to ensure the refresh
if (data.length == 0)
data.push(Qt.createQmlObject('import QtQuick 2.9; import QtQuick.Shapes 1.0; ShapePath {startX:0;startY:0;}', canvas,
"force_refresh"));
}
} // Shape
} // Rectangle
//////////// Buttons to handle the triangles
Column {
anchors.bottom: parent.bottom;
anchors.right: parent.right;
Button {
text: canvas.triangleUp? "Draw triangleUp" : "Draw triangleDown";
onClicked: { canvas.triangleUp = !canvas.triangleUp; }
} // Button
Button {
text: "Clear first";
onClicked: {
canvas.triangleList[0].destroy();
canvas.triangleList.splice(0,1);
shape.update();
}
} // Button
Button {
text: "Clear last";
onClicked: {
canvas.triangleList[canvas.triangleList.length -1].destroy();
canvas.triangleList.splice(canvas.triangleList.length -1,1);
shape.update();
}
} // Button
Button {
text: "Clear all";
onClicked: {
for (var i = 0; i < canvas.triangleList.length; i++)
canvas.triangleList[i].destroy();
canvas.triangleList = [];
shape.update();
}
} // Button
}
//////////// Component to draw the triangle
Component {
id: componentTriangle;
ShapePath {
property bool isUp;
property real offsetX: isUp? -20 : 20;
property real offsetY: isUp? -30 : 30;
strokeColor: isUp ? "green" : "yellow";
strokeWidth: 3;
fillColor: isUp ? "green" : "transparent";
PathLine { x: startX - offsetX; y: startY - offsetY }
PathLine { x: startX + offsetX; y: startY - offsetY }
PathLine { x: startX; y: startY }
} // ShapePath
}
}
Thank you very much for your help and feel free to ask me if I was not clear.
Have a nice day!
If you are going to handle many items (Shape) it is advisable to use a Repeater with a model. The repeater is responsible for displaying the items based on the information of the model, and to remove the items you just have to remove items from the model.
main.qml
import QtQuick 2.9;
import QtQuick.Controls 2.2;
import QtQuick.Shapes 1.0;
ApplicationWindow {
visible: true; width: 640; height: 480;
QtObject{
id: internals
property bool triangleUp: true;
}
ListModel{
id: datamodel
}
Rectangle {
id: canvas;
anchors.fill: parent;
color: "black";
Repeater{
model: datamodel
Triangle{
x: model.x
y: model.y
isUp: model.isUp
}
}
MouseArea{
anchors.fill: parent
onClicked: datamodel.append({"x": mouse.x, "y": mouse.y, "isUp": internals.triangleUp})
}
}
Column {
anchors.bottom: parent.bottom;
anchors.right: parent.right;
Button {
text: internals.triangleUp ? "Draw triangleUp" : "Draw triangleDown";
onClicked: internals.triangleUp = !internals.triangleUp;
} // Button
Button {
text: "Clear first";
onClicked: if(datamodel.count > 0) datamodel.remove(0)
} // Button
Button {
text: "Clear last";
onClicked: if(datamodel.count > 0) datamodel.remove(datamodel.count - 1)
} // Button
Button {
text: "Clear all";
onClicked: datamodel.clear()
} // Button
}
}
Triangle.qml
import QtQuick 2.9;
import QtQuick.Shapes 1.0
Shape {
id: shape
property bool isUp: false
QtObject{
id: internals
property real offsetX: isUp? -20 : 20;
property real offsetY: isUp? -30 : 30;
}
ShapePath {
strokeWidth: 3;
strokeColor: isUp ? "green" : "yellow";
fillColor: isUp ? "green" : "transparent";
PathLine { x: -internals.offsetX ; y: -internals.offsetY }
PathLine { x: internals.offsetX; y: -internals.offsetY }
PathLine { x: 0; y: 0 }
}
}

Qt.createComponent url of the library components

Below is a function from TimelinePresenter.qml which is a custom component I created.
function createMenu() {
var menuComp = Qt.createComponent("Menu.qml");
if( menuComp.status != Component.Ready )
{
if( menuComp.status == Component.Error )
console.debug("Error: " + menuComp.errorString());
return;
}
}
It gives the error:
Error: qrc:/qml/timeline/Menu.qml:-1 No such file or directory
TimelinePresenter.qml is a resource file specified in the .qrc file and its path is qml/timeline as shown in error message so qml engine is trying to find the Menu.qml there expectedly. How can I specify the path to create qt's Menu component?
Edit:
my resources.qrc file
<RCC>
<qresource prefix="/">
<file>qml/main_window.qml</file>
<file>qml/timeline/TimelineViewItem.qml</file>
<file>qml/timeline/HorizontalLine.qml</file>
<file>qml/timeline/TimelineView.qml</file>
<file>qml/timeline/VerticalLine.qml</file>
<file>qml/timeline/timeline-item/timeline_item.h</file>
<file>qml/timeline/TimelinePresenter.qml</file>
<file>qml/timeline/timeline-item/analog_timeline_item.h</file>
<file>qml/timeline/timeline-item/digital_timeline_item.h</file>
<file>qml/timeline/timeline_presenter_backend.h</file>
<file>qml/ControllableListPresenter.qml</file>
<file>qml/controllable_list_backend.h</file>
<file>qml/controllable-popup/AddControlUnitPopup.qml</file>
<file>qml/styled/CenteredPopup.qml</file>
<file>qml/styled/StyledTextField.qml</file>
</qresource>
</RCC>
You are confusing the creation of a component with the creation of an object that belongs to a component.
The Menu component already exists and is provided by Qt, what you must do is create the object using the Qt.createQmlObject() method.
Example:
var menuObj = Qt.createQmlObject('import QtQuick.Controls 2.0 ; Menu {
MenuItem { text: "Cut" }
MenuItem { text: "Copy" }
MenuItem { text: "Paste" } }', parentItem, "dynamicSnippet1");
Complete Example:
import QtQuick 2.7
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
id: parentItem
Component.onCompleted: {
var menu = Qt.createQmlObject('import QtQuick.Controls 2.0 ; Menu {
MenuItem { text: "Cut" }
MenuItem { text: "Copy" }
MenuItem { text: "Paste" }
}', parentItem,"dynamicSnippet1");
// test: open menu
menu.open()
}
}
In the case you have described in your comments, I would suggest to only create one Menu and only popup() it at the place where you have clicked, setting it in a specific context.
I prepared a small example to illustrate how the Menu could be used:
import QtQuick 2.7
import QtQuick.Window 2.0
import QtQuick.Controls 2.3 // Necessary for the "Action" I used. Create the Menu otherwise if you are bound to older versions.
import QtQml 2.0
ApplicationWindow {
id: window
visible: true
width: 600
height: 600
Repeater {
model: ListModel {
ListElement { color: 'black'; x: 400; y: 50 }
ListElement { color: 'black'; x: 100; y: 190 }
ListElement { color: 'black'; x: 70; y: 80 }
ListElement { color: 'black'; x: 30; y: 0 }
ListElement { color: 'black'; x: 340; y: 500 }
ListElement { color: 'black'; x: 210; y: 10 }
}
delegate: MouseArea {
x: model.x
y: model.y
width: 50
height: 50
property QtObject modelItem: model
onClicked: menu.openMenu(x + mouse.x, y + mouse.y, modelItem)
Rectangle {
color: model.color
anchors.fill: parent
}
}
}
Menu {
id: menu
Action { text: "green" ; onTriggered: { menu.currentContext.color = text } }
Action { text: "blue" ; onTriggered: { menu.currentContext.color = text } }
Action { text: "pink" ; onTriggered: { menu.currentContext.color = text } }
Action { text: "yellow" ; onTriggered: { menu.currentContext.color = text } }
Action { text: "orchid" ; onTriggered: { menu.currentContext.color = text } }
Action { text: "orange" ; onTriggered: { menu.currentContext.color = text } }
Action { text: "teal" ; onTriggered: { menu.currentContext.color = text } }
Action { text: "steelblue"; onTriggered: { menu.currentContext.color = text } }
property QtObject currentContext
function openMenu(x, y, context) {
currentContext = context
popup(x, y)
}
}
}
Though I think this answer might solve your problem, I know that it is not really the answer to the question you stated initially.
For the Component-part: I think you misunderstood what a Component is - it is not an Item. It is a prestage in the creation of QtObjects and more something like a prototype or configured factory.
So your function - if it would work - would end at the creation of a invisible thing, from which you could create objects, by calling createObject().
Creating Components is the right thing to do, if you want to create an object at a later time and you might want to create similar objects multiple times, either by JavaScript or by other QML-types that expect Components as some input (e.g. delegates).
To create Components you have multiple possibilities, e.g.:
Qt.createComponent(url)
Component { SomeItem {} }
The first expects you to know the url, which in your case, you do not. To circumvent that, the easiest solution is, to create a new File, like MyMenu.qml
that only contains the Menu {} - then you can create a Component from this.
The second does not expects you to know the url, but it is not dynamically created.
Component {
id: myCmp
Menu {
}
}
onSomeSignal: myCmp.createObject({ prop1: val1 }, this)
Here the Component is automatically created when the object in the file is instantiated. This makes that (one time) initially a bit slower, since more code has to be processed, but you don't have to do it later.
Creating objects like eyllanesc shows with Qt.createQmlObject("Write a new QML-File here") might be also used to create a Component if the top-level element is a Component. If you don't have a Component as top-level, it will also first create a component that is once used to create a QtObject and then is discarded. It is the slowest but most flexible way to dynamically create objects.

QML XmlListModel messes sorting when delegated

I'm trying to get a Canvas to draw lines in the same order as presented in this xml-file:
<root>
<doc><nopeus>80.0</nopeus><aika>40.0</aika></doc>
<doc><nopeus>110.0</nopeus><aika>80.0</aika></doc>
<doc><nopeus>120.0</nopeus><aika>120.0</aika></doc>
<doc><nopeus>190.0</nopeus><aika>160.0</aika></doc><doc><nopeus>243.0</nopeus><aika>200.0</aika></doc><doc><nopeus>260.0</nopeus><aika>240.0</aika></doc><doc><nopeus>300.0</nopeus><aika>280.0</aika></doc><doc><nopeus>350.0</nopeus><aika>320.0</aika></doc>
</root>
QML-file with XmlListModel:
import QtQuick 2.0
import Sailfish.Silica 1.0
import QtQuick.XmlListModel 2.0
Page {
id: page
property alias startx : coords.mX
property alias starty : coords.mY
Item {
id: coords
property int mX: 0
property int mY: 0
}
XmlListModel {
id: myxml
source: "/home/nemo/filename.xml"
query: "/root/doc"
XmlRole { name: "nopeus"; query: "nopeus/string()" }
XmlRole { name: "aika"; query: "aika/string()" }
}
ListView {
model: myxml
anchors.fill: page
delegate:
Item {
Chart {
xc: coords.mX;
yc: coords.mY;
xd: aika;
yd: nopeus;
}
}
}
}
Chart.qml:
import QtQuick 2.0
Rectangle {
id: myrect
width: 540
height: 960
color: "transparent"
property int xd: 0
property int yd: 0
property int xc: 0
property int yc: 0
Canvas {
id: mycanvas
width: myrect.width; height: myrect.height;
onPaint: {
var context = getContext('2d')
context.strokeStyle = "#FF0000"
context.lineWidth = 2
context.beginPath()
context.moveTo(xc,yc)
context.lineTo(xd,yd)
context.stroke()
startx = xd
starty = yd
}
}
}
The question is why is the resulting path messed up when inserted in the ListView via delegates? I have tried to sort the path items separately, via a function and another ListModel but the result is the same.
Here is a screenshot:
Delegates are created for each item in a model. Your model contains eight items (as of your input). Hence, you create eight Canvases (each one as a ListView item, i.e. at (theoretical) increasing y w.r.t. ListView origin coordinates).
Combile these problems with the (probably wrongly set) starting points...and you get a random mess! You can't see that, since the Canvases tend to overlap due to sizing/constraints set on the component.
In this case you just need one Canvas on which each myxml item is painted. Here is a (naive) adaptation of your code which correctly shows the path stored in the xml file:
// main.qml
import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.XmlListModel 2.0
Window {
visible: true
width: 600
height: 600
XmlListModel {
id: myxml
source: "qrc:/filename.xml" // added to the resources
query: "/root/doc"
XmlRole { name: "nopeus"; query: "nopeus/string()" }
XmlRole { name: "aika"; query: "aika/string()" }
onStatusChanged: {
if(status === XmlListModel.Ready)
comp.mod = myxml // set the model ASA it is ready to be used
}
}
Chart {
id: comp
anchors.fill: parent
mod: myxml
}
}
// Chart.qml
import QtQuick 2.4
import QtQuick.XmlListModel 2.0
Item {
property var mod: undefined
onModChanged: {
if(mod)
mycanvas.requestPaint() // repaint only when the model is available
}
Canvas {
id: mycanvas
width: parent.width; height: parent.height;
onPaint: {
var context = getContext('2d')
context.strokeStyle = "#FF0000"
context.lineWidth = 2
context.beginPath()
context.moveTo(0,0)
// iterate over all the point to print them
for(var i = 0; i < mod.count; i++)
{
var point = mod.get(i)
context.lineTo(point.aika, point.nopeus)
}
context.stroke()
}
}
}
The resulting path rendered:

Dynamically create QML ListElement and content

So I am trying to dynamically create ListElements in a ListModel. This works fine until I try writing some content in the ListElements to be loaded dynamically.
I tried making an own file with the ListElement within and the hour as a property, but the model then I got an error saying that ListElements can not be nested.
The error for running the code below is:
Cannot assign to non-existent property "hour"
How can I solve this?
Code:
import QtQuick 2.0
ListModel
{
id: listModel
Component.onCompleted:
{
for (var i = 0; i < 24; i++)
{
var object = createListElement(listModel)
}
}
function createListElement(parent)
{
var object = Qt.createQmlObject('import QtQuick 2.0; ListElement { hour: "01" }', parent);
return object;
}
}
EDIT:
Change the code line in the function to:
var object = Qt.createQmlObject('import QtQuick 2.0; ListElement { property string hour: "23" }', parent);
Now I get no errors, but the elements are still not showing in the list.
I'm not sure why that doesn't work, but using plain old JavaScript objects does the job:
import QtQuick 2.4
import QtQuick.Window 2.0
Window {
width: 400
height: 400
ListView {
id: listView
anchors.fill: parent
model: listModel
delegate: Rectangle {
width: listView.width
height: listView.height / 4
Text {
text: hour
anchors.centerIn: parent
}
}
}
ListModel {
id: listModel
Component.onCompleted: {
for (var i = 0; i < 24; i++) {
append(createListElement());
}
}
function createListElement() {
return {
hour: "01"
};
}
}
}

Resources