如何在 PyQt 中从 QListWidget 拖放到 QLabel

How to make drag and drop from QListWidget to QLabel in PyQt

在我使用 PyQt 编写的应用程序中,我想通过将项目从 QListWidget 拖到 QLabel 来实现拖放功能(根据 QListWidget 项目的文本在 QLabel 上设置文本)。为此,我创建了新的 class 继承窗体 QLabel 以添加 dragEnterEvent 和 dropEvent。我的标签代码:

class Label(QLabel):
  def __init__(self, parent):
    super(Label, self).__init__(parent)
    self.setAcceptDrops(True)

  def dragEnterEvent(self, event):
    print('drag')
    print(event.mimeData().text())
    if event.mimeData().hasText():
        event.accept()
    else:
        event.ignore()

  def dropEvent(self, event):
    print('drop')
    self.setText(event.mimeData().text())

问题是,当我从 QListWidget 中拖动项目时,它没有文本,所以我的代码无法正常工作。据我了解,这是因为当我从 QListWidget 中拖动项目时,我不是在拖动文本而是整个小部件(因为据我了解,列表中的项目不是字符串而是 QListWidgetItem)。根据我在这里发现的不同问题,我想我也应该为拖动的项目制作一个新的 class,但此时我不知道我是否应该为 QListWidget 或 QListWidgetItem 制作一个新的 class。或者也许我应该以完全不同的方式来做到这一点?

从 Qt 项目视图中拖动时,您不是在“拖动小部件”,而是选择的序列化表示。

该表示以 application/x-qabstractitemmodeldatalist mime 格式存储,为了读取其内容,您必须使用 QDataStream。

每个项目的存储数据是:

  • 项目行
  • 项目列
  • 对于数据大小范围内的每个条目(多少 role/value 对):
    • 角色
    • 价值

此数据必须始终以上述结构读取,不能“跳过”任何部分,否则数据将无法正确读取,因为它存储在“序列化”流。

因此,您可以通过以下方式实现:

class Label(QLabel):
    def __init__(self, parent):
        super(Label, self).__init__(parent)
        self.setAcceptDrops(True)

    def dragEnterEvent(self, event):
        mime = event.mimeData()
        if (mime.hasText() or 
            mime.hasFormat('application/x-qabstractitemmodeldatalist')):
                event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        mime = event.mimeData()
        if mime.hasText():
            self.setText(mime.text())
        elif mime.hasFormat('application/x-qabstractitemmodeldatalist'):
            textList = []
            stream = QDataStream(mime.data('application/x-qabstractitemmodeldatalist'))
            while not stream.atEnd():
                # we're not using row and columns, but we *must* read them
                row = stream.readInt()
                col = stream.readInt()
                for dataSize in range(stream.readInt()):
                    role, value = stream.readInt(), stream.readQVariant()
                    if role == Qt.DisplayRole:
                        textList.append(value)
            self.setText(', '.join(textList))