Search code examples
pythondrag-and-droppyqtqlistwidgetqlistwidgetitem

How can copy with drag and drop from one list but use move for internal drag and drop


I need to be able to copy an item from one listWidget to another. This is easy enough to do but I can't seem to figure out a way to differentiate drop actions based on whether the item being dragged originated in the list it is being dropped to without perhaps having to override almost every drag and drop function with my own. When I drag an item from one list to the other I want to copy it but when I drag the item in the same list I want to move it.

I've been looking into setting the mimetypes but then I have to write my own mouseMoveEvent as a way to perhaps tell where the dragged item is coming from but so far trying this breaks everything. Is it not possible to set a mime type for an item without overriding mouseMoveEvent ?

Since the items I am dragging are customized I have to write my own definition to rebuilt it when it gets moved or copied to the second list. With the default drag functions this all works fine with internal moves. But so far I have not been able to figure out how to use the default drag drop functions when the drag is an internal move and then switch to my custom function to copy the item when the drop is coming from a different list.

import sys
from PyQt4 import QtGui , QtCore


def main():

    app = QtGui.QApplication(sys.argv)

    w = QtGui.QWidget()
    w.resize(250, 150)
    w.move(300, 300)
    w.setWindowTitle('Simple')
    layout=QtGui.QHBoxLayout(w)
    dragList=DragDropListWidget()
    layout.addWidget(dragList)
    dragList.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
    dragList.name='dragList'
    dragList.populate(['one','two','three'])
    dragList2=DragDropListWidget()
    dragList2.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
    dragList2.name='dragList'


    layout.addWidget(dragList2)
    w.show()

    sys.exit(app.exec_())

class scriptsWidget(QtGui.QWidget):


    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self)

        self.name=''

        self.widget_QHBoxLayout = QtGui.QHBoxLayout(self)
        self.widget_QHBoxLayout.setSpacing(0)
        self.widget_QHBoxLayout.setContentsMargins(0, 0, 0, 0)

        self.name_QLabel = QtGui.QLabel(self)
        self.widget_QHBoxLayout.addWidget(self.name_QLabel)

        self.user_QLabel = QtGui.QLabel(self)
        self.widget_QHBoxLayout.addWidget(self.user_QLabel)

        self.widget_QHBoxLayout.setSpacing(0)
        self.widget_QHBoxLayout.setContentsMargins(0, 0, 0, 0)



    def setName(self,name):
        self.name_QLabel.setText(name)
        self.name=name

    def setUser(self,user):
        self.user_QLabel.setText(user)

class customQListWidgetItem(QtGui.QListWidgetItem):


    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self)
        self.name=''

    def setName(self,name):
        self.name=name   




class DragDropListWidget(QtGui.QListWidget):
    _drag_info = []
    def __init__(self, parent = None):

        super(DragDropListWidget, self).__init__(parent)


        self.name=''


    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls():
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()

        else:
            super(DragDropListWidget, self).dragMoveEvent(event)


    def dropEvent(self, event):
        if event.mimeData().hasText():
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()
            links = []
            for url in event.mimeData().urls():
                links.append(str(url.toLocalFile()))
            self.emit(QtCore.SIGNAL("dropped"), links)

        else:
            event.setDropAction(QtCore.Qt.CopyAction)
            items = []
            for index in xrange(self.count()):
                items.append(self.item(index))

            super(DragDropListWidget, self).dropEvent(event)

            for index in xrange(self.count()):
                if self.item(index) not in items:
                    self.populateDrop(self.item(index), index, [self.item(index).data(QtCore.Qt.UserRole).toPyObject()])

    def populateDrop(self,item,row,items=[]):
        for i in items:
            widget = scriptsWidget()
            widget.setName(i)
            widget.setUser('x')
            self.takeItem(row)
            item = customQListWidgetItem()
            item.setName(i)
            item.setWhatsThis(i)
            data = (i)
            item.setData(QtCore.Qt.UserRole, data)
            self.insertItem (row, item)
            self.setItemWidget(item,widget)



    def populate(self,items=[]):
        self.clear()
        for i in items:
            print(i)
            widget = scriptsWidget()
            widget.setName(i)
            widget.setUser('x')
            item = customQListWidgetItem()
            item.setName(i)
            data = (i)
            item.setData(QtCore.Qt.UserRole, data)
            self.addItem(item)
            self.setItemWidget(item,widget)



if __name__ == '__main__':
    main()

Solution

  • Well this is what I came up with. I'm setting a class data variable when the item is clicked so I can tell where it came from when the the item is dropped. I tried using a class variable inside DragDropListWidget but for some reason it would only store the local name of the current list.. weird. A global variable also worked but is not desirable.

    import sys
    from PyQt4 import QtGui , QtCore
    
    def main():
    
        app = QtGui.QApplication(sys.argv)
    
        w = QtGui.QWidget()
        w.resize(250, 150)
        w.move(300, 300)
        w.setWindowTitle('Simple')
        layout=QtGui.QHBoxLayout(w)
        dragList=DragDropListWidget()
        layout.addWidget(dragList)
        d=data()
        dragList.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
        dragList.name='dragList'
        dragList.populate(['one','two','three'])
        dragList.data=d
        dragList2=DragDropListWidget()
        dragList2.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
        dragList2.name='dragList2'
        dragList2.external='dragList2'
        dragList2.data=d
    
    
        layout.addWidget(dragList2)
        w.show()
    
        sys.exit(app.exec_())
    
    class data(object):
        def __init__(self, parent=None):
            self.origin='new'
    
    
    
    
    class scriptsWidget(QtGui.QWidget):
    
    
        def __init__(self, parent=None):
            QtGui.QWidget.__init__(self)
    
            self.name=''
    
            self.widget_QHBoxLayout = QtGui.QHBoxLayout(self)
            self.widget_QHBoxLayout.setSpacing(0)
            self.widget_QHBoxLayout.setContentsMargins(0, 0, 0, 0)
    
            self.name_QLabel = QtGui.QLabel(self)
            self.widget_QHBoxLayout.addWidget(self.name_QLabel)
    
            self.user_QLabel = QtGui.QLabel(self)
            self.widget_QHBoxLayout.addWidget(self.user_QLabel)
    
            self.widget_QHBoxLayout.setSpacing(0)
            self.widget_QHBoxLayout.setContentsMargins(0, 0, 0, 0)
    
    
    
        def setName(self,name):
            self.name_QLabel.setText(name)
            self.name=name
    
        def setUser(self,user):
            self.user_QLabel.setText(user)
    
    class customQListWidgetItem(QtGui.QListWidgetItem):
    
    
        def __init__(self, parent=None):
            QtGui.QWidget.__init__(self)
            self.name=''
            self.list=''
    
        def setName(self,name):
            self.name=name   
    
    
    
    
    class DragDropListWidget(QtGui.QListWidget):
    
        def __init__(self, parent = None):
    
            super(DragDropListWidget, self).__init__(parent)
            self._dropping = False
            self.itemPressed.connect(self.clicked)
    
            self.data=''
    
            self.name=''
            self.external=''
            self.internal=False
    
        def clicked(self):
            global origin 
            self.data.origin=self.name
    
    
        def dragMoveEvent(self, event):
    
            if event.mimeData().hasUrls():
                event.setDropAction(QtCore.Qt.CopyAction)
                event.accept()
    
            else:
    
                super(DragDropListWidget, self).dragMoveEvent(event)
    
    
        def dropEvent(self, event):
    
            if event.mimeData().hasText():
                event.setDropAction(QtCore.Qt.CopyAction)
                event.accept()
                links = []
                for url in event.mimeData().urls():
                    links.append(str(url.toLocalFile()))
                self.emit(QtCore.SIGNAL("dropped"), links)
    
            else:
                if self.external is self.name and self.data.origin is not self.name:
                    print('external')
                    event.setDropAction(QtCore.Qt.CopyAction)
                    items = []
                    for index in xrange(self.count()):
                        items.append(self.item(index))
                    print items
    
                    super(DragDropListWidget, self).dropEvent(event)
    
                    for index in xrange(self.count()):
                        if self.item(index) not in items:
                            print(index)
                            self.populateDrop(self.item(index), index, [self.item(index).data(QtCore.Qt.UserRole).toPyObject()])
    
                else:
                    print('internal')
                    event.setDropAction(QtCore.Qt.MoveAction)
    
                    super(DragDropListWidget, self).dropEvent(event)
    
            self.data.origin = None       
    
        def populateDrop(self,item,row,items=[]):
            for i in items:
                widget = scriptsWidget()
                widget.setName(i)
                widget.setUser('x')
                self.takeItem(row)
                item = customQListWidgetItem()
                item.setName(i)
                item.setWhatsThis(i)
                item.list=self.name
                data = (i)
                item.setData(QtCore.Qt.UserRole, data)
                self.insertItem (row, item)
                self.setItemWidget(item,widget)
    
    
    
        def populate(self,items=[]):
            self.clear()
            for i in items:
                print(i)
                widget = scriptsWidget()
                widget.setName(i)
                widget.setUser('x')
    
                item = customQListWidgetItem()
                item.setName(i)
                item.list=self.name
                data = (i)
                item.setData(QtCore.Qt.UserRole, data)
                self.addItem(item)
                self.setItemWidget(item,widget)
    
    
    
    if __name__ == '__main__':
        main()