c++ qt qabstracttablemodel subclass chrashes when underlying qmap is updated

562 views Asked by At

i am having great trouble with the update of a custom qabstracttablemodel. i want to have a tableview that shows the prices of several stocks. i get the prices from a local server i control. this setup is for testing purposes. the pricing information is received in a worker thread.

i have subclassed the qabstracttablemodel in the following way:

PriceModel.h:

class PriceModel : public QAbstractTableModel {
    Q_OBJECT
public:
    PriceModel( QObject* parent = 0 );

    void setPriceMap( const QMap<QString, ITick*> &curTickMap );
    int rowCount( const QModelIndex &parent ) const;
    int columnCount( const QModelIndex &parent ) const;
    QVariant data( const QModelIndex &index, int role ) const;
    QVariant headerData( int section, Qt::Orientation orientation, int role ) const;
private:
    QMap<QString, ITick*> currentTicks;
    QString stockAt( int offset ) const;
};

PriceModel.cpp

#include "PriceModel.h"

PriceModel::PriceModel( QObject* parent ) : QAbstractTableModel( parent ) {
}

int PriceModel::rowCount( const QModelIndex& parent ) const {
    return this->currentTicks.count();
}

int PriceModel::columnCount( const QModelIndex& parent ) const {
    return 4;
}

QString PriceModel::stockAt( int offset ) const {
    return ( currentTicks.begin() + offset ).key();
}

QVariant PriceModel::data( const QModelIndex& index, int role ) const {
    if ( !index.isValid() ) {
        return QVariant();
    }
    if ( role == Qt::TextAlignmentRole ) {
        return int( Qt::AlignRight | Qt::AlignVCenter );
    } else if ( role == Qt::DisplayRole ) {
        QString stock = stockAt( index.row() );
        int i = index.column();
        switch ( i ) {
            case 0 : return currentTicks.value( instrument )->getTime().toString( "hh:mm:ss:zzz" );
            case 1 : return currentTicks.value( instrument )->getBid();
            case 2 : return currentTicks.value( instrument )->getAsk();
            case 3 : return currentTicks.value( instrument )->getBidVolume();
            case 4 : return currentTicks.value( instrument )->getAskVolume();
        }
    }
    return QVariant();
}

QVariant PriceModel::headerData( int section, Qt::Orientation orientation, int role )  const {
    if ( role != Qt::DisplayRole ) {
        return QVariant();
    }
    if ( orientation == Qt::Horizontal ) {
        switch ( section ) {
            case 0 : return QString( "Time" );
            case 1 : return QString( "Bid" );
            case 2 : return QString( "Ask" );
            case 3 : return QString( "Bid Volume" );
            case 4 : return QString( "Ask Volume" );
        }
    } else {
        return instrumentAt( section )->getCurrencyPairWithDelimiter();
    }
    return QVariant();
}

void PriceModel::setTickMap( const QMap<QString,ITick*>& curTickMap ) {
    beginResetModel();
    this->currentTicks = curTickMap;
    endResetModel();
}

the model populates the tableview when i call the setTickMap( qmap<...> ) method and all the different stocks are shown just as expected. ( initializing the data in my model is working fine )

the problem arises when i want to call the setTickMap( qmap<...> ) method again. the application crashes and i do not understand why nor do i get a significant error message - namely a segvault.

on a crash in debug configuration netbeans opens a tab named "Disassemlby" with following content:

QMetaObject::activate(QObject*, QMetaObject const*, int, void**)+50: add    %ebx,%r15d
QMetaObject::activate(QObject*, QMetaObject const*, int, void**)+85: testb $0x4,0x20(%rax)
QMetaObject::activate(QObject*, QMetaObject const*, int, void**)+89: jne 0x7ffff63f4df8 <QMetaObject::activate(QObject*, QMetaObject const*, int, void**)+536>
QMetaObject::activate(QObject*, QMetaObject const*, int, void**)+102: add    0x6c(%rsp),%ebx
QMetaObject::activate(QObject*, QMetaObject const*, int, void**)+118: mov    %ebx,0x2c(%rsp)
QMetaObject::activate(QObject*, QMetaObject const*, int, void**)+106: movq   $0x0,0x40(%rsp)
QMetaObject::activate(QObject*, QMetaObject const*, int, void**)+95: mov    0x31c19a(%rip),%rdx        # 0x7ffff6710de0
QMetaObject::activate(QObject*, QMetaObject const*, int, void**)+115: mov    (%rdx),%rax
QMetaObject::activate(QObject*, QMetaObject const*, int, void**)+122: test   %rax,%rax
QMetaObject::activate(QObject*, QMetaObject const*, int, void**)+125: je     0x7ffff63f4c72 <QMetaObject::activate(QObject*, QMetaObject const*, int, void**)+146>
QMetaObject::activate(QObject*, QMetaObject const*, int, void**)+127: lea    0x40(%rsp),%rdx
QMetaObject::activate(QObject*, QMetaObject const*, int, void**)+132: test   %r13,%r13
QMetaObject::activate(QObject*, QMetaObject const*, int, void**)+135: mov    %ebx,%esi
QMetaObject::activate(QObject*, QMetaObject const*, int, void**)+137: mov    %r12,%rdi
QMetaObject::activate(QObject*, QMetaObject const*, int, void**)+140: cmovne %r13,%rdx
QMetaObject::activate(QObject*, QMetaObject const*, int, void**)+144: callq  *%rax

i sandwiched the map assignment in the beginResetModel() methods but that does not seem to work as i just get a crashing application. odly enough this works at the first call of the setter method. this makes me assume that it is a quite stupid mistake. i think as the first call works fine we can rule out that it has something to do with inter-thread-communication problems.

this model is not supposed to be edited by the user through the GUI and i am aware that i could have used the tablewidget to just display the data but i am not sure if there is going to be another view that will share this models information. the map with the current prices is not going to be very big ( 25 items tops ) so i am not uncomfortable to reset the model on every new price coming in.

thanks in advance and i hope someone can help me with this micropor

2

There are 2 answers

0
MicroPor On BEST ANSWER

Wow my stupidity knows no bounds...

I fixed the problem and it had nothing to do with the tickmodel itself. The codesamples provided work just fine. I was apparently just too stupid to make sure the correct modelpointer is used.

This is sufficient:

void PriceModel::setTickMap( const QMap<QString,ITick*>& curTickMap ) {
    this->currentTicks = curTickMap;
    reset();
}
0
Karlson On

Considering that you are displaying real time tickdata I'd recommend approaching the data storage. Since you are only displaying 5 items from the tick you might want to consider something other then a QMap may be arrays since your universe might be fairly standardized during the trading day.

Also I would have to assume that the code is multi-threaded and the curTickMap may change during the setTickMap as it is likely that you are not doing the model reset on every tick.

And as webclectic pointed out the code you have posted is unlikely to compile.