# PyQt5 拖放

> 原文： [http://zetcode.com/gui/pyqt5/dragdrop/](http://zetcode.com/gui/pyqt5/dragdrop/)

在 PyQt5 教程的这一部分中，我们将讨论拖放操作。

在计算机图形用户界面中，拖放是单击虚拟对象并将其拖动到其他位置或另一个虚拟对象上的动作（或支持以下动作）。 通常，它可用于调用多种动作，或在两个抽象对象之间创建各种类型的关联。

拖放是图形用户界面的一部分。 拖放操作使用户可以直观地执行复杂的操作。

通常，我们可以拖放两件事：数据或某些图形对象。 如果将图像从一个应用拖到另一个应用，则会拖放二进制数据。 如果我们在 Firefox 中拖动选项卡并将其移动到另一个位置，则将拖放图形组件。

## `QDrag`

`QDrag`支持基于 MIME 的拖放数据传输。 它处理拖放操作的大多数细节。 传输的数据包含在`QMimeData`对象中。

## 简单的拖放

在第一个示例中，我们有一个`QLineEdit`和一个`QPushButton`。 我们将纯文本从行编辑小部件中拖放到按钮小部件上。 按钮的标签将更改。

`simpledragdrop.py`

```py
#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial

This is a simple drag and
drop example. 

Author: Jan Bodnar
Website: zetcode.com
Last edited: August 2017
"""

from PyQt5.QtWidgets import (QPushButton, QWidget, 
    QLineEdit, QApplication)
import sys

class Button(QPushButton):

    def __init__(self, title, parent):
        super().__init__(title, parent)

        self.setAcceptDrops(True)

    def dragEnterEvent(self, e):

        if e.mimeData().hasFormat('text/plain'):
            e.accept()
        else:
            e.ignore() 

    def dropEvent(self, e):

        self.setText(e.mimeData().text()) 

class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):

        edit = QLineEdit('', self)
        edit.setDragEnabled(True)
        edit.move(30, 65)

        button = Button("Button", self)
        button.move(190, 65)

        self.setWindowTitle('Simple drag and drop')
        self.setGeometry(300, 300, 300, 150)

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    ex.show()
    app.exec_()  

```

该示例展示了一个简单的拖动&放下操作。

```py
class Button(QPushButton):

    def __init__(self, title, parent):
        super().__init__(title, parent)

        ...

```

为了在`QPushButton`小部件上放置文本，我们必须重新实现一些方法。 因此，我们创建了自己的`Button`类，该类将从`QPushButton`类继承。

```py
self.setAcceptDrops(True)

```

我们使用`setAcceptDrops()`为小部件启用放置事件。

```py
def dragEnterEvent(self, e):

    if e.mimeData().hasFormat('text/plain'):
        e.accept()
    else:
        e.ignore() 

```

首先，我们重新实现`dragEnterEvent()`方法。 我们告知我们接受的数据类型。 在我们的情况下，它是纯文本。

```py
def dropEvent(self, e):

    self.setText(e.mimeData().text()) 

```

通过重新实现`dropEvent()`方法，我们定义了放置事件发生了什么。 在这里，我们更改按钮小部件的文本。

```py
edit = QLineEdit('', self)
edit.setDragEnabled(True)

```

`QLineEdit`小部件具有对拖动操作的内置支持。 我们需要做的就是调用`setDragEnabled()`方法来激活它。

![Simple drag and drop](img/8254ee0f6e682ddef758e5cb105cb1de.jpg)

图：简单 drag and drop

## 拖放按钮小部件

在下面的示例中，我们将演示如何拖放按钮小部件。

`dragbutton.py`

```py
#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial

In this program, we can press on a button with a left mouse
click or drag and drop the button with  the right mouse click. 

Author: Jan Bodnar
Website: zetcode.com
Last edited: August 2017
"""

from PyQt5.QtWidgets import QPushButton, QWidget, QApplication
from PyQt5.QtCore import Qt, QMimeData
from PyQt5.QtGui import QDrag
import sys

class Button(QPushButton):

    def __init__(self, title, parent):
        super().__init__(title, parent)

    def mouseMoveEvent(self, e):

        if e.buttons() != Qt.RightButton:
            return

        mimeData = QMimeData()

        drag = QDrag(self)
        drag.setMimeData(mimeData)
        drag.setHotSpot(e.pos() - self.rect().topLeft())

        dropAction = drag.exec_(Qt.MoveAction)

    def mousePressEvent(self, e):

        super().mousePressEvent(e)

        if e.button() == Qt.LeftButton:
            print('press')

class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):

        self.setAcceptDrops(True)

        self.button = Button('Button', self)
        self.button.move(100, 65)

        self.setWindowTitle('Click or Move')
        self.setGeometry(300, 300, 280, 150)

    def dragEnterEvent(self, e):

        e.accept()

    def dropEvent(self, e):

        position = e.pos()
        self.button.move(position)

        e.setDropAction(Qt.MoveAction)
        e.accept()

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    ex.show()
    app.exec_() 

```

在我们的代码示例中，窗口上有一个`QPushButton`。 如果我们用鼠标左键单击该按钮，则会在控制台上显示`'press'`消息。 通过右键单击并移动按钮，我们可以对按钮小部件执行拖放操作。

```py
class Button(QPushButton):

    def __init__(self, title, parent):
        super().__init__(title, parent)

```

我们创建一个从`QPushButton`派生的`Button`类。 我们还重新实现了`QPushButton`的两种方法：`mouseMoveEvent()`和`mousePressEvent()`。 `mouseMoveEvent()`方法是拖放操作开始的地方。

```py
if e.buttons() != Qt.RightButton:
    return

```

在这里，我们决定只能用鼠标右键执行拖放操作。 鼠标左键保留用于单击该按钮。

```py
mimeData = QMimeData()

drag = QDrag(self)
drag.setMimeData(mimeData)
drag.setHotSpot(e.pos() - self.rect().topLeft())

```

`QDrag`对象已创建。 该类提供对基于 MIME 的拖放数据传输的支持。

```py
dropAction = drag.exec_(Qt.MoveAction)

```

拖动对象的`exec_()`方法开始拖放操作。

```py
def mousePressEvent(self, e):

    super().mousePressEvent(e)

    if e.button() == Qt.LeftButton:
        print('press')

```

如果我们用鼠标左键单击按钮，我们将在控制台上打印`'press'`。 注意，我们也在父对象上调用了`mousePressEvent()`方法。 否则，我们将看不到按钮被按下。

```py
position = e.pos()
self.button.move(position)

```

在`dropEvent()`方法中，我们指定释放鼠标按钮并完成放置操作后发生的情况。 在本例中，我们找出当前鼠标指针的位置并相应地移动按钮。

```py
e.setDropAction(Qt.MoveAction)
e.accept()

```

我们用`setDropAction()`指定放置动作的类型。 在我们的情况下，这是一个动作。

PyQt5 教程的这一部分专门用于拖放操作。