Reordering TableView with drag and drop



  • Hi,

    I implemented my own QAbstractListModel and currently its displayed in a standard QTableview.
    I want to allow reordering the elements. Furthermore it must still be possible to drop something from outside the QTableview (no use of setDragDropMode(QAbstractItemView::InternalMove)).
    The model is working properly. But currently items are copyed instead of moved.
    So I need a way to force the QTableview just to create drags as MoveAction and not as CopyAction.

    Either I can do this with any features provieded by the QTableview or I must costumize the view.
    I already tried the second approach:

    void CPlaylistWidget::mouseMoveEvent(QMouseEvent *event)
    {
        if (!(event->buttons() & Qt::LeftButton))
            return;
        if ((event->pos() - dragStartPosition).manhattanLength()
             < QApplication::startDragDistance())
            return;
    
        QDrag *drag = new QDrag(this);
    
        drag->setMimeData(this->model()->mimeData(this->selectionModel()->selectedRows()));
    
        Qt::DropAction dropAction = drag->exec(Qt::MoveAction);
    ....
    }
    

    But now I can't see the moved row during the drag. I know there is the drag->setPixmap method.
    But I don't know how to assign a row of the tableview to that.

    Happy about any suggestions.



  • You can use QWidget::render() to create the pixmap that you can assign to your QDrag object. You can retrieve the rectangle of your item by using QAbstractItemView::visualRect().

    Something like this (untested):

    void CPlaylistWidget::mouseMoveEvent(QMouseEvent *event)
    {
    	...
    
    	// Retrieve the selected index
    	const QModelIndex& index = currentIndex();
    
    	// Render the pixmap
    	QPixmap pixmap;
    	render(&pixmap, visualRect(index));
    
    	// Create the drag
        QDrag* drag = new QDrag(this);
    	drag->setPixmap(pixmap);
    
    	// Perform the drag
        switch (drag->exec(Qt::MoveAction)) {
    
    	}
    
    	...
    }
    

    You should be able to easily adopt that to allow dragging multiple items at once. Obviously you would want to do some sanity checks along the way (eg. checking the rectangle returned by visualRect()).

    I hope that helps.



  • Sounds great, but I get some runtime error:

    QPainter::begin: Paint device returned engine == 0, type: 2
    QWidget::render: Cannot render with an inactive painter
    

    Edit:
    There is some modification on your suggested code neccessary:

    const QModelIndex& index = currentIndex();
    
    // Render the pixmap
    QPixmap pixmap(visualRect(index).size());
    //pixmap.
    render(&pixmap, QPoint(), visualRect(index));
    
    // Create the drag
    QDrag* drag = new QDrag(this);
    drag->setPixmap(pixmap);
    ...
    

    But anyhow it renders the cell of one index above the selected one.
    Maybe that has something to do with the offset parameter (second one in render).

    Edit2:
    The proper QRect can be calculated with the height of the horizontal header:

    render(&pixmap, QPoint(0,0), visualRect(index).translated(0, horizontalHeader()->height() ));
    


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