[solved] Drag and drop a selection of cells in QTableWidget



  • Hi,

    I have encountered a problem while trying to implement my own drag and drop in a QTableWidget:
    When the selection is overlapping with the dropping area, the items in the overlap are not moved... The other case works just fine (when my selection is not overlapping with the dropping area). I am testing on only 1 row now.

    I tried looking in the documentation and FAQ but it wasn't helpful.

    I reimplemented dragEnterEvent, dragMoveEvent and dropEvent like the following:

    @
    void dragEnterEvent(QDragEnterEvent * evt)
    {
    QByteArray oldbyteData = evt->encodedData( "application/x-qabstractitemmodeldatalist" );

    QDataStream stream( &oldbyteData, QIODevice::ReadOnly );

    kfSelection.clear();

    while( !stream.atEnd() )
    {
    IndexKf kf;
    QMap<int, QVariant> v;
    stream >> kf.layer >> kf.frame >> v;

    QTableWidgetItem* keyframe = item( kf.layer, kf.frame );
    if( keyframe == NULL )
    continue;
    kfSelection.push_back( kf );
    }

    if( kfSelection.size() == 0 )
    return;

    evt->acceptProposedAction();
    }

    void dragMoveEvent(QDragMoveEvent * evt)
    {
    evt->accept();
    }

    void dropEvent(QDropEvent *evt)
    {
    QByteArray oldbyteData = evt->encodedData( "application/x-qabstractitemmodeldatalist" );

    QDataStream stream( &oldbyteData, QIODevice::ReadOnly );

    for( std::vector<IndexKf>::iterator it = kfSelection.begin(); it != kfSelection.end(); ++it )
    {
    IndexKf kf = *it;

    QModelIndex newIdx = indexAt( evt->pos() );
    QTableWidgetItem* keyframe = takeItem( kf.layer, kf.frame );

    int newlayer = newIdx.row();
    int newframe = newIdx.column() - dragStartPosition.column() + kf.frame;

    setItem( newlayer, newframe, keyframe );
    }
    evt->acceptProposedAction();
    }
    @

    IndexKf is just a structure to store the row and column of the cells in my selection.

    I would appreciate any help and advices :)

    Thank you



  • Hi,

    you overwrite the items from 0 to end (where 0 is start of drop area).
    As you call setItem, you replace the old items.
    Might that be your problem?



  • Hi,

    Thanks for the help, I modified my dropEvent function to this:

    @
    void dropEvent(QDropEvent *evt)
    {
    QByteArray oldbyteData = evt->encodedData( "application/x-qabstractitemmodeldatalist" );

    QDataStream stream( &oldbyteData, QIODevice::ReadOnly );

    QModelIndex newIdx = indexAt( evt->pos() );

    std::sort( kfSelection.begin(), kfSelection.end(), &IndexKf::sortIndexKf );

    int newframe = -1, newlayer = -1;
    QTableWidgetItem* tmp = NULL;
    for( std::vector<IndexKf>::iterator it = kfSelection.begin(); it != kfSelection.end(); ++it )
    {
    IndexKf kf = *it;

    newlayer = newIdx.row();
    newframe = newIdx.column() + (kf.frame - dragStartPosition.column());

    QTableWidgetItem* keyframe = (newframe==kf.frame)?tmp:takeItem( kf.layer, kf.frame ); // set to the deleted item if necessary
    tmp = takeItem( newlayer, newframe ); // take the item that is going to be deleted

    setItem( newlayer, newframe, keyframe );
    }
    evt->acceptProposedAction();
    }
    @

    Somehow it is still not working... I went through the debugger and the behaviour seemed correct. The item supposed to be dropped in the overlapping area is still not showing up after the drop.

    Edit: I think it is also interesting to note that dropping to an area overlapping with the original selection is not permitted in the original behaviour of QTableWidget. I can't figure out why though...



  • After taking a long break, I went back to check this code and finally found a solution.

    @
    void dragEnterEvent(QDragEnterEvent * evt)
    {
    QByteArray oldbyteData = evt->encodedData( "application/x-qabstractitemmodeldatalist" );

    QDataStream stream( &oldbyteData, QIODevice::ReadOnly );

    kfSelection.clear();

    while( !stream.atEnd() )
    {
    IndexKf kf;
    QMap<int, QVariant> v;
    stream >> kf.layer >> kf.frame >> v;

    kfSelection.push_back( kf );
    }

    if( kfSelection.size() == 0 )
    return;

    dragStartPosition = indexAt( evt->pos() );

    QTableWidget::dragEnterEvent( evt );
    }

    void dragMoveEvent(QDragMoveEvent * evt)
    {
    QTableWidget::dragMoveEvent( evt );
    }

    void dropEvent(QDropEvent *evt)
    {
    QModelIndex newIdx = indexAt( evt->pos() );

    int newframe, oldframe, layer;

    for( std::vector<IndexKf>::iterator it = kfSelection.begin(); it != kfSelection.end(); ++it )
    {
    (*it).item = takeItem( (*it).layer, (*it).frame );
    }

    for( std::vector<IndexKf>::iterator it = kfSelection.begin(); it != kfSelection.end(); ++it )
    {
    IndexKf kf = *it;
    layer = kf.layer;
    oldframe = kf.frame;
    newframe = newIdx.column() - dragStartPosition.column() + oldframe;

    setItem( layer, newframe, kf.item );
    }
    evt->setDropAction( Qt::IgnoreAction );
    }
    @


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.