【问题标题】:QT, Python, QTreeView, custom widget, setData - lose reference after drag and dropQT、Python、QTreeView、自定义小部件、setData - 拖放后失去参考
【发布时间】:2016-11-06 17:38:47
【问题描述】:

这个问题与本主题 Preserve QStandardItem subclasses in drag and drop 中的问题类似,但我无法找到一个好的解决方案。该主题对更复杂的任务有部分帮助,但失败了。

当我在 QTreeView 中创建一个项目时,我将该项目放入我的数组中,但是当我使用拖放操作时,该项目被删除并且我不再可以访问它。我知道这是因为拖放复制项目而不是移动它,所以我应该使用 setData。我不能将 setData 设置为一个对象,因为即使这样,该对象也会被复制并且我失去了对它的引用。

这是一个例子

itemsArray = self.addNewRow
def addNewRow(self)
    '''some code with more items'''
    itemHolder = QStandardItem("ProgressBarItem")
    widget = QProgressBar()
    itemHolder.setData(widget)

    inx = self.model.rowCount()
    self.model.setItem(inx, 0, itemIcon)
    self.model.setItem(inx, 1, itemName)
    self.model.setItem(inx, 2, itemHolder)
    ix = self.model.index(inx,2,QModelIndex())
    self.treeView.setIndexWidget(ix, widget)
    return [itemHolder, itemA, itemB, itemC]

#Simplified functionality
data = [xxx,xxx,xxx]
for items in itemsArray:
     items[0].data().setPercentage(data[0])
     items[1].data().setText(data[1])
     items[2].data().setChecked(data[2])

如果我不移动小部件,上面的代码就可以工作。第二次我拖放我失去参考我失去了我所有项目的更新,我得到了崩溃。

RuntimeError: wrapped C/C++ object of type QProgressBar has been deleted

我能想到的解决此问题的方法是在每个行/子项上递归遍历整个树视图,并在名称匹配更新项上循环......问题是我将每 0.5 秒刷新一次树视图,并且有 500 多个每行有 5-15 个项目。意思...我认为这不会非常快速/高效...如果我想每 0.5 秒循环超过 5 000 个项目...

有人能建议我如何解决这个问题吗?也许我可以编辑 dropEvent 所以它不会复制/粘贴项目而是移动项目....这样我就不会丢失数组中的对象

【问题讨论】:

    标签: python qt qtreeview qstandarditemmodel qstandarditem


    【解决方案1】:

    Qt 只能序列化可以存储在QVariant 中的对象,因此这不适用于QWidget 也就不足为奇了。但是即使它可以序列化小部件,我仍然认为它不会起作用,因为索引小部件属于视图,而不是模型。

    无论如何,我认为您必须单独保留对小部件的引用,并且只在模型项中存储一个简单的键。然后,一旦项目被删除,您可以检索小部件并在视图中重置它们。

    这是一个有效的演示脚本:

    from PyQt4 import QtCore, QtGui
    
    class TreeView(QtGui.QTreeView):
        def __init__(self, *args, **kwargs):
            super(TreeView, self).__init__(*args, **kwargs)
            self.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
            self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
            self.setAllColumnsShowFocus(True)
            self.setModel(QtGui.QStandardItemModel(self))
            self._widgets = {}
            self._dropping = False
            self._droprange = range(0)
    
        def dropEvent(self, event):
            self._dropping = True
            super(TreeView, self).dropEvent(event)
            for row in self._droprange:
                item = self.model().item(row, 2)
                self.setIndexWidget(item.index(), self._widgets[item.data()])
                self._droprange = range(0)
            self._dropping = False
    
        def rowsInserted(self, parent, start, end):
            super(TreeView, self).rowsInserted(parent, start, end)
            if self._dropping:
                self._droprange = range(start, end + 1)
    
        def addNewRow(self, name):
            model = self.model()
            itemIcon = QtGui.QStandardItem()
            pixmap = QtGui.QPixmap(16, 16)
            pixmap.fill(QtGui.QColor(name))
            itemIcon.setIcon(QtGui.QIcon(pixmap))
            itemName = QtGui.QStandardItem(name.title())
            itemHolder = QtGui.QStandardItem('ProgressBarItem')
            widget = QtGui.QProgressBar()
            widget.setValue(5 * (model.rowCount() + 1))
            key = id(widget)
            self._widgets[key] = widget
            itemHolder.setData(key)
            model.appendRow([itemIcon, itemName, itemHolder])
            self.setIndexWidget(model.indexFromItem(itemHolder), widget)
    
    class Window(QtGui.QWidget):
        def __init__(self):
            super(Window, self).__init__()
            self.treeView = TreeView()
            for name in 'red yellow green purple blue orange'.split():
                self.treeView.addNewRow(name)
            layout = QtGui.QVBoxLayout(self)
            layout.addWidget(self.treeView)
    
    if __name__ == '__main__':
    
        import sys
        app = QtGui.QApplication(sys.argv)
        window = Window()
        window.setGeometry(500, 150, 600, 400)
        window.show()
        sys.exit(app.exec_())
    

    【讨论】:

    • 我在使用字典和 ID 的作品中有类似的东西,但正如我在拖放时了解到的那样,因为当 QT Copy/past 也删除了该项目时,setIndexWidget 将数组中的对象也放在树视图中。也会导致错误 - 除非我犯了其他错误。在任何情况下,我想要做的是在dropEvent() super(treeView, self).dropEvent(event) self.treeView.reInitializeView() 之后,所以我会循环遍历每个小部件,并为它们重新附加一个新的进度条。它适用于 90% 的问题,但它也可以复制项目 :- (,drop 事件中的最后一个函数是什么?
    • @Dariusz。这一切对我来说都很好。我在答案中添加了一个完整的演示脚本。
    • 它半有效,一旦将 0 项拖到另一个 0 项,子项将具有 ProgressBarItem 文本而不是进度。我最终做的是在进度条中指定一个函数,该函数在数组中查找数据的特定位置。它在我移动项目等时起作用,因为它始终保留数组中数据位置的 ID。我总是在 dragEvent 上调用更新函数来重新附加这些进度条。附带说明一下,如果我将项目 1-2-3(0 以外)拖到另一个项目的顶部。项目行消失了,也许你知道如何限制它? youtu.be/L6qLuC2dmhE例子
    • @Dariusz。我假设您只是更改了行的顶级顺序,因此我的示例不会有太大用处。进一步调查后,默认的拖放行为似乎非常奇怪且非常不直观 - 几乎到了错误的地步。目前看来,正确解决问题的唯一方法可能是重新实现dropEvent - 但这将是很多工作。不过,我会继续寻找,并尝试提出一个更简单的解决方案。
    • @Dariusz。我现在尝试使用QTreeViewQTreeWidget 进行拖放,并确认默认行为非常错误。我尝试了所有设置组合,但找不到一个正常工作的组合。
    猜你喜欢
    • 2014-05-06
    • 1970-01-01
    • 2013-12-25
    • 2011-10-15
    • 1970-01-01
    • 2012-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多