如何从 QListview 添加图片到 QGraphicsView? (拖放)

How can I add pictures from QListview to QGraphicsView? (with Drag Drop)

首先,我创建了 QListview class。然后,我将项目添加到 QListview 中。之后,我创建了 QGraphicsView class。我想通过拖放将项目的图片(由 QListview 创建)传输到 QGraphicsView。我有个问题。我无法将项目的图片 class 传输到 class。我的代码是这样的

class PartsList(QListView):
    def __init__(self, parent):
        super().__init__(parent)        
        self.setGridSize(QSize(108, 80))        
        self.partsModel = LibraryModel(self)        
        for i in image_list:
            self.partsModel.appendRow(self.__getPartItem(i))
        self.setModel(self.partsModel)       
        self.setViewMode(self.IconMode)        
        self.setDragDropMode(self.DragOnly)

    def __getPartItem(self, name):
        part = QStandardItem()
        pixmap = QPixmap(name)
        part.setData(pixmap, Qt.DecorationRole)
        part.setText(name)
        part.setEditable(False)
        return part

class LibraryModel(QStandardItemModel):
    def __init__(self, parent=None):
        QStandardItemModel.__init__(self, parent)
        self.setColumnCount(1)

    def mimeTypes(self):
        return ['part/name']

    def DropMimeData(self, idxs):
       mimedata = QMimeData()
       for idx in idxs:
           if idx.isValid():
               txt = self.data(idx, Qt.DisplayRole)
               mimedata.setText(txt)
               mimedata.setData('part/name', txt)
        return mimedata

QGraphicsView

class SchematicView(QGraphicsView):
    def __init__(self, parent):
        self.scene = SchematicScene()
        super().__init__(self.scene, parent)
        self.setSceneRect(0, 0, 1, 1)

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

   def dragMoveEvent(self, event):
        event.accept()


   def dropEvent(self, event):
        pass

没有必要创建一个新的 mimetype,因为模型默认已经有一个 "application/x-qabstractitemmodeldatalist",所以在这种情况下,我将只在 QGraphicsView 中创建一个虚拟模型来接收数据,从而能够在 QGraphicsView 中重用它。

from PyQt5 import QtCore, QtGui, QtWidgets


class PartsList(QtWidgets.QListView):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setGridSize(QtCore.QSize(108, 80))
        self.setViewMode(QtWidgets.QListView.IconMode)
        self.setDragDropMode(QtWidgets.QAbstractItemView.DragOnly)
        self._model = QtGui.QStandardItemModel(self)
        self.setModel(self._model)
        path = QtCore.QStandardPaths.writableLocation(
            QtCore.QStandardPaths.PicturesLocation
        )
        d = QtCore.QDir(path)
        formats = [
            "*.{}".format(fm.data().decode())
            for fm in QtGui.QImageReader.supportedImageFormats()
        ]
        for info in d.entryInfoList(formats, QtCore.QDir.Files):
            part = QtGui.QStandardItem(info.filePath())
            pixmap = QtGui.QPixmap(info.absoluteFilePath())
            part.setData(pixmap, QtCore.Qt.DecorationRole)
            self._model.appendRow(part)


class SchematicView(QtWidgets.QGraphicsView):
    def __init__(self, parent=None):
        super().__init__(parent)
        scene = QtWidgets.QGraphicsScene(self)
        self.setScene(scene)
        self.setSceneRect(0, 0, 1, 1)
        self.setAcceptDrops(True)

    def dragEnterEvent(self, event):
        if event.mimeData().hasFormat(
            "application/x-qabstractitemmodeldatalist"
        ):
            event.acceptProposedAction()

    def dragMoveEvent(self, event):
        if event.mimeData().hasFormat(
            "application/x-qabstractitemmodeldatalist"
        ):
            event.acceptProposedAction()

    def dropEvent(self, event):
        sp = self.mapToScene(event.pos())
        dummy_model = QtGui.QStandardItemModel()
        dummy_model.dropMimeData(
            event.mimeData(), event.dropAction(), 0, 0, QtCore.QModelIndex()
        )
        for r in range(dummy_model.rowCount()):
            for c in range(dummy_model.columnCount()):
                ix = dummy_model.index(r, c)
                pixmap = ix.data(QtCore.Qt.DecorationRole)
                pixmap_item = self.scene().addPixmap(pixmap)
                pixmap_item.setPos(sp)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = QtWidgets.QWidget()
    hlay = QtWidgets.QHBoxLayout(w)
    pl = PartsList()
    sv = SchematicView()
    hlay.addWidget(pl)
    hlay.addWidget(sv)
    w.resize(640, 480)
    w.show()
    sys.exit(app.exec_())

如果您仍想继续使用其他 mimetype,在支持 QListView 等模型的视图中的正确选项是向 mimeData 添加新格式:

from PyQt5 import QtCore, QtGui, QtWidgets


class PartsList(QtWidgets.QListView):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setGridSize(QtCore.QSize(108, 80))
        self.setViewMode(QtWidgets.QListView.IconMode)
        self.setDragDropMode(QtWidgets.QAbstractItemView.DragOnly)
        self._model = LibraryModel(self)
        self.setModel(self._model)
        path = QtCore.QStandardPaths.writableLocation(
            QtCore.QStandardPaths.PicturesLocation
        )
        d = QtCore.QDir(path)
        formats = [
            "*.{}".format(fm.data().decode())
            for fm in QtGui.QImageReader.supportedImageFormats()
        ]
        for info in d.entryInfoList(formats, QtCore.QDir.Files):
            part = self.__getPartItem(info.filePath())
            self._model.appendRow(part)

    def __getPartItem(self, name):
        part = QtGui.QStandardItem()
        pixmap = QtGui.QPixmap(name)
        part.setData(pixmap, QtCore.Qt.DecorationRole)
        part.setText(name)
        part.setEditable(False)
        return part


class LibraryModel(QtGui.QStandardItemModel):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setColumnCount(1)

    def mimeTypes(self):
        return super().mimeTypes() + ["part/name"]

    def mimeData(self, indexes):
        data = super().mimeData(indexes)
        encoded = QtCore.QByteArray()
        stream = QtCore.QDataStream(encoded, QtCore.QIODevice.WriteOnly)
        for ix in indexes:
            stream << ix.data(QtCore.Qt.DecorationRole)
        data.setData("part/name", encoded)
        return data


class SchematicView(QtWidgets.QGraphicsView):
    def __init__(self, parent=None):
        super().__init__(parent)
        scene = QtWidgets.QGraphicsScene(self)
        self.setScene(scene)
        self.setSceneRect(0, 0, 1, 1)
        self.setAcceptDrops(True)

    def dragEnterEvent(self, event):
        if event.mimeData().hasFormat("part/name"):
            event.acceptProposedAction()

    def dragMoveEvent(self, event):
        if event.mimeData().hasFormat("part/name"):
            event.acceptProposedAction()

    def dropEvent(self, event):
        sp = self.mapToScene(event.pos())
        fmt = "part/name"
        data = event.mimeData()
        if data.hasFormat(fmt):
            encoded = data.data(fmt)
            stream = QtCore.QDataStream(encoded, QtCore.QIODevice.ReadOnly)
            pixmap = QtGui.QPixmap()
            while not stream.atEnd():
                stream >> pixmap
                pixmap_item = self.scene().addPixmap(pixmap)
                pixmap_item.setPos(sp)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = QtWidgets.QWidget()
    hlay = QtWidgets.QHBoxLayout(w)
    pl = PartsList()
    sv = SchematicView()
    hlay.addWidget(pl)
    hlay.addWidget(sv)
    w.resize(640, 480)
    w.show()
    sys.exit(app.exec_())