So ziehen Sie QListWidget per Drag & Drop auf ein anderes QListWidget

In einem Dialogfeld befinden sich zwei QListWIdgets. Die DragDrop-Funktionalität wurde für beide aktiviert. Wenn ich eine Datei auf einen von zwei ListWidges ziehe und dort ablege, erkennt das Programm sie und druckt die Liste der abgelegten Dateien aus. Aber abgesehen von Drag & Drop-Dateien möchte ich die Listen-Widget-Elemente per Drag & Drop von einem zum anderen verschieben können. Wenn ich die ListItems ziehe, wird das Drag & Drop-Ereignis ausgelöst. Es kann jedoch nicht erkennen, welche Elemente auf dem Widget abgelegt wurden. Der Beispielcode ist unten. Das Ziel ist, die Listenelemente von einem ListWidget auf ein anderes zu ziehen.

import sys, os
from PyQt4 import QtCore, QtGui   
class ThumbListWidget(QtGui.QListWidget):
    def __init__(self, type, parent=None):
        super(ThumbListWidget, self).__init__(parent)
        self.setAcceptDrops(True)
        self.setIconSize(QtCore.QSize(124, 124))

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls:
            event.accept()
        else:
            event.ignore()

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

    def dropEvent(self, event):
        if event.mimeData().hasUrls:
            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.ignore()


class Dialog_01(QtGui.QMainWindow):
    def __init__(self):
        super(QtGui.QMainWindow,self).__init__()
        self.listItems={}

        myQWidget = QtGui.QWidget()
        myBoxLayout = QtGui.QVBoxLayout()
        myQWidget.setLayout(myBoxLayout)
        self.setCentralWidget(myQWidget)

        self.listWidgetA = ThumbListWidget(self)
        for i in range(12): 
            QtGui.QListWidgetItem( 'Item '+str(i), self.listWidgetA )
        myBoxLayout.addWidget(self.listWidgetA)

        self.listWidgetB = ThumbListWidget(self)
        myBoxLayout.addWidget(self.listWidgetB)   

        self.listWidgetA.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
        self.listWidgetA.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
        self.connect(self.listWidgetA, QtCore.SIGNAL("dropped"), self.items_dropped)
        self.listWidgetA.currentItemChanged.connect(self.item_clicked)

        self.listWidgetB.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
        self.listWidgetB.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
        self.connect(self.listWidgetB, QtCore.SIGNAL("dropped"), self.items_dropped)
        self.listWidgetB.currentItemChanged.connect(self.item_clicked)

    def items_dropped(self, arg):
        print arg

    def item_clicked(self, arg):
        print arg

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    dialog_1 = Dialog_01()
    dialog_1.show()
    dialog_1.resize(480,320)
    sys.exit(app.exec_())
EDIT # 2

Hier ist der Code, der alles macht. Es gibt jedoch keine Möglichkeit, herauszufinden, welches Objekt fallen gelassen wurde. Die Methoden dropsOnA () und dropsOnB () funktionieren immer noch nicht.

from PyQt4 import QtGui, QtCore
import sys, os


class MyClassItem(QtGui.QListWidgetItem):
    def __init__(self, parent=None):
        super(QtGui.QListWidgetItem, self).__init__(parent)       


class ThumbListWidget(QtGui.QListWidget):
    def __init__(self, type, parent=None):
        super(ThumbListWidget, self).__init__(parent)
        self.setIconSize(QtCore.QSize(124, 124))
        self.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
        self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
        self.setAcceptDrops(True)

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            event.accept()
        else:
            super(ThumbListWidget, self).dragEnterEvent(event)

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

    def dropEvent(self, event):
        print 'dropEvent', event
        if event.mimeData().hasUrls():
            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.MoveAction)
            super(ThumbListWidget, self).dropEvent(event)


class Dialog_01(QtGui.QMainWindow):
    def __init__(self):
        super(QtGui.QMainWindow,self).__init__()
        self.listItems={}

        myQWidget = QtGui.QWidget()
        myBoxLayout = QtGui.QVBoxLayout()
        myQWidget.setLayout(myBoxLayout)
        self.setCentralWidget(myQWidget)

        self.listWidgetA = ThumbListWidget(self)
        self.listWidgetB = ThumbListWidget(self)

        for i in range(7):
            listItemAInstance=MyClassItem()
            listItemAInstance.setText('A'+'%04d'%i)
            listItemAInstance.setBackgroundColor(QtCore.Qt.darkGray)   
            if i%2: listItemAInstance.setBackgroundColor(QtCore.Qt.gray)
            self.listWidgetA.addItem(listItemAInstance) 

            listItemBInstance=MyClassItem()
            listItemBInstance.setText('B'+'%04d'%i)

            if i%2: listItemBInstance.setBackgroundColor(QtCore.Qt.lightGray)
            self.listWidgetB.addItem(listItemBInstance) 

        myBoxLayout.addWidget(self.listWidgetA)      

        myBoxLayout.addWidget(self.listWidgetB)   
        self.connect(self.listWidgetA, QtCore.SIGNAL("dropped"), self.droppedOnA)
        self.connect(self.listWidgetB, QtCore.SIGNAL("dropped"), self.droppedOnB)


    def droppedOnA(self, arg):
        print '\n\t droppedOnA', arg.text

    def droppedOnB(self, arg):
        print '\n\t droppedOnB', arg.text


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    dialog_1 = Dialog_01()
    dialog_1.show()
    dialog_1.resize(480,320)
    sys.exit(app.exec_())
EDIT # 3

Diesmal wird erneut versucht, mit MIME die Objekte des abgelegten Elements an ListWidget zu übergeben. Leider akzeptiert cPickle keine binären Objekte, die a werfen

TypeError: Der Typ sip.wrapper kann nicht instanziiert oder unterklassifiziert werden

Um das zu umgehen, konvertiere ich jeden Objektnamen in einen String und verwende ihn mit self.listItems = {} als Schlüssel, um die binären Objekte des Listenelements abzurufen. Welches scheint gut zu funktionieren. Aber am Ende, als ich es fast geschafft habe, fügt ein ListWidget ohne sichtbare Fehler das abgelegte Listenelement nicht zu sich selbst hinzu ... Es ist seltsam.

self.listWidgetB.addItem (dropsItemInstance)

.

from PyQt4 import QtGui, QtCore
import sys, os
import cPickle 

class MyClassItem(QtGui.QListWidgetItem):
    def __init__(self, parent=None):
        super(QtGui.QListWidgetItem, self).__init__(parent)       


class ThumbListWidget(QtGui.QListWidget):
    def __init__(self, type, parent=None):
        super(ThumbListWidget, self).__init__(parent)
        self.setIconSize(QtCore.QSize(124, 124))
        self.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
        self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
        self.setAcceptDrops(True)

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            event.accept()
        else:
            super(ThumbListWidget, self).dragEnterEvent(event)

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

    def dropEvent(self, event):

        if event.mimeData().hasUrls():
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()
            links = []
            for url in event.mimeData().urls():
                links.append(str(url.toLocalFile()))

        else:
            event.setDropAction(QtCore.Qt.MoveAction)
            super(ThumbListWidget, self).dropEvent(event)


    def mimeTypes(self):
        return ['bstream', 'text/xml']

    def mimeData(self, droppedItems):
        mimedata = QtCore.QMimeData()

        droppedItemsAsStrings=[]
        for each in droppedItems:            
            droppedItemsAsStrings.append( str(each) )

        bstream = cPickle.dumps(droppedItemsAsStrings)
        mimedata.setData('bstream', bstream)        
        return mimedata


    def dropMimeData(self, action, mimedata, row):

        if action == QtCore.Qt.IgnoreAction: return True  

        dropped=cPickle.loads(str(mimedata.data('bstream')))

        self.emit(QtCore.SIGNAL("dropped"), dropped)  

        return True


class Dialog_01(QtGui.QMainWindow):
    def __init__(self):
        super(QtGui.QMainWindow,self).__init__()
        self.listItems={}

        myQWidget = QtGui.QWidget()
        myBoxLayout = QtGui.QVBoxLayout()
        myQWidget.setLayout(myBoxLayout)
        self.setCentralWidget(myQWidget)

        self.listWidgetA = ThumbListWidget(self)
        self.listWidgetB = ThumbListWidget(self)

        for i in range(7):
            listItemAInstance=MyClassItem()
            listItemAInstance.setText('A'+'%04d'%i)
            listItemAInstance.setBackgroundColor(QtCore.Qt.darkGray)   
            if i%2: listItemAInstance.setBackgroundColor(QtCore.Qt.gray)
            self.listWidgetA.addItem(listItemAInstance) 

            listItemBInstance=MyClassItem()
            listItemBInstance.setText('B'+'%04d'%i)

            if i%2: listItemBInstance.setBackgroundColor(QtCore.Qt.lightGray)
            self.listWidgetB.addItem(listItemBInstance) 

            self.listItems[str(listItemAInstance)]=listItemAInstance
            self.listItems[str(listItemBInstance)]=listItemBInstance

        myBoxLayout.addWidget(self.listWidgetA)      

        myBoxLayout.addWidget(self.listWidgetB)   
        self.connect(self.listWidgetA, QtCore.SIGNAL("dropped"), self.droppedOnA)
        self.connect(self.listWidgetB, QtCore.SIGNAL("dropped"), self.droppedOnB)


    def droppedOnA(self, droppedItemsAsStrings):
        print '\n\t droppedOnA()'
        for each in droppedItemsAsStrings:
            if each in self.listItems.keys():
                droppedItemInstance = self.listItems[each]
                print 'adding', droppedItemInstance.text()
                self.listWidgetA.addItem(droppedItemInstance) 


    def droppedOnB(self, droppedItemsAsStrings):
        print '\n\t droppedOnB()'
        for each in droppedItemsAsStrings:
            if each in self.listItems.keys():
                droppedItemInstance = self.listItems[each]
                self.listWidgetB.addItem(droppedItemInstance) 
                print 'adding', droppedItemInstance.text()


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    dialog_1 = Dialog_01()
    dialog_1.show()
    dialog_1.resize(480,320)
    sys.exit(app.exec_())

Antworten auf die Frage(3)

Ihre Antwort auf die Frage