0
votes

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

2

2 Answers

0
votes

The Qt documentation is very clear. The first you have to read is that article. I think that in your case the simpliest way is list-based model. Or, of cource, you can subclass QAbstractListModel.

Your question is not so clear and you didn't provide the code of your model but maybe this small example will help you:

Declaration

    class MyModel : public QAbstractListModel
    {
        Q_OBJECT
    public:
        enum PointRoles {
                  XRole = Qt::UserRole + 1,
                  YRole
              };
        MyModel(QObject *parent = Q_NULLPTR);
        int rowCount(const QModelIndex &parent = QModelIndex()) const;
        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
        QHash<int, QByteArray> roleNames() const;

    private:
        QList<QPoint> m_list;
    };

Implementation

MyModel::MyModel(QObject *parent) :
    QAbstractListModel(parent)
{
}

QHash<int, QByteArray> MyModel::roleNames() const {
     QHash<int, QByteArray> roles;
     roles[XRole] = "xcoord";
     roles[YRole] = "ycoord";
     return roles;
 }

int MyModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return m_list.count();
}

QVariant MyModel::data(const QModelIndex &index, int role) const
{
    if(role == XRole)
        return m_list[index.row()].x();
    else if(role == YRole)
        return m_list[index.row()].y();

    return QVariant();
}

Registation

qmlRegisterType<MyModel>("qt.test", 1, 0, "PointModel");

Usage

Window {
    visible: true
    width: 800
    height: 800
    PointModel {id: mymodel}
    Repeater {
        model: mymodel
        delegate: Rectangle {
            x: 100 + xcoord
            y: 100 + ycoord
            width: 20
            height: 20
            color: "red"
            radius: 10
        }
    }
}
0
votes

* 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";
}