Drag and drop

Video: Drag and Drop (5 MB)

Valuable drag and drop examples for beginner are not present in Qt distribution, so let's have a closer look at it.

Please read customing widgets and have a look at Qt documentation about drag&drop before reading this.

Adjust the form from previous tutorial, place QLineEdit, QTreeWidget and QListWidget on it.

Promote QLineEdit, QTreeWidget and QListWidget to promote widgets and create appropriate .h and .cpp files like we did in previous tutorial.

Make sure acceptDrops property is set to true on each of them.

Add drop capability to customized QLineEdit

We will implement drop capability to QLineEdit (now myLineEdit). When file is dropped (but not directory), path will appear in myLineEdit.

Define the following methods in mylineedit.h

void dragEnterEvent(QDragEnterEvent *event);
void dropEvent(QDropEvent *event);

and implement in mylineedit.cpp

void myLineEdit::dragEnterEvent(QDragEnterEvent *event)
{
    // accept just text/uri-list mime format
    if (event->mimeData()->hasFormat("text/uri-list")) 
    {     
        event->acceptProposedAction();
    }
}
 
 
void myLineEdit::dropEvent(QDropEvent *event)
{
    QList<QUrl> urlList;
    QString fName;
    QFileInfo info;
 
    if (event->mimeData()->hasUrls())
    {
        urlList = event->mimeData()->urls(); // returns list of QUrls
    
        // if just text was dropped, urlList is empty (size == 0)
        if ( urlList.size() > 0) // if at least one QUrl is present in list
        {
            fName = urlList[0].toLocalFile(); // convert first QUrl to local path
            info.setFile( fName ); // information about file
            if ( info.isFile() ) setText( fName ); // if is file, setText
        }
    }
    
    event->acceptProposedAction();
}

Recompile and try to drop file (from explorer) into customized QLineEdit.

Drop to customized QTreeWidget

You may think we will do the same as with QLineEdit, but setup is little different.

If you look at Protected Functions of QTreeWidget you'll see some virtual functions responsible for drag/drop.

To actually implement drop on QTreeWidget, define following methods in mytreewidget.h

virtual bool dropMimeData(QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action);    
QStringList mimeTypes() const;
Qt::DropActions supportedDropActions () const;

mytreewidget.cpp

bool myTreeWidget::dropMimeData(QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action)
{
    QList<QUrl> urlList;
    QTreeWidgetItem *item;
    
    urlList = data->urls(); // retrieve list of urls
 
    foreach(QUrl url, urlList) // iterate over list
    {
        // make new QTreeWidgetItem and set its text
        // if parent is null - add top level item (this parent)
        if (parent == NULL) item = new QTreeWidgetItem(this); 
        else 
        // else add QTreeWidgetItem with parent and expand parent
        {    
            item = new QTreeWidgetItem(parent);
            parent->setExpanded( true );
        }
 
        // set item text
        item->setText( 0, url.toLocalFile() );
    }
 
    return true;    
}
 
 
QStringList myTreeWidget::mimeTypes () const
{
    QStringList qstrList;
    // list of accepted mime types for drop
    qstrList.append("text/uri-list");
    return qstrList;
}
 
 
Qt::DropActions myTreeWidget::supportedDropActions () const
{
    // returns what actions are supported when dropping
    return Qt::CopyAction | Qt::MoveAction;
}

DragDropOverwriteMode property can be enabled to enhance visual effect when dropping.

Drop to customized QListWidget

This is pretty much same as with QTreeWidget. The only difference is different parameters list in function dropMimeData.

mylistwidget.h

virtual bool dropMimeData(int index, const QMimeData *data, Qt::DropAction action);    
QStringList mimeTypes() const;    
Qt::DropActions supportedDropActions () const;

mylistwidget.cpp

bool myListWidget::dropMimeData(int index, const QMimeData *data, Qt::DropAction action)
{
    QList <QUrl> urlList;
    QListWidgetItem *item;
    QFileInfo info;
    QString fName;
    
    urlList = data->urls(); // retrieve list of urls
 
    foreach(QUrl url, urlList) // iterate over list
    {
        fName = url.toLocalFile();
        info.setFile( fName );
 
        item = new QListWidgetItem(info.fileName());
        insertItem(index, item);
        ++index; // increment index to preserve drop order
    }
 
    return true;    
}
 
 
QStringList myListWidget::mimeTypes () const 
{
// same as with QTreeWidget
}
 
 
Qt::DropActions myListWidget::supportedDropActions () const
{
// same as with QTreeWidget
}

Drag from customized QListWidget

To implement drag operation, override mouseMoveEvent(QMouseEvent *event) method.

mylistwidget.h

void mouseMoveEvent(QMouseEvent *event);

mylistwidget.cpp

void myListWidget::mouseMoveEvent(QMouseEvent *event)
{
    // if not left button - return
    if (!(event->buttons() & Qt::LeftButton)) return;
 
    // if no item selected, return (else it would crash)
    if (currentItem() == NULL) return;
 
    QDrag *drag = new QDrag(this);
    QMimeData *mimeData = new QMimeData;
 
    // construct list of QUrls
    // other widgets accept this mime type, we can drop to them
    QList<QUrl> list;
    list.append(QUrl(currentItem()->text())); // only QUrl in list will be text of actual item
 
    // mime stuff
    mimeData->setUrls(list); 
    drag->setMimeData(mimeData);
 
    // start drag
    drag->start(Qt::CopyAction | Qt::MoveAction);
}

Project ready: drag_and_drop.zip