0

I have a ChartView in QML I'm trying to update from a data model in C++, however, I'm running into certain issues where I can't seem to figure out a way to update the LineSeries properly.

My Sample QML Code looks like:

ChartView {
    id: dataChartView
    animationOptions: ChartView.NoAnimation
    theme: ChartView.ChartThemeDark
    antialiasing: true
    Layout.fillHeight: true
    Layout.fillWidth: true

    ValueAxis {
        id: axisXdata
        min: dataManager.xMin
        max: dataManager.xMax
    }

    ValueAxis {
        id: axisYdata
        min: 0
        max: 1
    }

    LineSeries {
        id:  dataLineSeries
        name: "Angle"
        axisX: axisXdata
        axisY: axisYdata
    }

    VXYModelMapper {
        id:  dataModelMapper
        model: dataManager.dataModel[data]
        series: dataLineSeries
        firstRow: 1
        xColumn: 0
        yColumn: 1
    }
}

The underlying model is a QAbstractTableModel that looks like this:

class DataModel : public QAbstractTableModel
{
    Q_OBJECT
public:
    enum{
        NameRole,
        ValueRole
    };

    explicit DataModel(QObject *parent = nullptr);
    QHash<int, QByteArray> roleNames() const override;
    QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
    int rowCount(const QModelIndex& parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;
    void setMaxSize(int maxSize);
    int maxSize() const;
    Q_INVOKABLE void addData(const QPointF& point);

     public:
         void handleNewData(const QPointF& point);
    
    private:
        QVector<QPointF> m_data;
        int m_ModelMaxSize;
    
    signals:
         void newDataAdded(const QPointF& point);
    
    
    };

and the relevant addData() function simply just pushes the Point into the m_data vector

void MsclDataModel::addData(const QPointF &point)
{
    beginInsertRows(QModelIndex(), rowCount(), rowCount());
    m_data.push_back(point);
    endInsertRows();
}

I then have another class called DataManager which runs a function in another thread and just adds points to the data model

void DataManager::GeneratePoints()
{
    setXMin(QDateTime::currentDateTime().toMSecsSinceEpoch()); // for the min and max on the horizontal axis
    for(double t=0 ; ; t+=1)
       {
           double y = (1 + sin(t/10.0)) / 2.0;

        // many data models are required
           for(const auto& model : m_Models)
           {
               auto time = QDateTime::currentDateTime().toMSecsSinceEpoch();
               setXMax(qMax(m_xMax, time));
               model->handleNewData(QPointF(time, y));
           }
           std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}

My issue is with this method and with the number of models I have, the application slows to a crawl and crashes eventually after about a minute or so. I've tried to constrain the addData() function to only show the latest 500 points by using something like this:

void DataModel::addData(const QPointF &point)
{
    beginInsertRows(QModelIndex(), rowCount(), rowCount());
    if(m_data.size() == 500)
    {
        m_data.erase(m_data.begin());
    }
    m_data.push_back(point);
    endInsertRows();
}

However, my output looks something like: DataModel stops updating where the data model just stops updating after the 500th point.

I've also tried using a List to pop after max size of the list has been reached but the output is still the same. What am I doing wrong here?

3
  • Just as a side note, you can use pop_front() or removeFirst() to remove the first element in you QVector. Commented Jan 6, 2023 at 15:19
  • @iam_peter, i was initially using pop_front() Commented Jan 6, 2023 at 15:35
  • Did you try to set useOpenGL to true for performance improvement? Commented Jan 6, 2023 at 17:51

1 Answer 1

2

Instead of beginInsertRows() and endInsertRows() I've used beginResetModel() and endResetModel() which works. I assume the range for the beginInsertRows() is wrong and it doesn't send an update anymore, so it stops drawing. You would probably also need to call beginRemoveRows() I would stick with a full reset.

void DataModel::addData(const QPointF &point)
{
    beginResetModel();

    if (m_data.size() == 500) {
        m_data.pop_front();
        emit xMinChanged();
    }

    m_data.push_back(point);
    emit xMaxChanged();

    endResetModel();
}

enter image description here


Edit: This will also work.

void DataModel::addData(const QPointF &point)
{
    if (m_data.size() == 500) {
        beginRemoveRows(QModelIndex(), 0, 0);
        m_data.pop_front();
        endRemoveRows();
        emit xMinChanged();
    }

    beginInsertRows(QModelIndex(), rowCount(), rowCount());
    m_data.push_back(point);
    endInsertRows();
    emit xMaxChanged();
}
Sign up to request clarification or add additional context in comments.

4 Comments

Wouldn't a full reset mean a much bigger performance hit than doing a proper BeginRemoveRows({}, 0, 0)? If done before the BeginInsertRows, things should work nice
When popping front and pushing back the whole model would be dirty and trigger a full redraw?
@iam_peter. Thanks for your edit. That is getting me there but mine is still a bit janky, Are you able to post a reproducible example please? I'm emitting minChanged and valueAxis on the X axis is then m_data[0] while max is now m_data(currentPoint) but this doesn't look as smooth as yours and I just experience a jump

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.