I'm trying to create a drag 'n drop label which accepts only PDF files.
To do that I try using the mimeData().hasFormat.
def dragEnterEvent(self, event):
if event.mimeData().hasFormat(application/pdf):
event.accept()
else:
event.ignore()
def dragMoveEvent(self, event):
if event.mimeData().hasFormat(application/pdf):
event.accept()
else:
event.ignore()
def dropEvent(self, event):
if event.mimeData().hasFormat(application/pdf):
event.setDropAction(Qt.CopyAction)
file_path = event.mimeData().urls()[0].toLocalFile()
self.set_path(file_path)
event.accept()
else:
event.ignore()
It does not seem to work though.
I tried it with mimeData().hasImage for an image file and it works.
Does .hasFormat support pdf files?
You can get the mimetype based on the url and verify that it corresponds to a pdf:
from PyQt5.QtCore import QMimeDatabase
from PyQt5.QtWidgets import QApplication, QLabel
class Label(QLabel):
def __init__(self, parent=None):
super().__init__(parent)
self.setAcceptDrops(True)
def dragEnterEvent(self, event):
if self.find_pdf(event.mimeData()):
event.accept()
else:
event.ignore()
def dragMoveEvent(self, event):
if self.find_pdf(event.mimeData()):
event.accept()
else:
event.ignore()
def dropEvent(self, event):
urls = self.find_pdf(event.mimeData())
if urls:
for url in urls:
print(url.toLocalFile())
event.accept()
else:
event.ignore()
def find_pdf(self, mimedata):
urls = list()
db = QMimeDatabase()
for url in mimedata.urls():
mimetype = db.mimeTypeForUrl(url)
if mimetype.name() == "application/pdf":
urls.append(url)
return urls
def main():
app = QApplication([])
label = Label()
label.resize(640, 480)
label.show()
app.exec_()
if __name__ == "__main__":
main()
Related
I am trying to drag and drop a folder into a a QTreeWidget and it will print out all the files in that folder and any other folder structures under that. I am very unfamiliar with QTreeWidget and in my example I was just trying to add data under the 'Name' column everything I tried i got different errors from model is a private method to argument 1 has unexpected type 'Tree'. How can I add data under each column?
import os
from PyQt5.QtWidgets import QFileSystemModel, QApplication, QAbstractItemView, \
QMainWindow, QPushButton, QTreeWidget, QTreeWidgetItem
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class Tree(QTreeWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setAcceptDrops(True)
self.resize(600, 600)
self.setHeaderLabels(['Name', 'Size', 'Upload Status'])
model = QFileSystemModel()
model.setRootPath(QDir.currentPath())
model.setReadOnly(False)
self.setSelectionMode(self.SingleSelection)
self.setDragDropMode(QAbstractItemView.InternalMove)
self.setDragEnabled(True)
self.setAcceptDrops(True)
self.setDropIndicatorShown(True)
def dragEnterEvent(self, event):
if event.mimeData().hasUrls:
event.accept()
else:
event.ignore()
def dragMoveEvent(self, event):
if event.mimeData().hasUrls():
event.setDropAction(Qt.CopyAction)
event.accept()
else:
event.ignore()
def dropEvent(self, event):
if event.mimeData().hasUrls():
event.setDropAction(Qt.CopyAction)
event.accept()
for url in event.mimeData().urls():
if url.isLocalFile():
rootPath = url.toString()[8:]
if os.path.isfile(rootPath):
address = rootPath
item = QTreeWidgetItem()
item.setData(self, 0, 0, address)
self.addItem(item)
else:
for path, subdirs, files in os.walk(rootPath):
for file in files:
address = str(url.toLocalFile())
path = path.replace("\\", "/")
directory_name = path.replace(rootPath, "")
displayName = directory_name + '/' + file
address += displayName
item = QTreeWidgetItem()
item.setData(self, 0, 0, address)
self.addItem(item)
else:
event.ignore()
class Main(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Login Form')
self.resize(1200, 600)
self.treeBox = Tree(self)
self.btn = QPushButton('Upload', self)
self.btn.setGeometry(850, 400, 200, 50)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = Main()
w.show()
sys.exit(app.exec_())
For example if I drop a folder that contains file1, file2, subfolder, and subfolder2 the treewidget will display the root folder with each file as a child. Along with the subfolders in the root will also display their children(files inside the subfolder).
I am trying to detect internal move signal from QListWidget while I have my drag and drop effect implement. But currently with my code, because I am self-handling dragEnterEvent, dragMoveEvent, and moveEvent, the internal move signal is being ignored. Any way I can get around that issue? Below is a simple code to duplicate the issue.
You can drag external items to the list widget but you now cannot move the items around.
import sys, os
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QLineEdit, QListWidget, \
QVBoxLayout, QHBoxLayout, QAbstractItemView
from PyQt5.QtCore import Qt
class ListWidget(QListWidget):
def __init__(self, parent=None):
super().__init__(parent=None)
self.setAcceptDrops(True)
self.setStyleSheet('''font-size:25px''')
def dragEnterEvent(self, event):
# print(event.mimeData().urls())
# print(dir(event.mimeData()))
if event.mimeData().hasUrls():
event.accept()
else:
event.ignore()
def dragMoveEvent(self, event):
if event.mimeData().hasUrls():
event.setDropAction(Qt.CopyAction)
event.accept()
else:
print('y')
event.ignore()
def dropEvent(self, event):
if event.mimeData().hasUrls():
event.setDropAction(Qt.CopyAction)
event.accept()
pdfFiles = []
for url in event.mimeData().urls():
if url.isLocalFile():
pdfFiles.append(str(url.toLocalFile()))
self.addItems(pdfFiles)
else:
event.ignore()
class AppDemo(QWidget):
def __init__(self):
super().__init__()
self.resize(1200, 800)
data = ['Microsoft', 'Facebook', 'Google']
mainLayout = QHBoxLayout()
self.lst = ListWidget()
self.lst.addItems(data)
self.lst.setStyleSheet('''
font-size:30px
''')
self.lst.setDragDropMode(QAbstractItemView.InternalMove)
mainLayout.addWidget(self.lst)
self.setLayout(mainLayout)
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = AppDemo()
demo.show()
sys.exit(app.exec_())
Instead of invoking event.ignore() when the mimedata has no urls, call the parent's method so the drag & drop will be handled like the default QListWidget. Also, another way to determine the source of the drag operation is with event.source().
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.accept()
else:
super().dragEnterEvent(event)
def dragMoveEvent(self, event):
if event.mimeData().hasUrls():
event.setDropAction(Qt.CopyAction)
event.accept()
else:
super().dragMoveEvent(event)
def dropEvent(self, event):
if event.mimeData().hasUrls():
event.setDropAction(Qt.CopyAction)
event.accept()
pdfFiles = []
for url in event.mimeData().urls():
if url.isLocalFile():
pdfFiles.append(str(url.toLocalFile()))
self.addItems(pdfFiles)
else:
super().dropEvent(event)
I am using PyQt5, and I want to make a Drag&Drop system.
I got the code on this post : PyQT4: Drag and drop files into QListWidget
When I Run, I got the following error : AttributeError: 'MainForm' object has no attribute 'connect'
The code:
import sys
import os
from PyQt5.Qt import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class TestListView(QListWidget):
def __init__(self, type, parent=None):
super(TestListView, self).__init__(parent)
self.setAcceptDrops(True)
self.setIconSize(QSize(72, 72))
def dragEnterEvent(self, event):
if event.mimeData().hasUrls:
event.accept()
else:
event.ignore()
def dragMoveEvent(self, event):
if event.mimeData().hasUrls:
event.setDropAction(Qt.CopyAction)
event.accept()
else:
event.ignore()
def dropEvent(self, event):
if event.mimeData().hasUrls:
event.setDropAction(Qt.CopyAction)
event.accept()
links = []
for url in event.mimeData().urls():
links.append(str(url.toLocalFile()))
self.emit(Qt.SIGNAL("dropped"), links)
else:
event.ignore()
class MainForm(QMainWindow):
def __init__(self, parent=None):
super(MainForm, self).__init__(parent)
self.view = TestListView(self)
self.connect(self.view, Qt.SIGNAL("dropped"), self.pictureDropped)
self.setCentralWidget(self.view)
def pictureDropped(self, l):
for url in l:
if os.path.exists(url):
print(url)
icon = QIcon(url)
pixmap = icon.pixmap(72, 72)
icon = QIcon(pixmap)
item = QListWidgetItem(url, self.view)
item.setIcon(icon)
item.setStatusTip(url)
def main():
app = QApplication(sys.argv)
form = MainForm()
form.show()
app.exec_()
if __name__ == '__main__':
main()
The problem is that you are using an old form of connection, you must update the following things:
create the signal:
class TestListView(QListWidget):
dropped = pyqtSignal(list)
Also change:
self.emit(Qt.SIGNAL("dropped"), links)
to:
self.dropped.emit(links)
And change:
self.connect(self.view, Qt.SIGNAL("dropped"), self.pictureDropped)
to:
self.view.dropped.connect(self.pictureDropped)
For more information you can read here
Complete code:
import sys
import os
from PyQt5.Qt import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class TestListView(QListWidget):
dropped = pyqtSignal(list)
def __init__(self, type, parent=None):
super(TestListView, self).__init__(parent)
self.setAcceptDrops(True)
self.setIconSize(QSize(72, 72))
def dragEnterEvent(self, event):
if event.mimeData().hasUrls:
event.accept()
else:
event.ignore()
def dragMoveEvent(self, event):
if event.mimeData().hasUrls:
event.setDropAction(Qt.CopyAction)
event.accept()
else:
event.ignore()
def dropEvent(self, event):
if event.mimeData().hasUrls:
event.setDropAction(Qt.CopyAction)
event.accept()
links = []
for url in event.mimeData().urls():
links.append(str(url.toLocalFile()))
self.dropped.emit(links)
else:
event.ignore()
class MainForm(QMainWindow):
def __init__(self, parent=None):
super(MainForm, self).__init__(parent)
self.view = TestListView(self)
self.view.dropped.connect(self.pictureDropped)
self.setCentralWidget(self.view)
def pictureDropped(self, l):
for url in l:
if os.path.exists(url):
print(url)
icon = QIcon(url)
pixmap = icon.pixmap(72, 72)
icon = QIcon(pixmap)
item = QListWidgetItem(url, self.view)
item.setIcon(icon)
item.setStatusTip(url)
def main():
app = QApplication(sys.argv)
form = MainForm()
form.show()
app.exec_()
if __name__ == '__main__':
main()
This example creates a simple dialog window with two drag-and-drop enabled listWidgets (thanks ekhumoro!).
On dropEvent the droppedOnA() and droppedOnB() functions print out an information: what listWidgets the items came from and what indexes they used to have. But I need to find what indexes the dropped listItems assigned after they were dropped onto a QListWidget-receiver. I would appreciate if it would be explained how to achieve this.
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)
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)
super(ThumbListWidget, self).dropEvent(event)
self.emit(QtCore.SIGNAL("dropped"), self._drag_info )
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')
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('A'+'%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.listWidgetA, QtCore.SIGNAL("dropped"), self.droppedOnA)
self.connect(self.listWidgetB, QtCore.SIGNAL("dropped"), self.droppedOnB)
def droppedOnA(self, dropped_list):
print '\n\t dropped On MainTree', dropped_list,
def droppedOnB(self, dropped_list):
print '\n\t dropped On SecondaryTree', dropped_list
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
dialog_1 = Dialog_01()
dialog_1.show()
dialog_1.resize(720,480)
sys.exit(app.exec_())
Not tested to destruction, but try reimplementing rowsInserted and emitting the draginfo from there:
class ThumbListWidget(QtGui.QListWidget):
_drag_info = []
def __init__(self, type, name, parent=None):
...
self._dropping = False
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)
There are two QListWIdgets sitting in a same dialog window. The DragDrop functionality has been enabled for both. If I drag and drop a file to any of two ListWidges the program recognizes it and prints out the list of the files dropped. But aside from drag and dropping files I would like to be able to drag and drop the List widget Items from one to another. If I drag the ListItems the drag and drop event is triggered. But it is not able to recognize what Items were dropped onto the widget. The example code is below. The goal is to drag-drop the list items from one ListWidget to another.
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
Here is the code that does it all. But there is no way to track down what object was dropped. The droppedOnA() and droppedOnB() methods are still not working.
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
Here is another attempt this time using MIME to pass dropped Item's objects to ListWidget. Unfortunately cPickle refuses to accept binary objects throwing a
TypeError: the sip.wrapper type cannot be instantiated or sub-classed
To get around it I convert each object names to string and use it with self.listItems={} dictionary as its key to retrieve list Item's binary objects. Which seems to be working well. But at the end when I almost though it as all done, a ListWidget with no visible errors doesn't add the dropped list Item to itself... It's strange.
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_())
Here is a revised code. It is working like a charm! Bravo!
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_())
Assuming you want to move the list-widget items, your subclass should be as below (note that setDragDropMode and setSelectionMode have been moved into __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)
The above code did not work for me, when implementing the Python 3 style of super().
The issue is that the inherited methods dragMoveEvent and dragDropEvent were not being overwritten with the new definitions in the child class.
Therefore event.setDropAction(QtCore.Qt.MoveAction) was not being called, and the widget behavior defaulted to QtCore.Qt.CopyAction.
I solved this using the setDefaultDropAction() method inherited from the QAbstractItemView class.
Here is the implementation in PyQt5 and Python 3.7:
class NewDragDropWidget(QListWidget):
def __init__(self):
super().__init__()
self.setIconSize(QtCore.QSize(124, 124))
self.setDragDropMode(QAbstractItemView.DragDrop)
self.setDefaultDropAction(QtCore.Qt.MoveAction) # this was the magic line
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.setAcceptDrops(True)