Spatz Spatz - 2 months ago 10
C++ Question

How to display values from lists in a UI QML (QT) inside a Repeater

I've been facing this issue for days without coming to a conclusion, I hope someone could give me some useful hints to solve it.

I'll try to simplify the issue with an example:


  • In my C++ code I defined the class MyObjectModel that will act
    later as a model in the Repeater block in my main ui.qml file.
    MyObjectModel is visible to the QQmlApplicationEngine.

  • MyObjectModel
    has 2 attributes (lists) : xCoordinatesList and yCoordinatesList.
    They represent the x and y pixel coordinates of a list of points.
    I.e. xCoordinatesList = [100, 200], yCoordinatesList = [10, 20] mean logically that I have 2 points with the following pixel coordinates that I want to display on the screen: (100,10), (10,20).

  • The xCoordinatesList and yCoordinatesList are Roles of my model
    for the QML engine. This means for instance that in a common
    .qml file i can clearly print the content of xCoordinatesList by
    typing:

    Component.onCompleted
    {
    console.log("x coordinates: ",xCoordinatesList);
    }



The question is: how can I display at the same time the list of points on the screen?

If I want to display only one dot (so one couple of coordinates) my code works. I really don't know how to extend it to make it print all of them.

In my MainForm.ui.qml I defined a Repeater inside a Rectangle :

Rectangle
{
....
Repeater
{
model: dotModel

delegate:
DotItem
{
id: dotItem;
objectName: "dotItem";
DotPositionOnMap
{
id: dotPositionId;
objectWidth: dotItem.width;
objectHeight: dotItem.height;
}
x: dotPositionId.xPositionOnMap;
y: dotPositionId.yPositionOnMap;
}
}
....
}


I need a Repeater because MyObjectModel which holds the two lists of x and y coordinates can dynamically change over time.
dotModel is just a fake model I use for an other purpose.
DotItem is my qml Item that identifies the red dot circle image I want to depict on the screen for each couple of elements in xCoordinatesList, yCoordinatesList.

DotItem.ui.qml

import QtQuick 2.4
import QtQuick.Layouts 1.1

Item
{
width: 10
height: 10
opacity: 1
Image
{
id: dotItemImage
anchors.fill: parent
source: "red_dot.png"
}
}


red_dot.png image should be displayed for each point depicted on the screen.

DotPositionOnMap.qml is responsible for computing the right x and y pixel position on the screen.

import QtQuick 2.5
import "calcCurrentPos_script.js" as CurrentPos

Item
{
// Values filled from MainForm.ui.qml
property int objectWidth
property int objectHeight

// Getting current coordinates
// Fetching element 0 from both lists
property real currentx: CurrentPos.getCurrentxPoint(0);
property real currenty: CurrentPos.getCurrentyPoint(0);

// Generating the x and y pixel position on map.
// Toy example
property int xPositionOnMap : currentx-(objectWidth/2);
property int yPositionOnMap : currenty-(objectHeight/2);
}


Where calcCurrentPos_script.js

function getCurrentxPoint(val)
{
return xCoordinatesList[val];
}

function getCurrentyPoint(val)
{
return yCoordinatesList[val];
}


In this way I can only display one dot on the screen since I specify in DotPositionOnMap.qml which point to fetch:

// Fetching element 0 in this case
property real currentx: CurrentPos.getCurrentxPoint(0);
property real currenty: CurrentPos.getCurrentyPoint(0);


I used javascript for this attempt because I thought I could use a for loop to scan all the elements to be displayed, but it didn't work.

Extract of my model

QVariant MyModelObject::data(const QModelIndex& index, int role) const
{
const MyModelObject& object = objects.values().value(index.row());
....
if(role == XRole)
{
QList<TrackPoint> tkrList = object.getList();
QList<QVariant> tkrVariantList;
for(auto track: trackpointList)
{
tkrVariantList.append(track.getPosition().getX());
}

return QVariant(tkrVariantList);
}
else if(role == YRole)
{
QList<TrackPoint> tkrList = object.getList();
QList<QVariant> tkrVariantList;
for(auto track: trackpointList)
{
tkrVariantList.append(track.getPosition().getY());
}
return QVariant(tkrVariantList);
}
}

....
....
QHash<int, QByteArray> MyModelObject::roleNames() const
{
QHash<int, QByteArray> roles;
roles[XRole] = "xCoordinatesList";
roles[YRole] = "yCoordinatesList";
return roles;
}


I truly appreciate any ideas concerning the generalisation of this implementation.

Thank you

Answer

* SOLVED *

In your main.qml you can have something like this, which handles the global drawing routine.

    Repeater
    {
        model: dotModel
        delegate:
        DotPositionOnMap{}
    }

DotPositionOnMap.qml will read the list of x and y coordinates and for each element of both will create a dot Item object which will be displayed on the screen.

import QtQuick 2.5

Item
{

id: dotPositionOnMap
objectName: "dotoPositionOnMap"

Component.onCompleted:
{
 // yCoordinatesList would have given the same result
 var dotListLength = xCoordinatesList.length;

// Dot properties can by handled dynamically
var dotWidth = 5;
var dotHeight = 5;
var dotRadius = 10;
var dotColor = "red"

// For each entry of xCoordinatesList and yCoordinatesList
// I generate a DotShape qml object that will display a dot on the screen
for(var i = 0; i < dotListLength; i++)
{

    var currentXValue = xCoordinatesList[i];
    var currentYValue = yCoordinatesList[i];
    // x and y pixrl values for a DotShape.qml instance
    var xPositionOnMap = currentXValue-(dotWidth/2);
    var yPositionOnMap = currentYValue-(dotHeight/2);

    // Creating DotShape.qml component
    var comp = Qt.createComponent("DotShape.qml");

    var dotComponent = comp.createObject(dotPositionOnMap,
                                           {
                                               "x": xPositionOnMap,
                                               "y": yPositionOnMap,
                                               "width": dotWidth,
                                               "height": dotHeight,
                                               "color": dotColor,
                                               "radius": dotRadius
                                           });
} // end for
} // end script
} // end Item

Finally DotShape.qml , which's just a small red dot plotted at x,y coordinate

import QtQuick 2.5

Rectangle
{ 
// Other properties are generated at runtime
id: dotShapeId;
objectName: "dotShapeId";
}
Comments