pyqt5自定义拖放功能的实现解说

样例仍然来自《Rapid GUI Programming with Python and Qt》一书。
中文版240页对拖放的支持有一段很重要的说明,但是有点语焉不详,所以结合代码部分重新给自己解释一下。
书中说:

setAcceptDrops()方法从QWidget继承而来,不过setDragEnabled()则不是,因此在默认情况下,只有部分窗口部件中它才是可用的。如果打算创建一个可以支持放下操作的自定义窗口部件,只需要简单调用setAcceptDrops(True)并重新实现dragEnterEvent()、dragMoveEvent、dropEvent()即可,正如在前面的例子所做的那样。如果也希望这个自定义窗口能够支持拖动,而该窗口是从QWidget继承而来,或者是从一些没有setDragEnabled()的QWidget子类继承而来,就必须做两件事来让该部件支持drag。一件事是提供一个startDrag()方法,以便可以创建一个QDrag对象,另一件事是确保在适当的时间能够调用startDrag()方法。要确保startDrag()的调用最简单的方法就是对mouseMoveEvent的重新实现……
回过头来重新看一遍代码,是怎样落实上面的说法的。

class DropLineEdit(QLineEdit):
    """QLineEdit继承自QWidget,要实现它需要四个step来支持'放下'"""

    def __init__(self, parent=None):
        super(DropLineEdit, self).__init__(parent)
        self.setAcceptDrops(True) #step1 True
        self.setToolTip("DropLineEdit")


    def dragEnterEvent(self, event): #step2 dragEnterEvent:accept
        if event.mimeData().hasFormat("application/x-icon-and-text"):
            event.accept()
        else:
            event.ignore()


    def dragMoveEvent(self, event): #step3 dragMoveEvent:Copy or Move
        if event.mimeData().hasFormat("application/x-icon-and-text"):
            event.setDropAction(Qt.CopyAction)
            event.accept()
        else:
            event.ignore()


    def dropEvent(self, event): #step4 dropEvent:get data
        if event.mimeData().hasFormat("application/x-icon-and-text"):
            data = event.mimeData().data("application/x-icon-and-text")
            stream = QDataStream(data, QIODevice.ReadOnly)
            text = stream.readQString()
            self.setText(text)
            event.setDropAction(Qt.CopyAction)
            event.accept()
        else:
            event.ignore()

第二个例子,祖上有德,支持drag的

class DnDListWidget(QListWidget):
    """QListWidget多重继承,其中一个父类是QAbstractItemView,支持setDragEnabled(),
    所以可以直接设置为True,但是还要要有startDrag()方法以创建Drag对象"""
    def __init__(self, parent=None):
        super(DnDListWidget, self).__init__(parent)
        self.setAcceptDrops(True)
        self.setDragEnabled(True)
        self.setToolTip("DnDListWidget")


    def dragEnterEvent(self, event):
        if event.mimeData().hasFormat("application/x-icon-and-text"):
            event.accept()
        else:
            event.ignore()


    def dragMoveEvent(self, event):
        if event.mimeData().hasFormat("application/x-icon-and-text"):
            event.setDropAction(Qt.MoveAction)
            event.accept()
        else:
            event.ignore()


    def dropEvent(self, event):
        if event.mimeData().hasFormat("application/x-icon-and-text"):
            data = event.mimeData().data("application/x-icon-and-text")
            stream = QDataStream(data, QIODevice.ReadOnly)
            text = stream.readQString()
            # text=""
            # stream>>text
            icon = QIcon()
            stream >> icon
            item = QListWidgetItem(text, self)
            item.setIcon(icon)
            event.setDropAction(Qt.MoveAction)
            event.accept()
        else:
            event.ignore()



    def startDrag(self, dropActions):
        item = self.currentItem()
        icon = item.icon()
        data = QByteArray()
        stream = QDataStream(data, QIODevice.WriteOnly)
        stream.writeQString(item.text())
        # stream<<item.text()
        stream << icon
        mimeData = QMimeData()
        mimeData.setData("application/x-icon-and-text", data)
        drag = QDrag(self)
        drag.setMimeData(mimeData)
        pixmap = icon.pixmap(24, 24)
        drag.setHotSpot(QPoint(12, 12))
        drag.setPixmap(pixmap)
        if drag.exec(Qt.MoveAction) == Qt.MoveAction:
            self.takeItem(self.row(item))

第三个例子就是自我奋斗型的凤凰男

class DnDWidget(QWidget):
    """本类直接继承自Qwidget,所以天生不支持Drag,必须自行实现startDrag()和和适当时机的调用,本例是在mouseMoveEvent"""
    def __init__(self, text, icon=QIcon(), parent=None):
        super(DnDWidget, self).__init__(parent)
        self.setAcceptDrops(True) #drop step1
        #self.setDragEnabled(True),如果硬加上,也出错'DnDWidget' object has no attribute 'setDragEnabled'
        self.text = text
        self.icon = icon
        self.setToolTip("DnDWidget")


    def minimumSizeHint(self):
        fm = QFontMetricsF(self.font())
        if self.icon.isNull():
            return QSize(fm.width(self.text), fm.height() * 1.5)
        return QSize(34 + fm.width(self.text), max(34, fm.height() * 1.5))


    def paintEvent(self, event):
        height = QFontMetricsF(self.font()).height()
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)
        painter.setRenderHint(QPainter.TextAntialiasing)
        painter.fillRect(self.rect(), QColor(Qt.yellow).lighter())
        if self.icon.isNull():
            painter.drawText(10, height, self.text)
        else:
            pixmap = self.icon.pixmap(24, 24)
            painter.drawPixmap(0, 5, pixmap)
            painter.drawText(34, height, self.text + " (Drag to or from me!)")


    def dragEnterEvent(self, event): #drops step2
        if event.mimeData().hasFormat("application/x-icon-and-text"):
            event.accept()
        else:
            event.ignore()


    def dragMoveEvent(self, event):#drop step3
        if event.mimeData().hasFormat("application/x-icon-and-text"):
            event.setDropAction(Qt.CopyAction)
            event.accept()
        else:
            event.ignore()


    def dropEvent(self, event): #drop step4 over
        if event.mimeData().hasFormat("application/x-icon-and-text"):
            data = event.mimeData().data("application/x-icon-and-text")
            stream = QDataStream(data, QIODevice.ReadOnly)
            self.text=""
            self.text = stream.readQString()
            self.icon = QIcon()
            stream >> self.icon
            event.setDropAction(Qt.CopyAction)
            event.accept()
            self.updateGeometry()
            self.update()
        else:
            event.ignore()


    def mouseMoveEvent(self, event): 
        self.startDrag() #drag step 1
        QWidget.mouseMoveEvent(self, event)


    def startDrag(self): #drag step 2
        icon = self.icon
        if icon.isNull():
            return
        data = QByteArray()
        stream = QDataStream(data, QIODevice.WriteOnly)
        stream.writeQString(self.text)
        # stream<<self.text
        stream << icon
        mimeData = QMimeData()
        mimeData.setData("application/x-icon-and-text", data)
        drag = QDrag(self)
        drag.setMimeData(mimeData)
        pixmap = icon.pixmap(24, 24)
        drag.setHotSpot(QPoint(12, 12))
        drag.setPixmap(pixmap)
        drag.exec(Qt.CopyAction)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值