Как добавить пользовательский 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 ответ
Похоже, вы хотите 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)