Qt implements drag and drop function (supports drag and drop files, drag and drop operation)


drag and drop

Drag and drop is an intuitive, modern way of transferring information within an application or across multiple applications. In addition to providing support for the clipboard, it usually provides data movement and copying functions.

Drag and drop involves two distinct actions: dragging and dropping. A Qt widget can act as a drag site, a drop site, or both.

Qt program accepts drag and drop from other programs

We often push and drop text files into text editor software such as notepate++. So how to make the Qt program also support this kind of operation?

We need to reimplement dragEnterEvent()the sum dropEvent()function from the parent class in the main window.

protected:
    virtual void dragEnterEvent(QDragEnterEvent* event) override;
    virtual void dropEvent(QDropEvent* event) override;

In the constructor, one is created QTextEditand set as the central widget. By default, QTextEditit accepts dragging of text from other applications, and if the user drops a file on it, it will fill the QTextEdit widget with the contents of the file.

Since drag and drop events are passed from child widgets to parent widgets, by disabling drop operations on QTextEdit and enabling drop operations on the main window, you can get drop events on the entire MainWindow window.

#include <QMimeData>
#include <QTextStream>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    
    
    ui->setupUi(this);
    
 
    textEdit = new QTextEdit(this);
    setCentralWidget(textEdit);

    textEdit->setAcceptDrops(false);
    setAcceptDrops(true);

    setWindowTitle("Text Editor");
}

Called when the user drags an object onto this widget dragEnterEvent(). If called on this event acceptProposedAction(), it indicates that the user can drag and drop objects on this widget. By default, widgets do not accept dragging. Qt will automatically change the cursor to indicate to the user whether the widget is a valid drop point .

Here, we hope that the user can only drag files, not other types of things. To achieve this, we can check MIMEthe type of the drag.

MIMEThe type is text/uri-listused to store a series of Uniform Resource Identifiers (Universal Re-source Identifier, URI), which can be a file name, Uniform Resource Locator (Uniform Resource Locator, URL, such as HTTP or FTP path), or other Global resource identifier. Standard MIME types are defined by the Internet Assigned Numbers Authority (IANA), and they consist of type, subtype information, and a slash separating the two. MME classes are commonly used by clipboard and drag-and-drop systems to identify different types of data. An official list of MIME types can be obtained from: http://www.iana.org/assignments/media-types/

void MainWindow::dragEnterEvent(QDragEnterEvent *event)
{
    
    
    if(event->mimeData()->hasFormat("text/uri-list"))
    {
    
    
        event->acceptProposedAction();
    }
}

Called when the user drops an object on the widget dropEvent(). .We call the function QMimeData::urls()to get QUrlthe list. Usually, the user only drags one file at a time, but it is also possible to drag multiple files at the same time by dragging a selection area. If there is more than one URL to be dragged and dropped, or the URL to be dragged and dropped is not a local file name, it will immediately return to the original calling place.
QWidget also provides dragMoveEvent() and dragLeaveEvent() functions, but in most applications there is no need to reimplement them.

void MainWindow::dropEvent(QDropEvent *event)
{
    
    
    QList<QUrl> urls = event->mimeData()->urls();
    if(urls.empty())
        return;

    QString fileName = urls.first().toLocalFile();
    if(fileName.isEmpty())
        return;

    if(ReadFile(fileName))
    {
    
    
        setWindowTitle(QString("%1-%2").arg(fileName).arg("Drag File"));
    }
}
bool MainWindow::ReadFile(const QString &filename)
{
    
    
    QFile file(filename);
    file.open(QIODevice::ReadOnly | QIODevice::Text | QIODevice::Truncate);
    if(false == file.isOpen())
    {
    
    
        return false;
    }
    QTextStream stream(&file);
    textEdit->insertPlainText(stream.readAll());
    file.flush();
    file.close();
    return true;
}

Let's see the effect
insert image description here

Drag and drop between widgets/controls

We will achieve an effect similar to the one below, but without the left and right buttons, and move by dragging the target.
insert image description here

IdeaQListWidget : Create a subclass that supports drag and drop ProjectListWidget, and use it as a component of the interface.

In the ProjectListWidget class, five events of the parent class need to be rewritten, and a private method and a coordinate record

protected:
    virtual void mousePressEvent(QMouseEvent* event) override;
    virtual void mouseMoveEvent(QMouseEvent* event) override;
    virtual void dragEnterEvent(QDragEnterEvent* event) override;
    virtual void dragMoveEvent(QDragMoveEvent* event) override;
    virtual void dropEvent(QDropEvent* event) override;
private:
    void performDrag();

private:
    QPoint startPos;

In the constructor, we enable the drop on the listbox.

#include <QApplication>
#include <QDrag>
#include <QMimeData>
ProjectListWidget::ProjectListWidget(QWidget* parent)
    :QListWidget{
    
    parent}
{
    
    
    setAcceptDrops(true);
}

When the user presses the left mouse button, save the mouse position to statPosa private variable. Then we call QListWidget normally mousePressEvent().

void ProjectListWidget::mousePressEvent(QMouseEvent *event)
{
    
    
    if(event->button() == Qt::LeftButton)
    {
    
    
        startPos = event->pos();
    }
    QListWidget::mousePressEvent(event);
}

When the user holds down the left mouse button and moves the mouse cursor, it is considered to be the start of a drag. We calculate the distance between the current mouse position and the point where the left mouse button was originally pressed - this " Manhattan Length" is actually a quick approximation from the origin of the coordinates to the length of this vector. If this distance is greater than or equal to QApplication's recommended drag start distance value (usually 4 pixels), then call the private function performDrag()to start the drag operation. This prevents the user from dragging due to jittery hands holding the mouse.

void ProjectListWidget::mouseMoveEvent(QMouseEvent *event)
{
    
    
    if(event->buttons() & Qt::LeftButton)
    {
    
    
        if(int distance = (event->pos() - startPos).manhattanLength();
            distance >= QApplication::startDragDistance())
        {
    
    
            performDrag();
        }
    }
    QListWidget::mouseMoveEvent(event);
}

In perfomDrag(), an object of type QDrag is created, and this is its parent object. This QDrag object stores data in QMimeDatathe object. In this instance, we take advantage of the data QMineData::setText()provided as text/plainstrings. QMimeData provides some functions that can be used to handle the most common drag and drop types (such as images, URLs, colors, etc.), and can also handle arbitrary MiME types represented by QByteArrays. QDrag::setPiximap()Call to make the icon move with the cursor when drag and drop occurs.
QDrag::exec()Called to initiate and perform a drag operation; the drag operation does not stop until the user drops or cancels the drag operation. It takes a combination of all supported "drag and drop actions" (such as Qi::CopyAction, Qt::MoveAction and Qt::LinkAction) as its parameters, and returns the drag and drop action that was performed (or Qt: IgnoreAction). As for which action is performed, it depends on whether the source widget allows it, whether the target supports it, and which key combinations are pressed when the drop occurs. After the exec() call, Qt takes ownership of the dragged object and can delete it when it is no longer needed.

void ProjectListWidget::performDrag()
{
    
    
    if(QListWidgetItem* item = currentItem();
        nullptr != item)
    {
    
    
        QMimeData* mineData = new QMimeData();
        mineData->setText(item->text());

        QDrag* drag = new QDrag(this);
        drag->setMimeData(mineData);
        drag->setPixmap(QPixmap(":/icon.jpg"));

        if(drag->exec(Qt::MoveAction) == Qt::MoveAction)
        {
    
    
            delete item;
            item = nullptr;
        }
    }
}	

The ProjectListWidget widget can not only initiate a drag, but also receive a drag from another ProjectListWidget widget in the same application . If the widget is part of the same application, QDragEnterEvent::source()returns a pointer to the widget that initiated the drag; otherwise, returns a null pointer value.

void ProjectListWidget::dragEnterEvent(QDragEnterEvent *event)
{
    
    
    ProjectListWidget* source = reinterpret_cast<ProjectListWidget*>(event->source());
    if(nullptr != source && source != this)
    {
    
    
        event->setDropAction(Qt::MoveAction);
        event->accept();
    }
}

The code in dragMoveEvent() is basically the same as the code written in dragEnterEvent(). Because it is necessary to rewrite the function implementation of QListWidget (actually the function implementation of QAbstractItemView).

void ProjectListWidget::dragMoveEvent(QDragMoveEvent *event)
{
    
    
    ProjectListWidget* source = reinterpret_cast<ProjectListWidget*>(event->source());
    if(nullptr != source && source != this)
    {
    
    
        event->setDropAction(Qt::MoveAction);
        event->accept();
    }
}

In dropEvent(), we QMimeData::text()retrieve the dragged text using Retrieve and create a dragged item along with the text. You also need to accept the event as a "move action", which tells the source widget that the original dragged item can now be removed.

void ProjectListWidget::dropEvent(QDropEvent *event)
{
    
    
    ProjectListWidget* source = reinterpret_cast<ProjectListWidget*>(event->source());
    if(nullptr != source && source != this)
    {
    
    
        addItem(event->mimeData()->text());
        event->setDropAction(Qt::MoveAction);
        event->accept();
    }
}

The effect is as follows
insert image description here

Summarize

Drag and drop is a powerful mechanism for passing data between applications. But in some cases; it is possible to perform drag and drop without using Qt's drag and drop tools. If you just want to move data between widgets in an application, you usually just need to reimplement the mousePressEvent() and mouseReleaseEvent() functions.

Guess you like

Origin blog.csdn.net/qq_45254369/article/details/131436838