【问题标题】:PyQt5 draggable icons from a listview to anotherPyQt5 可拖动图标从列表视图到另一个
【发布时间】:2018-10-29 19:48:07
【问题描述】:

我很难将图标从列表视图(ViewMode 设置为 IconMode)拖放到另一个。它类似于我在docs 中找到的内容。

场景:用户将 QIcon 1 从 ListView 1 拖放到 ListView 2。ListView 2 应将 QIcon 1 添加到其模型中。此外,当将 特定 QIcon 添加到 ListView 2 时,我想做一些后台工作。我如何知道 QIcon 1 被删除到 ListView 2 而不是 QIcon 2?

主窗口(设置布局,将图像加载到列表视图 1):

class Ui_MainWindow(object):
...
    def loadImages(self):
        model = QStandardItemModel()
        images = Path("images").glob("*.*")
        for image in images:
            item = QStandardItem()
            item.setIcon(QIcon(str(image)))
            model.appendRow(item)

        self.listView1.setModel(model)

列表视图 1:

class ListView1(QListView):
    def __init__(self):
        super().__init__()
        self.setAcceptDrops(False)
        self.setViewMode(QtWidgets.QListView.IconMode)
        self.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.setIconSize(QSize(50, 50))
        self.setResizeMode(QtWidgets.QListView.Adjust)
        self.setDragDropMode(QAbstractItemView.DragOnly)

列表视图 2:

class ListView2(QListView):
    def __init__(self):
        super().__init__()
        self.setViewMode(QtWidgets.QListView.IconMode)
        self.setDragDropMode(QAbstractItemView.DropOnly)
        self.setIconSize(QSize(50, 50))
        self.setAcceptDrops(True)

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

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

    def dropEvent(self, event):
        event.accept()
        event.setDropAction(QtCore.Qt.MoveAction)
        event.acceptProposedAction()

如何将图标从列表视图 1 拖放到列表视图 2 并访问其属性?

【问题讨论】:

    标签: python pyqt pyqt5 qmainwindow qlistview


    【解决方案1】:

    没有必要覆盖dragEnterEvent、dragMoveEvent 或dropEvent,因为这些实现已经存在并且可以正常工作,您指出的示例是针对没有实现这些事件的其他类型的小部件。

    from pathlib import Path
    from PyQt5 import QtCore, QtGui, QtWidgets
    
    class LListView(QtWidgets.QListView):
        def __init__(self, parent=None):
            super(LListView, self).__init__(parent)
            self.model = QtGui.QStandardItemModel(self)
            self.setModel(self.model)
    
            self.setAcceptDrops(False)
            self.setViewMode(QtWidgets.QListView.IconMode)
            self.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
            self.setIconSize(QtCore.QSize(50, 50))
            self.setResizeMode(QtWidgets.QListView.Adjust)
            self.setDragDropMode(QtWidgets.QAbstractItemView.DragOnly)
    
    class RListView(QtWidgets.QListView):
        def __init__(self, parent=None):
            super(RListView, self).__init__(parent)
            self.model = QtGui.QStandardItemModel(self)
            self.setModel(self.model)
    
            self.setAcceptDrops(True)
            self.setViewMode(QtWidgets.QListView.IconMode)
            self.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
            self.setIconSize(QtCore.QSize(50, 50))
            self.setResizeMode(QtWidgets.QListView.Adjust)
            self.setDragDropMode(QtWidgets.QAbstractItemView.DropOnly)
    
    
    class MainWindow(QtWidgets.QMainWindow):
        def __init__(self, parent=None):
            super(MainWindow, self).__init__(parent)
    
            central_widget = QtWidgets.QWidget()
            self.l_view = LListView()
            self.r_view = RListView()
    
            self.setCentralWidget(central_widget)
            lay = QtWidgets.QHBoxLayout(central_widget)
            lay.addWidget(self.l_view)
            lay.addWidget(self.r_view)
            self.loadImages()
    
        def loadImages(self):
            images = Path("images").glob("*.*")
            for image in images:
                item = QtGui.QStandardItem()
                item.setIcon(QtGui.QIcon(str(image)))
                self.l_view.model.appendRow(item)
    
    
    if __name__ == '__main__':
        import sys
        app = QtWidgets.QApplication(sys.argv)
        w = MainWindow()
        w.show()
        sys.exit(app.exec_())
    

    更新:

    如果要添加标识符,可以通过创建项目时传递的角色来完成,然后在dropEvent() 中获取所有角色,然后获取所需的角色并通过它获得标识符:

    from pathlib import Path
    from PyQt5 import QtCore, QtGui, QtWidgets
    
    class LListView(QtWidgets.QListView):
        def __init__(self, parent=None):
            super(LListView, self).__init__(parent)
            self.m_model = QtGui.QStandardItemModel(self)
            self.setModel(self.m_model)
    
            self.setAcceptDrops(False)
            self.setViewMode(QtWidgets.QListView.IconMode)
            self.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
            self.setIconSize(QtCore.QSize(50, 50))
            self.setResizeMode(QtWidgets.QListView.Adjust)
            self.setDragDropMode(QtWidgets.QAbstractItemView.DragOnly)
    
    class RListView(QtWidgets.QListView):
        def __init__(self, parent=None):
            super(RListView, self).__init__(parent)
            self.m_model = QtGui.QStandardItemModel(self)
            self.setModel(self.m_model)
    
            self.setAcceptDrops(True)
            self.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
            self.setIconSize(QtCore.QSize(50, 50))
            self.setResizeMode(QtWidgets.QListView.Adjust)
            self.setDragDropMode(QtWidgets.QAbstractItemView.DropOnly)
    
        def dropEvent(self, event):
            last_row_count = self.model().rowCount()
            super(RListView, self).dropEvent(event)
            # check if an item is added
            if self.model().rowCount() > last_row_count:
                md = event.mimeData()
                fmt = "application/x-qabstractitemmodeldatalist"
                if md.hasFormat(fmt):
                    encoded = md.data(fmt)
                    stream = QtCore.QDataStream(encoded, QtCore.QIODevice.ReadOnly)
                    datas = []
                    item = {}
                    while not stream.atEnd():
                        row = stream.readInt32()
                        column = stream.readInt32()
                        map_items = stream.readInt32()
                        for i in range(map_items):
                            key = stream.readInt32()
                            value = QtCore.QVariant()
                            stream >> value
                            item[QtCore.Qt.ItemDataRole(key)] = value
                        datas.append(item)
                    for data in datas:
                        identifier = data[QtCore.Qt.UserRole+1].value()
                        print("identifier: ", identifier)
    
    
    class MainWindow(QtWidgets.QMainWindow):
        def __init__(self, parent=None):
            super(MainWindow, self).__init__(parent)
    
            central_widget = QtWidgets.QWidget()
            self.l_view = LListView()
            self.r_view = RListView()
    
            self.setCentralWidget(central_widget)
            lay = QtWidgets.QHBoxLayout(central_widget)
            lay.addWidget(self.l_view)
            lay.addWidget(self.r_view)
            self.loadImages()
    
        def loadImages(self):
            images = Path("images").glob("*.*")
            for i, image in enumerate(images):
                item = QtGui.QStandardItem()
                identifier = "img_{:06d}".format(i+1)
                item.setData(identifier, QtCore.Qt.UserRole+1)
                item.setIcon(QtGui.QIcon(str(image)))
                self.l_view.m_model.appendRow(item)
    
    
    if __name__ == '__main__':
        import sys
        app = QtWidgets.QApplication(sys.argv)
        w = MainWindow()
        w.show()
        sys.exit(app.exec_())
    

    【讨论】:

    • 如果我不重写这些方法,我怎么能听一个 drop 事件?例如,我想知道删除了哪个图标,以便我可以将其从 LListView 中删除,并在后台使用该图标执行一些其他操作。所以我需要一个图标的路径或名称
    • @momo QIcon 不再是一个文件,所以它没有名称,QIcon 是加载在 RAM 中的数据,所以我建议你重新考虑你想要什么。
    • 我明白了。因此,如果我想通过拖放功能在网格中显示图像(而不是 QIcons),我不能使用 QListView 并且必须自己使用 QGridLayout 和 QPixmap 来实现它?
    • QPixmap 也是 RAM 中的数据,例如制作一个显示 .png 图像的程序,然后将其删除,您会看到即使保留了程序的图像,.png文件仅用作图像的初始源。你可以更简洁地表达你想要什么,你想要什么功能?你想要一个带有 QLabels 的 QGridLayout 并拖动图像并将它们设置为 QLabels 的 QPixmap?
    • 我想在 nxn 网格视图中显示图像并将图像拖动到另一个 1xn 网格视图。因此我的方法是使用两个 ViewMode 设置为 IconMode 的 QListView,因为它似乎可以根据窗口大小完美调整大小。但是通过这种方法,我无法知道哪个 QIcon(我的案例图像)已被删除,因为我无法获得已删除 QIcon 的名称或路径。根据您的回答,我假设我必须使用 QGridLayout 和 QLabels?
    猜你喜欢
    • 2011-01-31
    • 2017-06-11
    • 1970-01-01
    • 1970-01-01
    • 2015-11-14
    • 1970-01-01
    • 2013-09-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多