2016-04-08 190 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信號將由正確的線程發出。