PyQt5 drag and drop widget при переносе виджета в QlistWidget копируется пустой объект
Пытаюсь скопировать динамически созданный widget с помощью drag & drop и не получаю нужный результат, возможно чего-то не учел в функции def dropEvent(self, e), но не могу понять что.
from PyQt5.QtWidgets import QApplication, QWidget, QListWidget, QHBoxLayout,QListWidgetItem,QPushButton,QAbstractItemView
from PyQt5.QtGui import QIcon,QDragEnterEvent,QDropEvent
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class TaskList(QListWidget):
def __init__(self):
super(TaskList, self).__init__()
self.setAcceptDrops(True)
self.setDragEnabled(True)
self.setWordWrap(True)
self.setSortingEnabled(True)
def dragEnterEvent(self, e):
e.accept()
def dragMoveEvent(self, e):
e.accept()
def dropEvent(self, e):
print("========DropEvent--")
widget = e.source()
e.acceptProposedAction()
items=widget.selectedItems()
for i in items:
widget.takeItem(widget.indexFromItem(i).row())
dd = widget.takeItem(widget.indexFromItem(i).row())
self.addItem(i)
class Window(QWidget):
def __init__(self):
super().__init__()
self.setAcceptDrops(True)
self.myListWidget1 = TaskList()
self.myListWidget2 = TaskList()
self.myListWidget3 = TaskList()
self.setGeometry(300, 350, 500, 300)
self.myLayout = QHBoxLayout()
self.myLayout.addWidget(self.myListWidget1)
self.myLayout.addWidget(self.myListWidget2)
self.myLayout.addWidget(self.myListWidget3)
itemN = QListWidgetItem()
widget = QtWidgets.QWidget()
widgetText = QtWidgets.QLabel("I love PyQt!")
widgetText.setAcceptDrops(True)
widgetButton = QtWidgets.QPushButton("Push Me")
widgetButton.setAcceptDrops(True)
widgetLayout = QtWidgets.QVBoxLayout()
widgetLayout.addWidget(widgetText)
widgetLayout.addWidget(widgetButton)
widgetLayout.addStretch()
widgetLayout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize)
widget.setLayout(widgetLayout)
itemN.setSizeHint(widget.sizeHint())
self.myListWidget1.addItem(itemN)
self.myListWidget1.setItemWidget(itemN, widget)
self.setWindowTitle('Drag and Drop Example');
self.setLayout(self.myLayout)
self.show()
App = QApplication(sys.argv)
window = Window()
sys.exit(App.exec())
Ответы (2 шт):
Автор решения: gil9red
→ Ссылка
У вас элемент списка пустой т.к. не имеет ничего. Его содержимым является виджет, который вы подставляете через setItemWidget.
Изначально попробовал брать виджет из элемента списка и вставлять его с перенесенным элементом, но это вызывало ошибку внутри Qt и падение.
Проблему получилось обойти путем создания нового виджета элемента списка при переносе элемента списка
Шаги:
- Создал класс для виджета элемента списка
ItemWidget - Используем его при первичном наполнении списка
- При переносе элемента вытаскиваем предыдущий виджет, создаем новый и вставляем его (иначе, происходила ошибка внутри
Qt, которую не отловить)
Пример:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QListWidget, QHBoxLayout, QListWidgetItem
from PyQt5 import QtCore, QtGui, QtWidgets
class ItemWidget(QWidget):
def __init__(self):
super().__init__()
widgetText = QtWidgets.QLabel("I love PyQt!")
widgetButton = QtWidgets.QPushButton("Push Me")
widgetLayout = QtWidgets.QVBoxLayout()
widgetLayout.addWidget(widgetText)
widgetLayout.addWidget(widgetButton)
widgetLayout.addStretch()
widgetLayout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize)
self.setLayout(widgetLayout)
def clone(self, other: 'ItemWidget'):
# NOTE: Можно тут реализовать перенос данных
pass
class TaskList(QListWidget):
def __init__(self):
super(TaskList, self).__init__()
self.setAcceptDrops(True)
self.setDragEnabled(True)
self.setWordWrap(True)
self.setSortingEnabled(True)
def dragEnterEvent(self, e):
e.accept()
def dragMoveEvent(self, e):
e.accept()
def dropEvent(self, e):
print("========DropEvent--")
e.acceptProposedAction()
other_list = e.source()
for item in other_list.selectedItems():
item_widget = other_list.itemWidget(item)
other_list.removeItemWidget(item)
other_list.takeItem(other_list.indexFromItem(item).row())
self.addItem(item)
new_item_widget = ItemWidget()
new_item_widget.clone(item_widget)
self.setItemWidget(item, new_item_widget)
class Window(QWidget):
def __init__(self):
super().__init__()
self.setAcceptDrops(True)
self.myListWidget1 = TaskList()
self.myListWidget2 = TaskList()
self.myListWidget3 = TaskList()
self.setGeometry(300, 350, 500, 300)
self.myLayout = QHBoxLayout()
self.myLayout.addWidget(self.myListWidget1)
self.myLayout.addWidget(self.myListWidget2)
self.myLayout.addWidget(self.myListWidget3)
itemN = QListWidgetItem()
widget = ItemWidget()
itemN.setSizeHint(widget.sizeHint())
self.myListWidget1.addItem(itemN)
self.myListWidget1.setItemWidget(itemN, widget)
self.setWindowTitle('Drag and Drop Example')
self.setLayout(self.myLayout)
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec())
Автор решения: JackMas
→ Ссылка
Спасибо за ответ, то что и хотел получить. Здесь немного видоизмененный и рабочий код: '''
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QListWidget, QHBoxLayout, QListWidgetItem
from PyQt5 import QtCore, QtGui, QtWidgets
class ItemWidget(QWidget):
def __init__(self):
super().__init__()
# self.taskNumber = QtWidgets.QLabel("I love PyQt!")
self.taskNumber = QtWidgets.QSpinBox()
self.taskMessage = QtWidgets.QLabel()
# self.taskMessage.setText("New Task");
#widgetButton = QtWidgets.QPushButton("Push Me")
widgetLayout = QtWidgets.QVBoxLayout()
widgetLayout.addWidget(self.taskNumber)
widgetLayout.addWidget(self.taskMessage)
widgetLayout.addStretch()
widgetLayout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize)
self.setLayout(widgetLayout)
def clone(self, other: 'ItemWidget'):
self.taskMessage.setText(other.taskMessage.text());
self.taskNumber.setValue(other.taskNumber.value());
class TaskList(QListWidget):
def __init__(self):
super(TaskList, self).__init__()
self.setAcceptDrops(True)
self.setDragEnabled(True)
self.setWordWrap(True)
self.setSortingEnabled(True)
def dragEnterEvent(self, e):
e.accept()
def dragMoveEvent(self, e):
e.accept()
def dropEvent(self, e):
print("DropEvent--")
e.acceptProposedAction()
other_list = e.source()
for item in other_list.selectedItems():
item_widget = other_list.itemWidget(item)
other_list.removeItemWidget(item)
other_list.takeItem(other_list.indexFromItem(item).row())
self.addItem(item)
new_item_widget = ItemWidget()
new_item_widget.clone(item_widget)
self.setItemWidget(item, new_item_widget)
class Window(QWidget):
def __init__(self):
super().__init__()
self.setAcceptDrops(True)
self.myListWidget1 = TaskList()
self.myListWidget2 = TaskList()
self.myListWidget3 = TaskList()
self.setGeometry(300, 350, 500, 300)
self.myLayout = QHBoxLayout()
self.myLayout.addWidget(self.myListWidget1)
self.myLayout.addWidget(self.myListWidget2)
self.myLayout.addWidget(self.myListWidget3)
itemN = QListWidgetItem()
widget = ItemWidget()
widget.taskNumber.setValue(98)
widget.taskMessage.setText("New Task 98");
itemN.setSizeHint(widget.sizeHint())
self.myListWidget1.addItem(itemN)
self.myListWidget1.setItemWidget(itemN, widget)
itemN2 = QListWidgetItem()
widget2 = ItemWidget()
widget2.taskNumber.setValue(90)
widget2.taskMessage.setText("New Task 90");
itemN2.setSizeHint(widget2.sizeHint())
self.myListWidget1.addItem(itemN2)
self.myListWidget1.setItemWidget(itemN2, widget2)
self.setWindowTitle('Drag and Drop Example')
self.setLayout(self.myLayout)
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec())
'''