PyQt5 drag and drop widget при переносе виджета в QlistWidget копируется пустой объект

Рейтинг: 1Ответов: 2Опубликовано: 13.03.2023

Пытаюсь скопировать динамически созданный 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())

Ответы

▲ 0Принят

У вас элемент списка пустой т.к. не имеет ничего. Его содержимым является виджет, который вы подставляете через setItemWidget.

Изначально попробовал брать виджет из элемента списка и вставлять его с перенесенным элементом, но это вызывало ошибку внутри Qt и падение.

Проблему получилось обойти путем создания нового виджета элемента списка при переносе элемента списка

Шаги:

  1. Создал класс для виджета элемента списка ItemWidget
  2. Используем его при первичном наполнении списка
  3. При переносе элемента вытаскиваем предыдущий виджет, создаем новый и вставляем его (иначе, происходила ошибка внутри 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())
▲ 0

Спасибо за ответ, то что и хотел получить. Здесь немного видоизмененный и рабочий код: '''

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

'''