Как я могу перетаскивать экземпляры дочернего класса QListWidgetItem, которые имеют дополнительные атрибуты? - PullRequest
0 голосов
/ 07 апреля 2020

Я использую PyQt5 и совершенно новый для него. Я хотел бы перетащить QListWidgetItem из одного QListWidget в другой, чтобы результирующий QListWidgetItem с одной стороны содержал дополнительные данные. Я попытался создать подкласс QListWidgetItem, но этот тип не выполняет перетаскивание, поскольку создается впечатление, что он создает новый экземпляр QListWidgetItem.

Вот код:

from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QHBoxLayout, QListWidget, QListWidgetItem, QAction, QVBoxLayout
from PyQt5 import QtCore, QtGui
import sys


class GUI(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.central_widget = MyCentralWidget(self)
        self.setCentralWidget(self.central_widget)


class MyCustomItem(QListWidgetItem):
    def __init__(self, data, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.extra_data = data


class MyCentralWidget(QWidget):
    def __init__(self, parent):
        super(MyCentralWidget, self).__init__(parent)
        self.h_layout = QHBoxLayout(self)
        self.setLayout(self.h_layout)

        self.list_left = DragAndDropList()
        self.list_left.setDragEnabled(True)
        self.list_left.setAcceptDrops(False)

        self.list_right = DragAndDropList()

        self.h_layout.addWidget(self.list_left)
        self.h_layout.addWidget(self.list_right)

        item = MyCustomItem(69, 'custom_item')
        self.list_left.insertItem(1, item)


class DragAndDropList(QListWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setIconSize(QtCore.QSize(124, 124))
        self.setDragDropMode(self.DragDrop)
        self.setSelectionMode(self.ExtendedSelection)
        self.setAcceptDrops(True)

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            event.accept()
        else:
            super().dragEnterEvent(event)

    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls():
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()
        else:
            super().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.LinkAction)
            super().dropEvent(event)

            list_items = [self.item(i) for i in range(self.count())]
            for l in list_items:
                print(l.extra_data)


def main():
    app = QApplication([])
    win = GUI()
    win.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

Здесь я выдается ошибка: «AttributeError: у объекта« QListWidgetItem »нет атрибута« extra_data »», когда я пытаюсь перетащить custom_item.

Я действительно посмотрел эту тему [ 1 ], но оно устарело и не было четких решений.

1 Ответ

0 голосов
/ 07 апреля 2020

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

Если ваши данные сериализуемы (очевидно, строки, но обычно подходит любой тип QVariant, например, QColor или QPixmap), вы можете просто использовать QListWidgetItem.setData() с пользовательской ролью, определяемой c для каждого поля данных, которое вы хотите сохранить, а затем используйте QListWidgetItem.data(), чтобы вернуть эти данные.

В этом случае я создал пользовательскую функцию, но это, очевидно, не нужно, так как вы можете просто вручную установить данные для каждого элемента до или после его вставки, если у вас есть правильный индекс строки.

MySpecialRole = QtCore.Qt.UserRole + 1

class DragAndDropList(QtWidgets.QListWidget):
    def addCustomItem(self, name, data):
        item = QtWidgets.QListWidgetItem(name)
        item.setData(MySpecialRole, data)
        self.addItem(item)

    def dropEvent(self, event):
        super().dropEvent(event)
        # let's see the data for each item, including our custom role
        for row in range(self.count()):
            item = self.item(row)
            print('Data for item {}: {}'.format(
                row + 1, item.data(MySpecialRole)))


class MyCentralWidget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(MyCentralWidget, self).__init__(parent)
        self.h_layout = QtWidgets.QHBoxLayout(self)
        self.setLayout(self.h_layout)

        self.list_left = DragAndDropList()
        self.list_left.setDragEnabled(True)

        self.list_right = DragAndDropList()
        self.list_right.setAcceptDrops(True)

        self.h_layout.addWidget(self.list_left)
        self.h_layout.addWidget(self.list_right)

        self.list_left.addCustomItem('item (using addCustomItem)', 'some data')
        self.list_left.addItem('item without custom data')
        self.list_left.addItem('this item will have data')
        self.list_left.item(2).setData(MySpecialRole, 'some custom data')

Обратите внимание, что следующая строка выдаст вам ошибку:

    self.emit(QtCore.SIGNAL("dropped"), links)

Мало того, что виджет списка не имеет атрибута emit (как у любого другого подкласса QObject), но и пользовательские сигналы должны быть объявлены в определении класса, а затем отправлены напрямую.

class DragAndDropList(QtWidgets.QListWidget):
    dropped = QtCore.pyqtSignal(object)
    def dropEvent(self, event):
        # ...
        self.dropped.emit(links)
...