My goal is to implement a sort of simulator, with high rate data updates.
The application is composed from the following parts:
- A data model: it stores the data and it is used by a TableView as a model
- A simulator: a thread which updates the whole data model every 250ms
- A Qml view: contains a TableView to show the data
As soon as I start the application, I can see the data changing in the TableView, but there is a wierd crash if I try to scroll the TableView using the mouse wheel (keep scrolling for a while).
The only log I get is the following:
ASSERT failure in QList::at: "index out of range", file C:\work\build\qt5_workdir\w\s\qtbase\include/QtCore/../../src/corelib/tools/qlist.h, line 510
The more interesting thing is that I get this crash only in a Windows environment, while in a CentOs machine I do not get any error.
I tried here to extract the main part of my project and to generalize it as much as possibile.
Find below the code, or if you prefer you can download the full project from this link
mydata.h
#ifndef MYDATA_H
#define MYDATA_H
#include <QObject>
class MyData : public QObject
{
Q_OBJECT
Q_PROPERTY(int first READ first WRITE setFirst NOTIFY firstChanged)
Q_PROPERTY(int second READ second WRITE setSecond NOTIFY secondChanged)
Q_PROPERTY(int third READ third WRITE setThird NOTIFY thirdChanged)
public:
explicit MyData(int first, int second, int third, QObject* parent=0);
int first()const {return m_first;}
int second()const {return m_second;}
int third()const {return m_third;}
void setFirst(int v){m_first=v;}
void setSecond(int v){m_second=v;}
void setThird(int v){m_third=v;}
signals:
void firstChanged();
void secondChanged();
void thirdChanged();
private:
int m_first;
int m_second;
int m_third;
};
#endif // MYDATA_H
mydata.cpp
#include "mydata.h"
MyData::MyData(int first, int second, int third, QObject* parent) : QObject(parent)
{
m_first=first;
m_second=second;
m_third=third;
}
datamodel.h
#ifndef DATAMODEL_H
#define DATAMODEL_H
#include <QAbstractListModel>
#include <QMutex>
#include "mydata.h"
class DataModel: public QAbstractListModel
{
Q_OBJECT
public:
enum DataModelRoles {
FirstRole = Qt::UserRole + 1,
SecondRole,
ThirdRole
};
//*****************************************/
//Singleton implementation:
static DataModel& getInstance()
{
static DataModel instance; // Guaranteed to be destroyed.
// Instantiated on first use.
return instance;
}
DataModel(DataModel const&) = delete;
void operator=(DataModel const&) = delete;
//*****************************************/
QList<MyData*>& getData(){return m_data;}
void addData(MyData* track);
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = FirstRole) const;
protected:
QHash<int, QByteArray> roleNames() const;
private:
QMutex m_mutex;
QList<MyData*> m_data;
DataModel(QObject* parent=0);
};
#endif // DATAMODEL_H
datamodel.cpp
#include "DataModel.h"
#include "QDebug"
DataModel::DataModel(QObject* parent): QAbstractListModel(parent)
{
}
void DataModel::addData(MyData *track)
{
beginInsertRows(QModelIndex(),rowCount(),rowCount());
m_data<<track;
endInsertRows();
}
int DataModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_data.size();
}
QVariant DataModel::data(const QModelIndex &index, int role) const
{
MyData* data=m_data[index.row()];
switch (role) {
case FirstRole:
return data->first();
case SecondRole:
return data->second();
case ThirdRole:
return data->third();
default:
return QVariant();
}
}
QHash<int, QByteArray> DataModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[FirstRole] = "First";
roles[SecondRole] = "Second";
roles[ThirdRole] = "Third";
return roles;
}
simulator.h
#ifndef SIMULATOR_H
#define SIMULATOR_H
#include <QThread>
class Simulator: public QThread
{
Q_OBJECT
public:
Simulator(QObject* parent=0);
void run() Q_DECL_OVERRIDE;
private:
void createNewData();
void updateExistingData();
int randInt(int from, int to);
};
#endif // SIMULATOR_H
simulator.cpp
#include "simulator.h"
#include <math.h>
#include <mydata.h>
#include <datamodel.h>
Simulator::Simulator(QObject* parent) : QThread(parent)
{
createNewData();
}
void Simulator::run()
{
long updateRate=250;
while(true)
{
updateExistingData();
msleep(updateRate);
}
}
void Simulator::createNewData()
{
int numOfData=10000;
for(int i=0;i<numOfData;i++)
{
int first=i;
int second=randInt(0,1000);
int third=randInt(0,1000);
MyData* data=new MyData(first,second,third);
DataModel::getInstance().addData(data);
}
}
void Simulator::updateExistingData()
{
QList<MyData*> list=DataModel::getInstance().getData();
for(int i=0;i<list.size();i++)
{
MyData* curr=list.at(i);
curr->setSecond(curr->second()+1);
curr->setThird(curr->third()+2);
QModelIndex index=DataModel::getInstance().index(i,0, QModelIndex());
emit DataModel::getInstance().dataChanged(index,index);
}
}
int Simulator::randInt(int from, int to)
{
// Random number between from and to
return qrand() % ((to + 1) - from) + from;
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "simulator.h"
#include "datamodel.h"
#include <QQmlContext>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
DataModel& model=DataModel::getInstance();
Simulator* s=new Simulator();
s->start();
QQmlApplicationEngine engine;
QQmlContext *ctxt = engine.rootContext();
ctxt->setContextProperty("myModel", &model);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
main.qml
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.4
Window {
visible: true
width: 800
height: 600
TableView {
id: tableView
width: parent.width
height: parent.height
frameVisible: true
model: myModel
sortIndicatorVisible: true
property string fontName: "Arial"
TableViewColumn {
id: firstColumn
title: "First"
role: "First"
movable: false
resizable: false
width: tableView.viewport.width/3
delegate: Text{
font.family: tableView.fontName
text: styleData.value
horizontalAlignment: TextInput.AlignHCenter
verticalAlignment: TextInput.AlignVCenter
}
}
TableViewColumn {
id: secondColumn
title: "Second"
role: "Second"
movable: false
resizable: false
width: tableView.viewport.width/3
delegate: Text{
font.family: tableView.fontName
text: styleData.value
horizontalAlignment: TextInput.AlignHCenter
verticalAlignment: TextInput.AlignVCenter
}
}
TableViewColumn {
id: thirdColumn
title: "Third"
role: "Third"
movable: false
resizable: false
width: tableView.viewport.width/3
delegate: Text{
font.family: tableView.fontName
text: styleData.value
horizontalAlignment: TextInput.AlignHCenter
verticalAlignment: TextInput.AlignVCenter
}
}
}
}