You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在QTreeView的dropEvent事件中为拖拽项设置对应图标?

解决QTreeView拖拽项后设置图标及重复项检测问题

我来帮你搞定这个需求——把Master Data下的用户项拖拽到Security Model的对应层级后,自动设置正确的用户图标,同时避免重复添加相同项。咱们直接调整你的代码,核心修改集中在dropEvent方法里,同时优化一些逻辑细节:

修改后的完整代码

#!/usr/bin/env python3
# coding = utf-8
from PyQt5 import QtWidgets, QtCore, QtGui

TXT_CLIENT = "Clients"
TXT_STAKEHLD = "Stakeholders"
TXT_USER = "Users"
TXT_SYSTEM = "Master Data"
TXT_SECURITY = "Security Model"

CLS_LVL_ROOT = 0
CLS_LVL_CLIENT = 1
CLS_LVL_STAKEHLD = 2
CLS_LVL_USER = 3

ICON_LVL_CLIENT = "img/icons8-bank-16.png"
ICON_LVL_STAKEHLD = "img/icons8-initiate-money-transfer-24.png"
ICON_LVL_USER = "img/icons8-checked-user-male-32.png"

DATA = [
    (TXT_SYSTEM, [
        (TXT_USER, [
            ("user_a", []),
            ("user_b", [])
        ]),
        (TXT_CLIENT, [
            ("client_a", []),
            ("client_b", []),
            ("client_c", []),
            ("client_d", [])
        ]),
        (TXT_STAKEHLD, [
            ("stakeholder_a", []),
            ("stakeholder_b", []),
            ("stakeholder_c", []),
            ("stakeholder_d", [])
        ])
    ]),
    (TXT_SECURITY, [
        ("client_a", [
            ("stakeholder_b",[ ("user_a",[]) ])
        ]),
        ("client_c", [
            ("stakeholder_d",[ ("user_b",[]) ])
        ])
    ])
]

def create_tree_data(tree):
    model = QtGui.QStandardItemModel()
    addItems(tree, model, DATA)
    tree.setModel(model)

def addItems(tree, parent, elements, level=0, root=0):
    level += 1
    for text, children in elements:
        if text == TXT_SYSTEM:
            root = 1
        elif text == TXT_SECURITY:
            root = 2
        item = QtGui.QStandardItem(text)
        # 初始化默认图标(这里可以根据需要调整)
        icon = QtGui.QIcon()
        item.setIcon(icon)
        parent.appendRow(item)
        if root==1:
            if children:
                if text == TXT_CLIENT:
                    icon = QtGui.QIcon(ICON_LVL_CLIENT)
                    item.setIcon(icon)
                elif text == TXT_STAKEHLD:
                    icon = QtGui.QIcon(ICON_LVL_STAKEHLD)
                    item.setIcon(icon)
                elif text == TXT_USER:
                    icon = QtGui.QIcon(ICON_LVL_USER)
                    item.setIcon(icon)
        elif root == 2:
            if level == 2:
                icon = QtGui.QIcon(ICON_LVL_CLIENT)
                item.setIcon(icon)
            if level == 3:
                icon = QtGui.QIcon(ICON_LVL_STAKEHLD)
                item.setIcon(icon)
            elif level == 4:
                icon = QtGui.QIcon(ICON_LVL_USER)
                item.setIcon(icon)
        addItems(tree, item, children, level, root)

def get_tree_selection_level(index):
    level = 0
    while index.parent().isValid():
        index = index.parent()
        level += 1
    return level

class TreeView(QtWidgets.QTreeView):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.initUI()

    def initUI(self):
        self.setHeaderHidden(True)
        self.setColumnHidden(1, True)
        self.setSelectionMode(self.SingleSelection)
        self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop) # InternalMove)

    def dropEvent(self, event):
        # 获取拖拽的源项
        selected_indexes = self.selectedIndexes()
        if not selected_indexes:
            super().dropEvent(event)
            return
        
        source_index = selected_indexes[0]
        source_item = self.model().itemFromIndex(source_index)
        source_text = source_item.text()
        
        # 获取目标父节点:如果拖拽到项上,就用该节点的父节点;如果拖拽到空白区域,用当前节点
        target_index = self.indexAt(event.pos())
        if target_index.isValid():
            # 判断目标节点的层级,如果是用户层级(CLS_LVL_USER),则父节点是stakeholder
            target_level = get_tree_selection_level(target_index)
            if target_level == CLS_LVL_USER:
                target_parent_item = self.model().itemFromIndex(target_index.parent())
            else:
                target_parent_item = self.model().itemFromIndex(target_index)
        else:
            # 拖拽到根节点区域,直接返回(不符合需求场景)
            super().dropEvent(event)
            return
        
        # 验证拖拽场景:只有从Master Data的Users拖到Security Model的Stakeholder下才处理
        # 先判断源项的父节点是否是Users
        source_parent_index = source_index.parent()
        if not source_parent_index.isValid() or source_parent_index.data() != TXT_USER:
            super().dropEvent(event)
            return
        
        # 判断目标父节点是否是Stakeholder层级(CLS_LVL_STAKEHLD)
        target_parent_level = get_tree_selection_level(target_parent_item.index())
        if target_parent_level != CLS_LVL_STAKEHLD:
            super().dropEvent(event)
            return
        
        # 1. 检测重复项:检查目标父节点下是否已有同名子项
        duplicate_exists = False
        for row in range(target_parent_item.rowCount()):
            child_item = target_parent_item.child(row)
            if child_item.text() == source_text:
                duplicate_exists = True
                break
        
        if duplicate_exists:
            # 如果重复,取消拖拽操作
            event.ignore()
            return
        
        # 2. 设置正确的用户图标并添加项
        # 先调用父类的dropEvent完成默认拖拽操作
        super().dropEvent(event)
        
        # 找到刚添加的项,设置图标
        # 因为刚添加,所以取目标父节点的最后一个子项(或者遍历找同名的)
        new_item = None
        for row in range(target_parent_item.rowCount()):
            child_item = target_parent_item.child(row)
            if child_item.text() == source_text:
                new_item = child_item
                break
        
        if new_item:
            new_item.setIcon(QtGui.QIcon(ICON_LVL_USER))
            # 展开目标父节点,方便查看新添加的项
            self.setExpanded(target_parent_item.index(), True)

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        centralwidget = QtWidgets.QWidget()
        self.setCentralWidget(centralwidget)
        hBox = QtWidgets.QHBoxLayout(centralwidget)
        self.treeView = TreeView(centralwidget)
        hBox.addWidget(self.treeView)

if __name__ == "__main__":
    app = QtWidgets.QApplication([])
    window = MainWindow()
    create_tree_data(window.treeView)
    window.treeView.expand(window.treeView.model().index(0, 0)) # expand the System-Branch
    window.setGeometry(400, 400, 500, 400)
    window.show()
    app.exec_()

关键修改说明

  1. 源项与目标节点的精准判断:

    • 首先确认拖拽的源项来自Master Data下的Users节点,避免其他类型项的误操作
    • 确保目标父节点是Security Model下的Stakeholder层级(也就是你说的client_c/stakeholder_d的子层级)
  2. 重复项检测逻辑:

    • 在添加前遍历目标父节点的所有子项,检查是否已有同名项,如果有则直接忽略拖拽操作,避免重复
  3. 图标设置时机:

    • 先调用父类的dropEvent完成默认的拖拽添加操作,然后找到刚添加的子项,给它设置ICON_LVL_USER图标,这样就和user_b的图标保持一致了
  4. 优化目标节点判断:

    • 处理了拖拽到用户项上的情况,此时自动将父节点设为该用户项的父节点(也就是Stakeholder节点),符合你的层级要求

内容的提问来源于stack exchange,提问作者ProfP30

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 09:15:53