有两个QListWIdgets位于同一个对话窗口中 . 已为两者启用DragDrop功能 . 如果我将文件拖放到两个ListWidges中的任何一个,程序会识别它并打印出丢弃的文件列表 . 但是除了拖放文件之外,我希望能够将List小部件项从一个拖放到另一个 . 如果我拖动ListItems,则会触发拖放事件 . 但它无法识别哪些项目被放到窗口小部件上 . 示例代码如下 . 目标是将列表项从一个ListWidget拖放到另一个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
这是完成所有工作的代码 . 但是没有办法追踪丢弃的物体 . lostOnA()和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 '\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_())
编辑#3
这次是另一次尝试使用MIME将已删除的Item的对象传递给ListWidget . 不幸的是,cPickle拒绝接受投掷的二进制对象
TypeError:sip.wrapper类型无法实例化或子类化
为了解决这个问题,我将每个对象名称转换为字符串,并将其与self.listItems = {}字典一起用作检索列表项的二进制对象的键 . 这似乎运作良好 . 但最后,当我几乎完成所有操作时,没有可见错误的ListWidget不会将删除的列表项添加到自身......这很奇怪 .
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 '\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_())
3 回答
这是修改后的代码 . 它的工作就像一个魅力!好样的!
假设您要移动list-widget项,您的子类应如下所示(请注意
setDragDropMode
和setSelectionMode
已移至__init__
):在实现
super()
的Python 3样式时,上面的代码对我不起作用 .问题是继承的方法
dragMoveEvent
和dragDropEvent
没有被子类中的新定义覆盖 .因此,未调用
event.setDropAction(QtCore.Qt.MoveAction)
,并且窗口小部件行为默认为QtCore.Qt.CopyAction
.我使用从
QAbstractItemView
类继承的setDefaultDropAction()
方法解决了这个问题 .这是PyQt5和Python 3.7中的实现: