我的目标是实现一种具有高速率数据更新的模拟器
该申请由以下部分组成:
一旦我启动应用程序,我就可以看到TableView中的数据发生了变化,但是如果我尝试使用鼠标滚轮滚动TableView(继续滚动一段时间),则会出现严重的崩溃。
我得到的唯一日志如下:
在QList :: at中的ASSERT失败:"索引超出范围",文件C:\ work \ build \ qt5_workdir \ w \ s \ qtbase \ include / QtCore /../../ src /corelib/tools/qlist.h,第510行
更有趣的是,我只在Windows环境中遇到此崩溃,而在CentOs机器中我没有收到任何错误。
我在这里尝试提取项目的主要部分并尽可能多地概括它。
查找下面的代码,或者如果您愿意,可以从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
}
}
}
}
答案 0 :(得分:0)
我很高兴与您分享我自己答案的解决方案,希望它可以帮助那些犯同样错误的人。
问题的关键在于:
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); //ERROR!
}
}
实际上我在一个不是Gui线程的线程中的DataModel上发出一个信号:这将陷入gui线程(访问数据以填充TableView)和更新程序之间的并发访问问题线程(访问数据以更新值)。
解决方案是在gui线程上发出dataChanged信号,我们可以通过使用Qt信号/插槽机制来实现。
因此:
在 datamodel.h :
public slots:
void updateGui(int rowIndex);
在 datamodel.cpp :
void DataModel::updateGui(int rowIndex)
{
QModelIndex qIndex=index(rowIndex,0, QModelIndex());
dataChanged(qIndex,qIndex);
}
在 simulator.h :
signals:
void dataUpdated(int row);
在 simulator.cpp :
Simulator::Simulator(QObject* parent) : QThread(parent)
{
createNewData();
connect(this,SIGNAL(dataUpdated(int)), &DataModel::getInstance(), SLOT(updateGui(int)));
}
...
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 dataUpdated(i);
}
}
使用Signal / Slot方法,我们确信请求将由创建该类的线程(Gui线程)在接收器类中处理,因此正确的线程将发出以下dataChanged信号。