【问题标题】:QTreeView: checking if item to be dropped is already present and avoid it on dropEvent()QTreeView:检查要删除的项目是否已经存在并在 dropEvent() 上避免它
【发布时间】:2019-12-30 13:17:16
【问题描述】:

我有一个带有实现 dropEvent() 处理程序的 QTreeView。 如果要丢弃的项目已经存在于目标根目录下,我怎么能完全避免丢弃? 例如。从已经存在的节点“Security Model”/“client_c”/“stakeholder_d”的“Master Data”节点拖拽“user_b”。

完整的工作代码示例:

#!/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(TXT_USER)
        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):
        tree = event.source()        

        if self.viewport().rect().contains(event.pos()):
            fake_model = QtGui.QStandardItemModel()
            fake_model.dropMimeData(
                event.mimeData(), event.dropAction(), 0, 0, QtCore.QModelIndex()
            )            
            for r in range(fake_model.rowCount()):
                for c in range(fake_model.columnCount()):
                    ix = fake_model.index(r, c)
                    print("item: ", ix.data())

                    item = QtGui.QStandardItem(ix.data())
                    icon = QtGui.QIcon(TXT_USER)
                    item.setIcon(icon)

            sParent: str = ""
            par_ix = tree.selectedIndexes()[0].parent()
            if par_ix.isValid():
                sParent = par_ix.data()
                print("par. item: ", sParent)

            to_index = self.indexAt(event.pos())
            if to_index.isValid():
                print("to:", to_index.data())

            if (sParent == TXT_CLIENT and get_tree_selection_level(to_index) == CLS_LVL_ROOT) or (sParent == TXT_STAKEHLD and get_tree_selection_level(to_index) == CLS_LVL_CLIENT) or (sParent == TXT_USER and get_tree_selection_level(to_index) == CLS_LVL_STAKEHLD):
                # to-do:
                # 1 - check if the item is already there; if yes: omit
                pass

                # 2 - set the proper icon
                pass

                super().dropEvent(event)
                self.setExpanded(to_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_()

【问题讨论】:

  • 从你所指出的我可以推断,你应该只能拖动没​​有孩子的项目,例如“主数据”或“用户”节点应该不能拖动,但如果“user_a”,我说的对吗?
  • 另一方面,你能创造一个新的关卡吗?也就是说,例如,将一个项目拖动为“user_b”的子项
  • 在第一个根中,“主数据”没有层次结构。只是平了三个父母和他们的孩子。此结构是“固定的”,用户不应修改。层次结构的手动构建在第二个节点“安全模型”中完成。在这里,用户将在 z 客户下的 y 利益相关者下建立 x 用户。
  • “主数据”可以被认为是一个对象池。在“安全模型”中,最终用户可以构建一个灵活的结构,将这些节点置于关系中,但父结构是客户端 -> 利益相关者 -> 用户所以在最深层次上,我想从“主数据”中拖动“用户” ” 在“安全模型”中所需的利益相关者下的池。稍后将使用右键单击上下文菜单创建“安全模型”中的容器(稍后必须自己实现)。
  • 好的,据我了解拖拽的来源只能是“主数据”子节点的最终节点,而“安全模型”节点的层次结构为“client_x”->“ stakeholder_y” -> “user_z” 所以作为“主数据”的最后一个节点的“user_a”只能作为现有“stakeholder_b”的子节点拖动,另一个例子是stakeholder_p只能被拖动为子节点的 client_q(x、y、z 等是通用的)我正确吗?

标签: python pyqt5 qtreeview


【解决方案1】:

您可以使用 dragMoveEvent 来执行过滤器:

  • 可以存在与同一个父亲具有相同文本的节点。
  • SM 节点只能将客户端作为子节点。
  • 客户端节点只能将利益相关者作为子节点。
  • 利益相关者节点只能将用户作为子节点。

综合以上情况,解决办法是:

from enum import Enum
import os
import sys

from PyQt5 import QtCore, QtGui, QtWidgets

TXT_SYSTEM = "Master Data"
TXT_SECURITY = "Security Model"

TXT_CLIENT = "Clients"
TXT_STAKEHLD = "Stakeholders"
TXT_USER = "Users"

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"

TYPE_ROLE = QtCore.Qt.UserRole + 1000


class NodeRoles(Enum):
    ROOT_ROLE = 0
    CLIENT_ROLE = 1
    STAKEHOLD_ROLE = 2
    USER_ROLE = 3


CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))

SYSTEM_DATA = {
    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"],
}


SECURITY_DATA = {
    "client_a": {"stakeholder_b": ["user_a"]},
    "client_c": {"stakeholder_d": ["user_b"]},
}


class CustomModel(QtGui.QStandardItemModel):
    def __init__(self, system_data, security_data, parent=None):
        super().__init__(parent)
        self.create_system_node(system_data)
        self.create_security_node(security_data)

    def create_system_node(self, system_data):
        root = QtGui.QStandardItem(TXT_SYSTEM)
        self.appendRow(root)

        for it in (root, self.invisibleRootItem()):
            it.setFlags(
                it.flags() & ~QtCore.Qt.ItemIsDragEnabled & ~QtCore.Qt.ItemIsDropEnabled
            )
            it.setData(NodeRoles.ROOT_ROLE, TYPE_ROLE)

        for key, path_icon, role in zip(
            (TXT_USER, TXT_CLIENT, TXT_STAKEHLD),
            (ICON_LVL_USER, ICON_LVL_CLIENT, ICON_LVL_STAKEHLD),
            (NodeRoles.USER_ROLE, NodeRoles.CLIENT_ROLE, NodeRoles.STAKEHOLD_ROLE),
        ):
            it = QtGui.QStandardItem(key)
            it.setFlags(
                it.flags() & ~QtCore.Qt.ItemIsDragEnabled & ~QtCore.Qt.ItemIsDropEnabled
            )
            icon = QtGui.QIcon(os.path.join(CURRENT_DIR, path_icon))
            it.setIcon(icon)
            it.setData(NodeRoles.ROOT_ROLE, TYPE_ROLE)
            root.appendRow(it)

            for value in system_data[key]:
                child = QtGui.QStandardItem(value)
                child.setFlags(child.flags() & ~QtCore.Qt.ItemIsDropEnabled)
                child.setData(role, TYPE_ROLE)
                it.appendRow(child)

    def create_security_node(self, security_data):
        root = QtGui.QStandardItem(TXT_SECURITY)
        root.setData(NodeRoles.ROOT_ROLE, TYPE_ROLE)
        self.appendRow(root)
        root.setFlags(root.flags() & ~QtCore.Qt.ItemIsDragEnabled)

        self._fill_node(security_data, root, 0)

    def _fill_node(self, data, root, level):
        role = (NodeRoles.CLIENT_ROLE, NodeRoles.STAKEHOLD_ROLE, NodeRoles.USER_ROLE)[
            level
        ]
        icon_path = (ICON_LVL_CLIENT, ICON_LVL_STAKEHLD, ICON_LVL_USER)[level]
        icon = QtGui.QIcon(os.path.join(CURRENT_DIR, icon_path))
        if isinstance(data, dict):
            for key, value in data.items():
                it = QtGui.QStandardItem(key)
                it.setFlags(it.flags() & ~QtCore.Qt.ItemIsDropEnabled)
                it.setIcon(icon)
                it.setData(role, TYPE_ROLE)
                root.appendRow(it)
                self._fill_node(value, it, level + 1)
            return
        else:
            for d in data:
                it = QtGui.QStandardItem(d)
                it.setIcon(icon)
                it.setData(role, TYPE_ROLE)
                root.appendRow(it)


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)
        self.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)

        self.resize(640, 480)

    def dragMoveEvent(self, event):
        if event.source() is not self:
            event.ignore()
            return
        fake_model = QtGui.QStandardItemModel()
        fake_model.dropMimeData(
            event.mimeData(), event.dropAction(), 0, 0, QtCore.QModelIndex()
        )
        it = fake_model.item(0)
        role = it.data(TYPE_ROLE)

        to_index = self.indexAt(event.pos())
        root = to_index
        while root.parent().isValid():
            root = root.parent()
        if root == self.model().item(0).index():
            event.ignore()
        else:
            to_role = to_index.data(TYPE_ROLE)
            if (
                (to_role == NodeRoles.ROOT_ROLE and role == NodeRoles.CLIENT_ROLE)
                or (
                    to_role == NodeRoles.CLIENT_ROLE
                    and role == NodeRoles.STAKEHOLD_ROLE
                )
                or (to_role == NodeRoles.STAKEHOLD_ROLE and role == NodeRoles.USER_ROLE)
            ):
                to_item = self.model().itemFromIndex(to_index)
                for i in range(to_item.rowCount()):
                    child_it = to_item.child(i)
                    if child_it.text() == it.text():
                        event.ignore()
                        return
                self.setExpanded(to_index, True)
                super().dragMoveEvent(event)
            else:
                event.ignore()


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    w = TreeView()
    model = CustomModel(system_data=SYSTEM_DATA, security_data=SECURITY_DATA)
    w.setModel(model)
    w.show()
    sys.exit(app.exec_())

【讨论】:

  • 使用给定的代码,我无法再从主数据树中拖动任何用户。在此处查看屏幕截图:imgur.com/a/xCNZ8Ez
猜你喜欢
  • 1970-01-01
  • 2010-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-16
  • 2013-10-28
  • 2014-12-18
  • 2012-05-13
相关资源
最近更新 更多