首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >PyQt5: QTreeWidget中项目对等点之间的拖放

PyQt5: QTreeWidget中项目对等点之间的拖放
EN

Stack Overflow用户
提问于 2022-07-14 09:58:00
回答 1查看 165关注 0票数 1
  1. 可以拖动和阻止同一级别的节点彼此进入,而不是其他父节点。

我在QTreeWidget中覆盖了dragMoveEventdropEvent方法,我只有正确的second layer效果,它可以互相拖动,防止进入对方。但是first and third levels有不同的错误。例如,节点1-1-1和节点1-2-2不能相互拖动。Node1可以拖动到Node2中,这不符合我完成的要求。

代码语言:javascript
运行
复制
class TreeWidget(QTreeWidget):
def __init__(self):
    super().__init__()
    self.setDragDropMode(QTreeWidget.InternalMove)

def dragMoveEvent(self, event):
    current_item = self.currentItem()
    item = self.itemAt(event.pos())

    if current_item and current_item.type() == 0:
        super().dragMoveEvent(event)
    elif item and item.type() == 1 and current_item.parent() == item.parent():
        super().dragMoveEvent(event)
    elif item and item.type() == 2 and current_item.parent() == item.parent():
        super().dragMoveEvent(event)
    else:
        event.ignore()

def dropEvent(self, event):
    current_item = self.currentItem()
    item = self.itemAt(event.pos())
    if current_item and current_item.type() == 0:
        super(TreeWidget, self).dropEvent(event)
    elif item and item.type() == 1 and current_item.parent() == item.parent():
        super(TreeWidget, self).dropEvent(event)
    elif item and item.type() == 2 and current_item.parent() == item.parent():
        super(TreeWidget, self).dropEvent(event)
    else:
        event.ignore()


class BasicTreeTest1(QMainWindow):
  def __init__(self):
    super().__init__()
    self.setWindowTitle('QTreeWidget')
    self.tree = TreeWidget()

    root1 = QTreeWidgetItem(self.tree,type=0)
    root1.setText(0, 'node1')

    child1_1 = QTreeWidgetItem(root1,type=1)
    child1_1.setText(0, 'node1-1')
    child1_1.setFlags(child1_1.flags() & ~Qt.ItemIsDropEnabled)

    child1_2 = QTreeWidgetItem(root1, type=1)
    child1_2.setText(0, 'node1-2')
    child1_2.setFlags(child1_2.flags() & ~Qt.ItemIsDropEnabled)

    child1_1_1 = QTreeWidgetItem(child1_1, type=2)
    child1_1_1.setText(0, 'node1-1-1')
    child1_1_1.setFlags(child1_1_1.flags() & ~Qt.ItemIsDropEnabled)

    child1_2_1 = QTreeWidgetItem(child1_1, type=2)
    child1_2_1.setText(0, 'node1-2-1')
    child1_2_1.setFlags(child1_2_1.flags() & ~Qt.ItemIsDropEnabled)


    root2 = QTreeWidgetItem(self.tree,type=0)
    root2.setText(0, 'node2')


    child2_1 = QTreeWidgetItem(root2, type=1)
    child2_1.setText(0, 'node2-1')
    child2_1.setFlags(child2_1.flags() & ~Qt.ItemIsDropEnabled)

    child2_2 = QTreeWidgetItem(root2, type=1)
    child2_2.setText(0, 'node2-2')
    child2_2.setFlags(child2_2.flags() & ~Qt.ItemIsDropEnabled)

    root3 = QTreeWidgetItem(self.tree, type=0)
    root3.setText(0, 'node3')

    child3_1 = QTreeWidgetItem(root3, type=1)
    child3_1.setText(0, 'node3_1')
    child3_1.setFlags(child3_1.flags() & ~Qt.ItemIsDropEnabled)

    child3_2 = QTreeWidgetItem(root3, type=1)
    child3_2.setText(0, 'node3_2')
    child3_2.setFlags(child3_2.flags() & ~Qt.ItemIsDropEnabled)

    child3_2_1 = QTreeWidgetItem(child3_2, type=2)
    child3_2_1.setText(0, 'node3-2-1')
    child3_2_1.setFlags(child3_2_1.flags() & ~Qt.ItemIsDropEnabled)

    child3_2_2 = QTreeWidgetItem(child3_2, type=2)
    child3_2_2.setText(0, 'node3-2-2')
    child3_2_2.setFlags(child3_2_2.flags() & ~Qt.ItemIsDropEnabled)
    # root1.setFlags(root1.flags() & ~Qt.ItemIsDropEnabled)
    # root2.setFlags(root2.flags() & ~Qt.ItemIsDropEnabled)
    # root3.setFlags(root3.flags() & ~Qt.ItemIsDropEnabled)
    # child22.setFlags(child22.flags() & ~Qt.ItemIsDropEnabled)
    self.setCentralWidget(self.tree)
    self.tree.expandAll()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = BasicTreeTest1()
    w.show()
    sys.exit(app.exec_())

这个网站是我的参考:id/文章/详细信息/125258736

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-07-17 12:19:41

更新

由于您的最新编辑更改了需求,因此需要一种不同的方法。目前,我唯一能看到的解决方案是得到当前的跌落指示器,这样就有可能知道目标是否在某一项之上或下方。Qt为此使用了一个私有方法,必须将其移植到PyQt以访问相同的功能。我已经在下面的演示脚本中实现了这一点,它只允许在自己的父级内重新排序兄弟项目(在任何级别):

代码语言:javascript
运行
复制
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

class TreeWidget(QTreeWidget):
    def __init__(self):
        super().__init__()
        self.setDragDropMode(QTreeWidget.InternalMove)

    def dragMoveEvent(self, event):
        if self.canDrop(event):
            super().dragMoveEvent(event)
        else:
            event.ignore()

    def dropEvent(self, event):
        if self.canDrop(event):
            super().dropEvent(event)
        else:
            event.ignore()

    def canDrop(self, event):
        target = self.itemAt(event.pos())
        current = self.currentItem()
        if target is not None and target.parent() is current.parent():
            index = self.indexFromItem(target)
            indicator = self.dragIndicator(
                event.pos(), self.visualItemRect(target), index)
            return (indicator == QAbstractItemView.AboveItem or
                    indicator == QAbstractItemView.BelowItem)
        return False

    def dragIndicator(self, pos, rect, index):
        indicator = QAbstractItemView.OnViewport
        if not self.dragDropOverwriteMode():
            margin = int(max(2, min(rect.height() / 5.5, 12)))
            if pos.y() - rect.top() < margin:
                indicator = QAbstractItemView.AboveItem
            elif rect.bottom() - pos.y() < margin:
                indicator = QAbstractItemView.BelowItem
            elif rect.contains(pos, True):
                indicator = QAbstractItemView.OnItem
        else:
            touching = rect.adjust(-1, -1, 1, 1)
            if touching.contains(pos, False):
                indicator = QAbstractItemView.OnItem
        if (indicator == QAbstractItemView.OnItem and
            not self.model().flags(index) & Qt.ItemIsDropEnabled):
            if pos.y() < rect.center().y():
                indicator = QAbstractItemView.AboveItem
            else:
                indicator = QAbstractItemView.BelowItem
        return indicator

class BasicTreeTest1(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('QTreeWidget')
        self.tree = TreeWidget()
        root1 = QTreeWidgetItem(self.tree,type=0)
        root1.setText(0, 'node1')
        child1_1 = QTreeWidgetItem(root1,type=1)
        child1_1.setText(0, 'node1-1')
        child1_2 = QTreeWidgetItem(root1, type=1)
        child1_2.setText(0, 'node1-2')
        child1_1_1 = QTreeWidgetItem(child1_1, type=2)
        child1_1_1.setText(0, 'node1-1-1')
        child1_2_1 = QTreeWidgetItem(child1_1, type=2)
        child1_2_1.setText(0, 'node1-2-1')
        root2 = QTreeWidgetItem(self.tree,type=0)
        root2.setText(0, 'node2')
        child2_1 = QTreeWidgetItem(root2, type=1)
        child2_1.setText(0, 'node2-1')
        child2_2 = QTreeWidgetItem(root2, type=1)
        child2_2.setText(0, 'node2-2')
        root3 = QTreeWidgetItem(self.tree, type=0)
        root3.setText(0, 'node3')
        child3_1 = QTreeWidgetItem(root3, type=1)
        child3_1.setText(0, 'node3_1')
        child3_2 = QTreeWidgetItem(root3, type=1)
        child3_2.setText(0, 'node3_2')
        child3_2_1 = QTreeWidgetItem(child3_2, type=2)
        child3_2_1.setText(0, 'node3-2-1')
        child3_2_2 = QTreeWidgetItem(child3_2, type=2)
        child3_2_2.setText(0, 'node3-2-2')
        self.setCentralWidget(self.tree)
        self.tree.expandAll()

if __name__ == '__main__':

    app = QApplication(sys.argv)
    w = BasicTreeTest1()
    w.setGeometry(600, 100, 500, 400)
    w.show()
    sys.exit(app.exec_())

前解

若要防止子项目彼此掉落,您可以使用更改其项目标志。默认标志包括Qt.ItemIsDropEnabled,因此只需删除:

代码语言:javascript
运行
复制
child111 = QTreeWidgetItem(root1, type=1)
child111.setFlags(child111.flags() & ~Qt.ItemIsDropEnabled)

当然,这不会阻止父母之间拖放项目,但是通过重新实现dragMoveEventdropEvent,您的当前代码已经阻止了这一点,因此看起来上面的更改就足够了。

下面是一个基于代码的完整的工作示例:

代码语言:javascript
运行
复制
from PyQt5 import QtCore, QtWidgets

class TreeWidget(QtWidgets.QTreeWidget):
    def __init__(self):
        super().__init__()
        self.setDragDropMode(QtWidgets.QTreeWidget.InternalMove)

    def dragMoveEvent(self, event):
        current_item = self.currentItem()
        item = self.itemAt(event.pos())

        if item and item.type() == 1 and current_item.parent() == item.parent():
            super().dragMoveEvent(event)
        else:
            event.ignore()

    def dropEvent(self, event):
        current_item = self.currentItem()
        item = self.itemAt(event.pos())
        if item and item.type() == 1 and current_item.parent() == item.parent():
            super(TreeWidget, self).dropEvent(event)
        else:
            event.ignore()

class Window(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.tree = TreeWidget()
        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.tree)

        root1 = QtWidgets.QTreeWidgetItem(self.tree,type=0)
        root1.setText(0, '1')

        child111 = QtWidgets.QTreeWidgetItem(root1,type=1)
        child111.setFlags(child111.flags() & ~QtCore.Qt.ItemIsDropEnabled)
        child111.setText(0, '11')

        child12 = QtWidgets.QTreeWidgetItem(root1, type=1)
        child12.setFlags(child12.flags() & ~QtCore.Qt.ItemIsDropEnabled)
        child12.setText(0, '12')

        root2 = QtWidgets.QTreeWidgetItem(self.tree,type=0)
        root2.setText(0, '2')

        child121 = QtWidgets.QTreeWidgetItem(root2, type=1)
        child121.setFlags(child121.flags() & ~QtCore.Qt.ItemIsDropEnabled)
        child121.setText(0, '21')

        child122 = QtWidgets.QTreeWidgetItem(root2, type=1)
        child122.setFlags(child122.flags() & ~QtCore.Qt.ItemIsDropEnabled)
        child122.setText(0, '22')

        self.tree.expandAll()

if __name__ == '__main__':

    app = QtWidgets.QApplication(['Test'])
    window = Window()
    window.setWindowTitle('Test')
    window.setGeometry(600, 100, 300, 200)
    window.show()
    app.exec_()
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72978651

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档