2016-04-08 186 views
0

我的目标是实现一种模拟器,并进行高速数据更新。
该应用程序是由以下部分组成:
Qml TableView:滚动时崩溃

  • 数据模型:它存储数据,并且它被用来通过一个TableView中作为模型
  • 模拟器:用于更新整个数据模型中的螺纹每250毫秒
  • 的QML视图:包含一个TableView中尽快显示数据

我启动应用程序,我可以看到数据TableView中不断变化,但有一个奇怪的崩溃,如果我尝试滚动TableVi使用鼠标滚轮(保持滚动一段时间)。
中仅有的日志我得到的是以下内容:在的QList

ASSERT失败::在: “索引超出范围”,文件C:\工作\编译\ qt5_workdir \ W \ S \ qtbase \包括/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

我很高兴与大家分享的解决方案,以我自己的答案,希望它可以帮助别人谁是得到同样的错误。

问题的关键就在这里:

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线程的数据模型发出一个信号:这将陷入并发访问的问题,之间gui线程(正在访问数据以填充TableView)和更新程序线程(正在访问数据以更新值)。

解决方案是在gui线程上发出dataChanged信号,我们可以使用Qt Signal/Slot机制来实现。
因此:
datamodel.h

public slots: 
    void updateGui(int rowIndex); 

datamodel.cpp

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); 
    } 
} 

使用信号/槽的方法,我们相信,该请求将在接收机类由谁创建了类(GUI线程)的线程来处理,因此以下dataChanged信号将由正确的线程发出。