【问题标题】:How to drop a custom QStandardItem into a QListView如何将自定义 QStandardItem 放入 QListView
【发布时间】:2017-01-27 18:46:53
【问题描述】:

我正在尝试使用自定义 QStandardItem 在两个 QListViews 之间进行拖放操作。 除了this document,我在网上找不到我需要的信息,这有点帮助,但现在我被困住了。

当我使用QStandardItem 来保存我的数据时,从一个QListView 拖放到另一个工作正常,但是当我使用自定义项目时我遇到了麻烦,因为接收模型/视图会创建一个QStandardItem自定义项目被丢弃。

理想情况下,我可以告诉接收模型使用我的自定义项目作为默认项目,否则就这样做,但我想这不会那么容易?! 除了在放置时创建 QStandardItem 而不是我的自定义项目之外,似乎一切都是开箱即用的,所以我希望我不必为了得到那个部分而重新发明(拖放)轮子对吧?!

如果我必须重新发明轮子并实现视图的 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_())

这个想法是解码丢弃的数据以接收原始项目,然后制作一个副本并将该副本附加到接收模型。 自定义项被附加到模型中,但在放置事件之后它不会显示在视图中。如果我在 drop even 中创建一个新的自定义项目并附加它,一切都会按预期工作。

所以我有两个关于上述问题的问题:

  1. 这种方法是允许删除自定义项的正确方法还是有更简单的方法?
  2. 为什么上述代码中自定义项的副本在放置后没有显示在视图中?

提前致谢, 坦率的

【问题讨论】:

    标签: qt pyqt pyside


    【解决方案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)
    

    【讨论】:

    • 我早些时候问过this question。我已尝试遵循您的解决方案,但是我仍然无法在移动事件中维护自定义数据。我不确定如何正确实现 setData 方法。您能否详细说明如何正确实施setData?我不确定进行此讨论的最佳格式是什么(新问题?),但这里是my code with changes
    • @Johndt6。我添加了一个示例来说明我的答案。这个想法是完全避免使用动态 python 属性。 @property 的使用只是语法糖。一个更简单的实现是坚持使用纯 Qt API:即 QStandardItem 方便方法,如 text()/setText(),加上 data()/ setData() 用于任何自定义值。除了clone() 之外,应该不需要重新实现任何东西。唯一相关的是项目标志和数据。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-30
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    相关资源
    最近更新 更多