dataChange signal ignored by GridView



  • Hello,

    I am trying to update the content of a GridView with the dataChange signal. I studied the problem discussed here:
    https://forum.qt.io/topic/78406/how-to-update-qabstractitemmodel-view-when-a-data-is-updated
    After a day trying to bring my implementation closer to the example above and vice versa I like to ask for some help.

    The little game I am writing occurs on a Battlefield.qml:

    GridView {
                 id: grid;
                 anchors.fill: parent
                 cellWidth:  elementWidth
                 cellHeight: elementHeight
                 interactive: false
                 model: BattlefieldModel {
                     id: bfModel
                     columns: base.columns; rows: base.rows
                     wallImage:  "/images/wall.png"
                     stoneImage: "/images/stone.png"
                     bombImage:  "/images/bomb.png"
                     explosionImage: "/images/explosion.png"
                     rangeExtension: "/images/rangeExtension.png"
                     pickupBomb: "/images/pickupBomb.gif"
                 }
                 delegate:
                     Rectangle {
                     width: grid.cellWidth ; height: grid.cellHeight ;
                     color: "transparent";
    
    AnimatedImage {
                         id: image
                         anchors.fill: parent
                     }
                     Component.onCompleted: {
                         if( content )
                             image.source = content
                     }}}
    

    The BF is fed by a BattlefieldModel.cpp:

        class BattlefieldModel : public QAbstractListModel
        {
        Q_OBJECT
        Q_PROPERTY( int columns READ columns WRITE setColumns )
        Q_PROPERTY( int rows    READ rows    WRITE setRows    )
    
        Q_PROPERTY( QString wallImage       READ wallImage       WRITE setWallImage  )
        .. more properties the same type
    
        public:
            enum Roles {        content = Qt::UserRole,    };
    
        // Define the game raster NOTHING TO DO WITH Qt-MODEL
        int  columns();
        void setColumns( int columns );
        int  rows();
        void setRows( int rows );
    
         <<setters getters removed>>
    
        // QAbstractItemModel interface
        int rowCount(const QModelIndex &parent) const;
        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole ) const;
        QHash<int, QByteArray> roleNames() const;
    
    public slots:
        void refresh( const QSet< LinearCoordinate > & indices );
    
    private:
        class BattlefieldModelPrivate;
        BattlefieldModelPrivate * d;
    };
    

    The implementation:

        class BattlefieldModel::BattlefieldModelPrivate
        {
        public:
        BattlefieldModelPrivate( BattlefieldModel * q ) :
            q(q) {}
    
        void initialize() {
            int rasterSize = rows * columns;
            if( rasterSize == 0 )
                return;
    
            q->beginInsertRows( QModelIndex(), 0, rasterSize );
            BF_instance.setColumns( columns );
            BF_instance.setRows( rows );
            q->endInsertRows();
        }
    
        QString wallImage, stoneImage, bombImage, explosionImage, rangeExtension;
        QString pickupBomb;
        BattlefieldModel * q;
        uint columns = 0, rows = 0;
        };
    
        BattlefieldModel::BattlefieldModel() :
            d( new BattlefieldModelPrivate( this ) )
        {
        connect( &BF_instance, SIGNAL(tilesChanged(QSet<LinearCoordinate>)),
                 this, SLOT(refresh(QSet<LinearCoordinate>)));
        }
    
        BattlefieldModel::~BattlefieldModel() { delete d; }
    
        int BattlefieldModel::columns() { return BF_instance.columns(); }
    
        void BattlefieldModel::setColumns(int columns) {
        d->columns = columns;
        d->initialize();
        }
    
        int BattlefieldModel::rows() { return BF_instance.rows(); }
    
        void BattlefieldModel::setRows(int rows) {
        d->rows = rows;
        d->initialize();
        }
    
        void BattlefieldModel::refreshTile(LinearCoordinate lc)
        {
        BF_instance.getTile( 0 )->setType( Tile::bomb );
        BF_instance.getTile( 3 )->setType( Tile::bomb );
        QModelIndex a = index( 3 );
        emit dataChanged( a, a );
        }
    
        int BattlefieldModel::rowCount(const QModelIndex &parent) const
        {
        if(parent.isValid()) return 0;
        return BF_instance.rasterSize();
        }
    
    QVariant BattlefieldModel::data(const QModelIndex &index, int ) const
    {
        qDebug() << "refreshing? " << index;
        Tile * tile = BF_instance.getTile( index.row() );
        switch( tile->type() ) {
        case Tile::wall:
            return QVariant(d->wallImage);
        case Tile::stone:
            return QVariant(d->stoneImage);
        case Tile::bomb:
            return QVariant(d->bombImage);
        case Tile::explosion:
            return QVariant(d->explosionImage);
        case Tile::pickupRangeExtension:
            return QVariant(d->rangeExtension);
        case Tile::pickupBomb:
            return QVariant(d->pickupBomb);
        default:
            return QVariant();
        }
    }
    
    
    QHash<int, QByteArray> BattlefieldModel::roleNames() const
    {
        QHash<int, QByteArray> roles;
        roles[ Roles::content ] = "content";
        return roles;
    }
    
    void BattlefieldModel::refresh( const QSet< LinearCoordinate > & indices ) {
        BF_instance.getTile( 0 )->setType( Tile::bomb );
        BF_instance.getTile( 3 )->setType( Tile::bomb );
        QModelIndex a = index( 3 );
        emit dataChanged( a, a );
    }
    

    BF_instance is singleton holding all data for the BF and used throughout the game.
    If I use:
    beginResetModel();
    endResetModel();

    The BF gets updated, but that is to slow. Bf is registered, but I tried also setting it as context, didn't work. On the other hand, registering the Animal from the above link works flawless. Also switching from GridView to ListView did not work for my game, but did work in the link.

    Help is much appreciated.



  • You need to create a binding to content instead of assigning the image source once on component completion.

    For example:

    source: content
    

    Or if the check is necessary (not sure why):

    source: content ? content : ""
    // or alternatively
    // source: content || ""


  • While your answer is correct and solves the problem, a different one appears. The image, a gif, is not animated any more.
    Animation worked before when updating was done by reseting the whole view.

    Any further ideas?

    Thank you.


Log in to reply