【问题标题】:python - drag and drop only between itemspython - 仅在项目之间拖放
【发布时间】:2022-01-12 00:34:50
【问题描述】:

我有一个启用拖放功能的 MVC QAbstractItemModel,如下所示:

class Model(QtCore.QAbstractItemModel):

    # ... other code ...

    def flags(self, index):
        flags = super(Model, self).flags(index)

        flags |= QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsDropEnabled

        return flags

这会启用拖放,但拖放行为过于宽松。在这样的树中:

- root
    - A
    - B
    - C
        - D
        - E

如果删除的索引保留其原始父级,我只想允许拖放。

在其他作品中,我可以在root 下重新排列 A、B 或 C,我可以在 C 下重新排列 D 或 E,但我无法将 A 移至 C,或将 D/E 移至 @987654327 @等

这是一个“有效的”拖放:

- root
    - B
    - C
        - D
        - E
    - A

因为 A 已移至最后一行,但 A 仍具有相同的父级,root

但这不是有效的拖放

- root
    - B
        - A
    - C
        - D
        - E

A 的父级从 root 更改为 B,这不是我想要的。

第一次尝试

我试图通过子类化QTreeView 并向其添加dragMoveEvent 来实现这种“始终保持同一个父级”逻辑,就像这样

class MyTreeSubClass(QtWidgets.QTreeView):

    # ... more code ...

    def dragMoveEvent(self, event):
        super(MyTreeSubClass, self).dragMoveEvent(event)

        widget = event.source()

        if widget != self:
            # Forbid dropping any external data to this widget
            event.ignore()

            return

        index = widget.indexAt(event.pos())

        for selected in widget.selectedIndexes():
            if index.parent() != selected.parent():
                event.ignore()

                return

        event.accept()

但我认为这不起作用,因为index = widget.indexAt(event.pos()) 返回相同的索引,无论您是直接悬停在树中的索引上还是两个索引之间。

有没有更可靠的方法来判断一个位置是否直接高于索引(如果是,请致电event.reject())?如果是这样,我也许可以用它来接受/拒绝事件。

如果有更简单的方法来实现我正在寻找的东西,我将不胜感激。

【问题讨论】:

    标签: python qt model-view-controller


    【解决方案1】:

    毕竟,我设法通过 dragMoveEvent 完成了这项工作。

    代码变成了这样:

    • 在拖动
      • 获取最近的索引
      • 如果光标位置 (event.pos()) 高于或低于此索引,则 event.accept() 删除。
      • 否则,改为event.ignore()
    from PySide2 import QtCore, QtWidgets
    
    IN_BETWEEN_POSITIONS = frozenset(
        (
            QtWidgets.QAbstractItemView.AboveItem, 
            QtWidgets.QAbstractItemView.BelowItem
        )
    )
    
    class MyTreeSubClass(QtWidgets.QTreeView):
    
        # ... more code ...
    
        def dragMoveEvent(self, event):
            widget = event.source()
    
            if widget != self:
                # Forbid dropping any external data to this widget
                event.ignore()
    
                return
            
            index = widget.indexAt(event.pos())
            position = _get_position(position, self.visualRect(index))
    
            if position in IN_BETWEEN_POSITIONS:
                super(MyTreeSubClass, self).dragMoveEvent(event)
                event.accept()
    
                return
    
            event.ignore()
    
    
    def _get_position(position, bounds):
        output = QtWidgets.QAbstractItemView.OnViewport
        margin = 2
    
        if position.y() - bounds.top() < margin:
            return QtWidgets.QAbstractItemView.AboveItem
    
        if bounds.bottom() - position.y() < margin:
            return QtWidgets.QAbstractItemView.BelowItem
    
        if bounds.contains(position, True):
            return QtWidgets.QAbstractItemView.OnItem
    
        return output
    

    我在网上搜索到_get_position的代码

    https://stackoverflow.com/a/26311179/3626104

    https://github.com/jimmykuu/PyQt-PySide-Cookbook/blob/master/tree/drop_indicator.md

    据我了解,只要位置在 QTreeView 中,widget.indexAt 总是返回正确的索引,如果位置恰好在多个索引之间,这并不理想。 _get_position 本质上澄清了返回的索引实际上是OnItem 还是其他地方。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-03
      • 1970-01-01
      • 2017-04-08
      • 1970-01-01
      • 1970-01-01
      • 2017-05-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多