Как перетащить из одного QListWidget в другой

в одном диалоговом окне находятся два QListWIdgets. Функция DragDrop была включена для обоих. Если я перетаскиваю файл в любой из двух ListWidges, программа распознает его и распечатывает список упавших файлов. Но помимо перетаскивания файлов, я хотел бы иметь возможность перетаскивать элементы виджета списка из одного в другой. Если я перетаскиваю ListItems, событие перетаскивания запускается. Но он не может распознать, какие предметы были сброшены на штучка. Пример кода приведен ниже. Цель состоит в том, чтобы перетащить элементы списка из одного ListWidget в другой.

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_())

правка # 2

вот код, который делает все это. Но нет никакого способа отследить, какой объект был сброшен. Методы droppedOnA() и droppedOnB() по-прежнему не работают.


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 'nt droppedOnA', arg.text

    def droppedOnB(self, arg):
        print 'nt 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_())

редактировать # 3

вот еще одна попытка на этот раз с помощью MIME передать объекты упавшего элемента в ListWidget. К сожалению cPickle отказывается принимать двоичные объекты, бросающие

TypeError: глоток.тип оболочки не может быть создан экземпляром или подклассом

чтобы обойти это, я преобразую имена каждого объекта в строку и использую его с собой.listitems={} словарь в качестве ключа для извлечения двоичных объектов элемента списка. Что, кажется, работает хорошо. Но в конце, когда я почти подумал, что все это сделано, ListWidget без видимых ошибок не добавляет к себе элемент dropped list... Это странный.

self.listWidgetB.addItem (droppedItemInstance)

.


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 'nt 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 'nt 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_())

2 ответов


вот пересмотренный код. Это работает как шарм! Браво!

from PyQt4 import QtGui, QtCore
import sys, os

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)
        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.connect(self.listWidgetA, QtCore.SIGNAL("dropped"), self.items_dropped)
        self.listWidgetA.currentItemChanged.connect(self.item_clicked)

        self.connect(self.listWidgetB, QtCore.SIGNAL("dropped"), self.items_dropped)
        self.listWidgetB.currentItemChanged.connect(self.item_clicked)

    def items_dropped(self, arg):
        print 'items_dropped', 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_())

если вы хотите движение список-элементы виджета, ваш подкласс должен быть, как показано ниже (обратите внимание, что setDragDropMode и setSelectionMode переехали в __init__):

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()))
            self.emit(QtCore.SIGNAL("dropped"), links)
        else:
            event.setDropAction(QtCore.Qt.MoveAction)
            super(ThumbListWidget, self).dropEvent(event)