简体   繁体   English

PyQt:如何在dropEvent上获取QListWidget的listItem .data()

[英]PyQt: How to get QListWidget's listItem .data() on dropEvent

There are two QListWidgets. 有两个QListWidgets。 Every list item was assigned MyClass()'s instance using setData(QtCore.Qt.UserRole, myClassInstA) . 使用setData(QtCore.Qt.UserRole, myClassInstA)为每个列表项分配MyClass()的实例。

Clicking a lower QListWidget's listItem prints out a clicked listItem's data retrieved using: 单击较低的QListWidget的listItem将打印出使用以下方法检索的单击的listItem数据:

.data(QtCore.Qt.UserRole).toPyObject()

Any itemA dragged and dropped from top QListWidget onto a lower shows None for data. 从顶部QListWidget拖放到底部的任何itemA均显示None。 Interesting that clicking the same item prints there is a data. 有趣的是,单击同一项目会打印出一个数据。 I wonder if it is possible to retrieve a data stored in listItem within droppedOnB() function (so droppedOnB() is able to print out the data stored in a item). 我想知道是否有可能在dropOnB()函数中检索存储在listItem中的数据(因此dropOnB()能够打印出存储在项目中的数据)。


from PyQt4 import QtGui, QtCore
import sys, os

class MyClass(object):
        def __init__(self):
            super(MyClass, self).__init__()               

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

    def startDrag(self, actions):
        self._drag_info[:] = [str(self.objectName())]
        for item in self.selectedItems():
            self._drag_info.append(self.row(item))
        super(ThumbListWidget, self).startDrag(actions)

    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)
            self._dropping = True
            super(ThumbListWidget, self).dropEvent(event)
            self._dropping = False

    def rowsInserted(self, parent, start, end):
        if self._dropping:
            self._drag_info.append((start, end))
            self.emit(QtCore.SIGNAL("dropped"), self._drag_info)
        super(ThumbListWidget, self).rowsInserted(parent, start, end)


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, 'MainTree')
        self.listWidgetB = ThumbListWidget(self, 'SecondaryTree')
        self.listWidgetB.setDragDropMode(QtGui.QAbstractItemView.DropOnly)

        for i in range(7):
            listItemA=QtGui.QListWidgetItem()
            listItemA.setText('A'+'%04d'%i)
            self.listWidgetA.addItem(listItemA)

            myClassInstA=MyClass()
            listItemA.setData(QtCore.Qt.UserRole, myClassInstA)

            listItemB=QtGui.QListWidgetItem()
            listItemB.setText('B'+'%04d'%i)
            self.listWidgetB.addItem(listItemB)

            myClassInstB=MyClass()
            listItemB.setData(QtCore.Qt.UserRole, myClassInstB)

        myBoxLayout.addWidget(self.listWidgetA)      

        myBoxLayout.addWidget(self.listWidgetB)   
        self.connect(self.listWidgetB, QtCore.SIGNAL("dropped"), self.droppedOnB)
        self.listWidgetB.clicked.connect(self.itemClicked)


    def droppedOnB(self, dropped_list):
        print '\n\t droppedOnB()', dropped_list, dropped_list[-1]

        destIndexes=dropped_list[-1]
        for index in range(destIndexes[0],destIndexes[-1]+1):
            dropedItem=self.listWidgetB.item(index)
            modelIndex=self.listWidgetB.indexFromItem(dropedItem)
            dataObject = modelIndex.data(QtCore.Qt.UserRole).toPyObject()
            print '\n\t\t droppedOnB()', type(modelIndex), type(dataObject)

    def itemClicked(self, modelIndex):      
        dataObject = modelIndex.data(QtCore.Qt.UserRole).toPyObject()
        print 'itemClicked(): ' ,type(modelIndex), type(dataObject)


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

The ultimate goal was to replace MyClassA 's instance data -object (attached to itemA) with MyClassB after itemA is dropped onto listWidgetB. 最终目标是意达滴到listWidgetB后MyClassB更换MyClassA的实例数据 -object(连接到意达)。

When itemA is dropped onto the destination listWidgetB the itemA arrives with no data stored. 将itemA放到目标listWidgetB上时,iteA到达时未存储任何数据。 It returns None if you try to retrieve it with itemA.data(QtCore.Qt.UserRole).toPyObject() (if done inside of droppedOnB() - a method triggered first onDrop event). 如果尝试使用itemA.data(QtCore.Qt.UserRole).toPyObject()检索它,则返回None(如果在dropOnB()内完成-一个方法触发第一个onDrop事件)。

Trying to assign, reassign a data to just dropped listItem... or even removing it causes all kind of surprises later. 尝试分配,重新分配数据到刚刚删除的listItem ...甚至删除它,都会在以后引起各种意外。 I am using .setHidden(True). 我正在使用.setHidden(True)。

Those are the steps: 这些是步骤:

  1. Get source listItem and its data. 获取源listItem及其数据。
  2. Get destination (dropped) item and hide it. 获取目的地(已删除)项并将其隐藏。
  3. Declare MyClassB() instance 声明MyClassB()实例
  4. Create new list item. 创建新的列表项。 Assign created in previous step MyClassB() instance object to listItem using .setData(QtCore.Qt.UserRole, myClassInstB) 使用.setData(QtCore.Qt.UserRole,myClassInstB)将在上一步MyClassB()实例对象中创建的对象分配给listItem
  5. Add new listItem to ListWidget. 将新的listItem添加到ListWidget。

Here is a functional code. 这是一个功能代码。 Once again, itemA is assign an instance of ClassA on creation. 在创建时,再次为itemA分配ClassA的实例。 After itemA is dropped onto listWidgetB a dropped item is hidden and 'substituted' with another item to which classB instance is assigned. 在将itemA放置到listWidgetB上之后,隐藏的项目被隐藏,并被分配了classB实例的另一个项目“替代”。

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

class MyClassA(object):
        def __init__(self):
            super(MyClassA, self).__init__()               

class MyClassB(object):
        def __init__(self):
            super(MyClassB, self).__init__()
            self.DataObjectCopy=None  

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

    def startDrag(self, actions):
        self._drag_info[:] = [str(self.objectName())]
        for item in self.selectedItems():
            self._drag_info.append(self.row(item))
        super(ThumbListWidget, self).startDrag(actions)

    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)
            self._dropping = True
            super(ThumbListWidget, self).dropEvent(event)
            self._dropping = False

    def rowsInserted(self, parent, start, end):
        if self._dropping:
            self._drag_info.append((start, end))
            self.emit(QtCore.SIGNAL("dropped"), self._drag_info)
        super(ThumbListWidget, self).rowsInserted(parent, start, end)


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, 'MainTree')
        self.listWidgetB = ThumbListWidget(self, 'SecondaryTree')
        self.listWidgetB.setDragDropMode(QtGui.QAbstractItemView.DropOnly)

        for i in range(3):
            listItemA=QtGui.QListWidgetItem()
            listItemA.setText('A'+'%04d'%i)
            self.listWidgetA.addItem(listItemA)

            myClassInstA=MyClassA()
            listItemA.setData(QtCore.Qt.UserRole, myClassInstA)

            listItemB=QtGui.QListWidgetItem()
            listItemB.setText('B'+'%04d'%i)
            self.listWidgetB.addItem(listItemB)

            myClassInstB=MyClassB()
            listItemB.setData(QtCore.Qt.UserRole, myClassInstB)

        myBoxLayout.addWidget(self.listWidgetA)      

        myBoxLayout.addWidget(self.listWidgetB)   
        self.connect(self.listWidgetB, QtCore.SIGNAL("dropped"), self.droppedOnB)
        self.listWidgetB.clicked.connect(self.itemClicked)


    def droppedOnB(self, dropped_list):
        if not dropped_list or len(dropped_list)<3: return       

        srcIndexes=dropped_list[1:-1]
        destIndexes=dropped_list[-1]

        # build a list of corresponding src-to-dest indexes sets
        itemsIndxes=[]
        i=0
        for num in range(destIndexes[0], destIndexes[-1]+1):
            itemsIndxes.append((srcIndexes[i], num))
            i+=1 

        print '\n\t droppedOnB(): dropped_list =',dropped_list,'; srcIndexes =',srcIndexes,'; destIndexes =',destIndexes, '; itemsIndxes =',itemsIndxes

        for indexSet in itemsIndxes:
            srcNum = indexSet[0]
            destIndxNum = indexSet[-1]

            # Get source listItem's data object
            srcItem=self.listWidgetA.item(srcNum)
            if not srcItem: continue
            srcItemData = srcItem.data(QtCore.Qt.UserRole)
            if not srcItemData: continue
            srcItemDataObject=srcItemData.toPyObject()
            if not srcItemDataObject: continue
            # make a deepcopy of src data object
            itemDataObject_copy=copy.deepcopy(srcItemDataObject)

            # get dropped item
            droppedItem=self.listWidgetB.item(destIndxNum)
            # hide dropped item since removing it results to mess
            droppedItem.setHidden(True)
            # declare new ClassB instance
            myClassInstB=MyClassB()
            myClassInstB.DataObjectCopy=itemDataObject_copy

            # create a new listItem
            newListItem=QtGui.QListWidgetItem()
            newListItem.setData(QtCore.Qt.UserRole, myClassInstB)
            newListItem.setText(srcItem.text())
            self.listWidgetB.blockSignals(True)
            self.listWidgetB.addItem(newListItem)
            self.listWidgetB.blockSignals(False)


    def itemClicked(self, modelIndex):      
        dataObject = modelIndex.data(QtCore.Qt.UserRole).toPyObject()
        print 'itemClicked(): instance type:', type(dataObject)


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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM