PyQt4 - 拖放

Posted

技术标签:

【中文标题】PyQt4 - 拖放【英文标题】:PyQt4 - Drag and Drop 【发布时间】:2013-01-01 23:29:43 【问题描述】:

嘿,我一直在阅读 tutorial 以了解 PyQt4 中的拖放方法。但是我无法理解以下几点。如果 somepne 能让我更清楚,那就太好了。

 def mouseMoveEvent(self, e): //class Button


    mimeData = QtCore.QMimeData()

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

    dropAction = drag.start(QtCore.Qt.MoveAction)

def dropEvent(self, e): //class Example

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

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

为什么有一个单独的 self.button.move() 和 e.setDropAction() self.button.move() 实际上不移动按钮本身?有人可以解释一下 drag.setHotSpot 和 drag.start() 的作用吗?谢谢。

【问题讨论】:

【参考方案1】:

那个教程已经严重过时了。自 Qt 4.3 以来,QDrag.start 已过时。应该改用QDrag.exec_

exec 的文档中可以看出,它有一个返回值。 dropEvent 中的 setDropAction 确定此值。它不执行移动。这就是您需要self.button.move() 来进行实际移动的原因。那么,setDropAction 的意义何在?你可能需要知道你做了什么样的拖拽操作。想象一下,您正在两个列表小部件之间实现拖放。如果您执行了移动操作,这意味着您需要从源小部件中删除该项目并在目标中创建一个。如果是复制操作,可以保留原件,只在目标中创建一个副本。

setHotSpot/hotSpotQDragsetPixmap 相关。您可以在拖动项目时显示QPixmaphotSpot 确定像素图的定位。像素图的位置将使得光标位于相对于像素图左上角的hotSpot。因此,在该教程的情况下,它是毫无意义的,因为没有要显示的像素图。

这里是该教程的一些修改和更新版本。希望我已经包含了足够多的 cmets。您可以使用Right-Click移动或使用Shift + Right-Click复制

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
from PyQt4 import QtGui, QtCore


class Button(QtGui.QPushButton):
    def mouseMoveEvent(self, e):
        if e.buttons() != QtCore.Qt.RightButton:
            return

        # write the relative cursor position to mime data
        mimeData = QtCore.QMimeData()
        # simple string with 'x,y'
        mimeData.setText('%d,%d' % (e.x(), e.y()))

        # let's make it fancy. we'll show a "ghost" of the button as we drag
        # grab the button to a pixmap
        pixmap = QtGui.QPixmap.grabWidget(self)

        # below makes the pixmap half transparent
        painter = QtGui.QPainter(pixmap)
        painter.setCompositionMode(painter.CompositionMode_DestinationIn)
        painter.fillRect(pixmap.rect(), QtGui.QColor(0, 0, 0, 127))
        painter.end()

        # make a QDrag
        drag = QtGui.QDrag(self)
        # put our MimeData
        drag.setMimeData(mimeData)
        # set its Pixmap
        drag.setPixmap(pixmap)
        # shift the Pixmap so that it coincides with the cursor position
        drag.setHotSpot(e.pos())

        # start the drag operation
        # exec_ will return the accepted action from dropEvent
        if drag.exec_(QtCore.Qt.CopyAction | QtCore.Qt.MoveAction) == QtCore.Qt.MoveAction:
            print 'moved'
        else:
            print 'copied'


    def mousePressEvent(self, e):
        QtGui.QPushButton.mousePressEvent(self, e)
        if e.button() == QtCore.Qt.LeftButton:
            print 'press'



class Example(QtGui.QWidget):
    def __init__(self):
        super(Example, self).__init__()
        self.initUI()


    def initUI(self):
        self.setAcceptDrops(True)

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

        self.buttons = [button]

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


    def dragEnterEvent(self, e):
        e.accept()


    def dropEvent(self, e):
        # get the relative position from the mime data
        mime = e.mimeData().text()
        x, y = map(int, mime.split(','))

        if e.keyboardModifiers() & QtCore.Qt.ShiftModifier:
            # copy
            # so create a new button
            button = Button('Button', self)
            # move it to the position adjusted with the cursor position at drag
            button.move(e.pos()-QtCore.QPoint(x, y))
            # show it
            button.show()
            # store it
            self.buttons.append(button)
            # set the drop action as Copy
            e.setDropAction(QtCore.Qt.CopyAction)
        else:
            # move
            # so move the dragged button (i.e. event.source())
            e.source().move(e.pos()-QtCore.QPoint(x, y))
            # set the drop action as Move
            e.setDropAction(QtCore.Qt.MoveAction)
        # tell the QDrag we accepted it
        e.accept()



if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    ex = Example()
    ex.show()
    app.exec_()  

【讨论】:

感谢您的精彩回答。但是我仍然有疑问,为什么要将按钮移动到 e.pos() - QtCore.QPoint(x , y) 。 e.pos() 本身是否不将位置指定给应该丢弃的位置? .并且 mimeData 没有给出光标的位置,因为您已将其设置为 ex() 和 ey(),其中 e 是鼠标的位置,当我放下它时,不会同时显示 e.pos() 和 QtCore.QPoint (x , y) 。对不起我的菜鸟。 @Manoj: x 和来自mimey 是光标相对于按钮的本地位置e.pos() 将是光标在小部件上的实际位置 (Example)。如果您要将按钮移动到e.pos(),它会将按钮的左上角移动到该位置。例如,您选择了按钮中间的按钮。如果您只使用e.pos() 移动到新的光标位置,按钮将不会按照您拾取的方式放置,而是移动。 x,y 通过调整按钮的左上角来纠正这种偏移,使光标在放置后位于同一位置。 @Manoj:我猜你的困惑来自代码中的e.x()/e.y()e.pos()。我得到e.x()/e.y() 的部分是ButtonmouseMoveEvent。所以它们是相对于按钮的(0,0 是左上角)。其中e.pos()dropEventExample 中。它将给出的位置将是光标相对于Example 的相对位置。 是的,我现在明白了,感谢您的宝贵时间。 如果您也可以帮助我处理这段代码,那就太好了。 ***.com/questions/14606192/… 。很抱歉发送垃圾邮件。【参考方案2】:

Avaris 的答案适用于 PyQt5 和 Python 3。

#!/usr/bin/python
# -*- coding: utf-8 -*-

# Adapted for PyQt5 and Python 3 from Avaris' answer to
# https://***.com/questions/14395799/pyqt4-drag-and-drop

import sys
from PyQt5.QtWidgets import QPushButton, QWidget, QApplication
from PyQt5.QtCore import Qt, QMimeData, QPoint
from PyQt5.QtGui import QDrag, QPixmap, QPainter, QColor


class Button(QPushButton):
    def mouseMoveEvent(self, e):
        if e.buttons() != Qt.RightButton:
            return

        # write the relative cursor position to mime data
        mimeData = QMimeData()
        # simple string with 'x,y'
        mimeData.setText('%d,%d' % (e.x(), e.y()))

        # let's make it fancy. we'll show a "ghost" of the button as we drag
        # grab the button to a pixmap
        pixmap = QWidget.grab(self)

        # below makes the pixmap half transparent
        painter = QPainter(pixmap)
        painter.setCompositionMode(painter.CompositionMode_DestinationIn)
        painter.fillRect(pixmap.rect(), QColor(0, 0, 0, 127))
        painter.end()

        # make a QDrag
        drag = QDrag(self)
        # put our MimeData
        drag.setMimeData(mimeData)
        # set its Pixmap
        drag.setPixmap(pixmap)
        # shift the Pixmap so that it coincides with the cursor position
        drag.setHotSpot(e.pos())

        # start the drag operation
        # exec_ will return the accepted action from dropEvent
        if drag.exec_(Qt.CopyAction | Qt.MoveAction) == Qt.MoveAction:
            print('moved')
        else:
            print('copied')


    def mousePressEvent(self, e):
        QPushButton.mousePressEvent(self, e)
        if e.button() == Qt.LeftButton:
            print('press')



class Example(QWidget):
    def __init__(self):
        super(Example, self).__init__()
        self.initUI()


    def initUI(self):
        self.setAcceptDrops(True)

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

        self.buttons = [button]

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


    def dragEnterEvent(self, e):
        e.accept()


    def dropEvent(self, e):
        # get the relative position from the mime data
        mime = e.mimeData().text()
        x, y = map(int, mime.split(','))

        if e.keyboardModifiers() & Qt.ShiftModifier:
            # copy
            # so create a new button
            button = Button('Button', self)
            # move it to the position adjusted with the cursor position at drag
            button.move(e.pos()-QPoint(x, y))
            # show it
            button.show()
            # store it
            self.buttons.append(button)
            # set the drop action as Copy
            e.setDropAction(Qt.CopyAction)
        else:
            # move
            # so move the dragged button (i.e. event.source())
            e.source().move(e.pos()-QPoint(x, y))
            # set the drop action as Move
            e.setDropAction(Qt.MoveAction)
        # tell the QDrag we accepted it
        e.accept()



if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Example()
    ex.show()
    app.exec_()

【讨论】:

以上是关于PyQt4 - 拖放的主要内容,如果未能解决你的问题,请参考以下文章

chapter9 拖放

使用修饰键在 pyqt4 中拖放

HTML5之拖拽和拖放

#yyds干活盘点# 4.2 HTML5 拖放(Drag和Drop)

Html5 的拖拽功能

在 PyQt 中支持拖放的 QTreeView