Как добавить пользовательский QStandardItem в QListView

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

Перетаскивание из одного QListView в другое прекрасно работает, когда я использую QStandardItem для хранения своих данных, но когда я использую пользовательский элемент, у меня возникают проблемы, потому что принимающая модель / представление создает QStandardItem, когда пользовательский элемент отбрасывается.

В идеале я мог бы сказать принимающей модели использовать свой пользовательский элемент в качестве элемента по умолчанию, а в остальном просто сделать это, но я полагаю, это будет не так просто?! Кажется, что все работает из коробки, кроме создания QStandardItem on drop, а не моего пользовательского элемента, поэтому я надеюсь, что мне не придется заново изобретать колесо (перетаскивание) только для того, чтобы получить правильную часть?!

Если мне придется заново изобрести колесо и реализовать представление dropEvent, чтобы затем вручную добавить входящие элементы, я столкнусь с другой странностью. Вот мой тестовый код (включая некоторый код для декодирования удаленных данных, которые я нашел в Интернете):

from PySide import QtCore, QtGui

class MyItem(QtGui.QStandardItem):
    '''This is the item I'd like to drop into the view'''

    def __init__(self, parent=None):
        super(MyItem, self).__init__(parent)
        self.testAttr = 'test attribute value'

class ReceivingView(QtGui.QListView):
    '''Custom view to show the problem - i.e. the dropEvent produces a QStandardItem rather than MyItem'''

    def __init__(self, parent=None):
        super(ReceivingView, self).__init__(parent)

    def decode_data(self, bytearray):
        '''Decode byte array to receive item back'''
        data = []
        item = {}

        ds = QtCore.QDataStream(bytearray)
        while not ds.atEnd():

            row = ds.readInt32()
            column = ds.readInt32()

            map_items = ds.readInt32()
            for i in range(map_items):

                key = ds.readInt32()

                value = MyItem()
                ds >> value
                #item[QtCore.Qt.ItemDataRole(key)] = value
                item = value

            data.append(item)

        return data

    def dropEvent(self, event):    
        byteArray = event.mimeData().data('application/x-qabstractitemmodeldatalist')
        for item in self.decode_data(byteArray):
            copiedItem = MyItem(item)
            newItem = MyItem('hello')
            print copiedItem
            print newItem
            self.model().appendRow(copiedItem) # the copied item does not show up, even though it is appended to the model
            #self.model().appendRow(newItem) # this works as expected

        event.accept()

        item = self.model().item(self.model().rowCount() - 1)
        print item

if __name__ == "__main__":
    import sys

    app = QtGui.QApplication(sys.argv)

    mw = QtGui.QMainWindow()
    w = QtGui.QSplitter()
    mw.setCentralWidget(w)

    # models
    model1 = QtGui.QStandardItemModel()
    model2 = QtGui.QStandardItemModel()

    for i in xrange(5):
        #item = QtGui.QStandardItem()
        item = MyItem()
        item.setData(str(i), QtCore.Qt.DisplayRole)
        model1.appendRow(item)

    # views
    view1 = QtGui.QListView()
    view2 = ReceivingView()
    for v in (view1, view2):
        v.setViewMode(QtGui.QListView.IconMode)

    view1.setModel(model1)
    view2.setModel(model2) 

    w.addWidget(view1)
    w.addWidget(view2)

    mw.show()
    mw.raise_()
    sys.exit(app.exec_())

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

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

  1. Правильный ли это подход, позволяющий сбросить пользовательские предметы или есть более простой?
  2. Почему копия пользовательского элемента в приведенном выше коде не отображается в представлении после удаления?

Заранее спасибо

1 ответ

Решение

Похоже, вы хотите setItemPrototype. Это обеспечивает фабрику элементов для модели, так что она будет неявно использовать ваш пользовательский класс при необходимости.

Все, что вам нужно сделать, это переопределить clone() в вашем классе предметов:

class MyItem(QtGui.QStandardItem):
    '''This is the item I'd like to drop into the view'''

    def __init__(self, parent=None):
        super(MyItem, self).__init__(parent)
        self.testAttr = 'test attribute value'

    def clone(self):
        return MyItem()

Затем установите экземпляр этого класса в качестве прототипа на принимающей модели:

    # models
    model1 = QtGui.QStandardItemModel()
    model2 = QtGui.QStandardItemModel()
    model2.setItemPrototype(MyItem())

Вы можете забыть обо всем, что касается потока данных.

PS:

Я полагаю, я должен указать, что Qt, очевидно, ничего не знает ни о каких атрибутах данных python, которые могли быть установлены во время жизни элемента, и поэтому они не будут сериализованы при передаче элемента во время операции перетаскивания. Если вы хотите сохранить такие данные, используйте setData() с пользовательской ролью:

class MyItem(QtGui.QStandardItem):
    _TestAttrRole = QtCore.Qt.UserRole + 2

    def clone(self):
        item = MyItem()
        item.testArr = 'test attribute value'
        return item

    @property
    def testAttr(self):
        return self.data(self._TestAttrRole)

    @testAttr.setter
    def testAttr(self, value):
        self.setData(value, self._TestAttrRole)
Другие вопросы по тегам