在 QTreeView 中实现拖放以重新排序项目,如窗口文件资源管理器

问题描述 投票:0回答:0

我已经尝试在我的

Drag and Drop
中实现
QTreeView
已经好几个星期了。所以我决定创建这篇文章来寻找解决方案。我查了很多网站,Qt的官网,github上都查不到。我还尝试询问 chatGPT 是否可以向我展示在
QTreeView
中拖放的示例,但我也没有成功。所以这就是我想要实现的目标:我想从
QTreeView
类创建一个
QAbstractItemModel
。然后我希望能够重新排序我的项目,例如将最后一个项目移动到第二行,或者将其移动到其他项目上以便将其作为该项目的子项插入。如果我也能移动一个孩子来制作顶级物品,那就太好了。顺便说一句,我正在使用 PySide6 所以问题可能来自模块? 这是一段需要处理的代码,它已经有一个带有子项的模型,因此您可以测试其中的每个方法并添加实现拖放所需的方法:

import sys

from PySide6.QtGui import *
from PySide6.QtCore import *
from PySide6.QtWidgets import *




class MainWindow(QMainWindow):
    def __init__(self, parent: QWidget = None):
        super().__init__(parent)
        self.resize(573, 468)

        self.frame = QFrame()
        self.setCentralWidget(self.frame)
        self.hlayout = QHBoxLayout()
        self.frame.setLayout(self.hlayout)


        self.view = QTreeView()
        self.view.setAlternatingRowColors(True)
        self.view.setSelectionBehavior(QAbstractItemView.SelectItems)
        self.view.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
        self.view.setAnimated(False)
        self.view.setAllColumnsShowFocus(True)
        self.hlayout.addWidget(self.view)
        headers = ["value",'Type','Progress Bar','Test','Test','Test','Test']


        self.model = TreeModel(headers, self)
        # self.proxyModel = QSortFilterProxyModel(self)
        # self.proxyModel.setSourceModel(self.model)
        self.view.setModel(self.model)

        self.view.expandAll()
        for column in range(self.model.columnCount()):
            self.view.resizeColumnToContents(column)



class TreeItem:
    def __init__(self, data: list, parent: 'TreeItem' = None):
        self.item_data = data
        self.parent_item = parent
        self.child_items = []

    def child(self, number: int) -> 'TreeItem':
        if number < 0 or number >= len(self.child_items):
            return None
        return self.child_items[number]

    def last_child(self):
        return self.child_items[-1] if self.child_items else None

    def child_count(self) -> int:
        return len(self.child_items)

    def child_number(self) -> int:
        if self.parent_item:
            return self.parent_item.child_items.index(self)
        return 0

    def column_count(self) -> int:
        return len(self.item_data)

    def data(self, column: int):
        if column < 0 or column >= len(self.item_data):
            return None
        return self.item_data[column]

    def insert_children(self, position: int, count: int, columns: int) -> bool:
        if position < 0 or position > len(self.child_items):
            return False

        for row in range(count):
            data = [None] * columns
            item = TreeItem(data.copy(), self)
            self.child_items.insert(position, item)

        return True
    
    def remove_children(self, position: int, count: int) -> bool:
        if position < 0 or position + count > len(self.child_items):
            return False

        for row in range(count):
            self.child_items.pop(position)

        return True


    def parent(self):
        return self.parent_item


    def set_data(self, column: int, value):
        if column < 0 or column >= len(self.item_data):
            return False

        self.item_data[column] = value
        return True

    def __repr__(self) -> str:
        result = f"<treeitem.TreeItem at 0x{id(self):x}"
        for d in self.item_data:
            result += f' "{d}"' if d else " <None>"
        result += f", {len(self.child_items)} children>"
        return result




class TreeModel(QAbstractItemModel):

    def __init__(self, headers: list, parent=None):
        super().__init__(parent)

        self.root_data = headers
        self.root_item = TreeItem(self.root_data.copy())
        self.setup_model_data(self.root_item)

    def columnCount(self, parent: QModelIndex = None) -> int:
        return self.root_item.column_count()

    def data(self, index: QModelIndex, role: int = None):
        if not index.isValid():
            return None

        if role != Qt.DisplayRole and role != Qt.EditRole:
            return None

        item: TreeItem = self.get_item(index)

        return item.data(index.column())

    def flags(self, index: QModelIndex) -> Qt.ItemFlags:
        if not index.isValid():
            return Qt.NoItemFlags

        return Qt.ItemIsEditable | QAbstractItemModel.flags(self, index)

    def get_item(self, index: QModelIndex = QModelIndex()) -> TreeItem:
        if index.isValid():
            item: TreeItem = index.internalPointer()
            if item:
                return item

        return self.root_item
      
    def headerData(self, section: int, orientation: Qt.Orientation,
                   role: int = Qt.DisplayRole):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self.root_item.data(section)

        return None

    def index(self, row: int, column: int, parent: QModelIndex = QModelIndex()) -> QModelIndex:

        if parent.isValid() and parent.column() != 0:
            return QModelIndex()

        parent_item: TreeItem = self.get_item(parent)
        if not parent_item:
            return QModelIndex()
        

        child_item: TreeItem = parent_item.child(row)
        if child_item:
            return self.createIndex(row, column, child_item)
        return QModelIndex()

    def insertColumns(self, position: int, columns: int,
                      parent: QModelIndex = QModelIndex()) -> bool:
        self.beginInsertColumns(parent, position, position + columns - 1)
        success: bool = self.root_item.insert_columns(position, columns)
        self.endInsertColumns()

        return success

    def insertRows(self, position: int, rows: int,
                   parent: QModelIndex = QModelIndex()) -> bool:
        parent_item: TreeItem = self.get_item(parent)
        if not parent_item:
            return False

        self.beginInsertRows(parent, position, position + rows - 1)
        column_count = self.root_item.column_count()
        success: bool = parent_item.insert_children(position, rows, column_count)
        self.endInsertRows()

        return success

    def parent(self, index: QModelIndex = QModelIndex()) -> QModelIndex:
        if not index.isValid():
            return QModelIndex()

        child_item: TreeItem = self.get_item(index)
        if child_item:
            parent_item: TreeItem = child_item.parent()
        else:
            parent_item = None

        if parent_item == self.root_item or not parent_item:
            return QModelIndex()

        return self.createIndex(parent_item.child_number(), 0, parent_item)

    def removeColumns(self, position: int, columns: int,
                      parent: QModelIndex = QModelIndex()) -> bool:
        self.beginRemoveColumns(parent, position, position + columns - 1)
        success: bool = self.root_item.remove_columns(position, columns)
        self.endRemoveColumns()

        if self.root_item.column_count() == 0:
            self.removeRows(0, self.rowCount())

        return success

    def removeRows(self, position: int, rows: int,
                   parent: QModelIndex = QModelIndex()) -> bool:
        parent_item: TreeItem = self.get_item(parent)
        if not parent_item:
            return False

        self.beginRemoveRows(parent, position, position + rows - 1)
        success: bool = parent_item.remove_children(position, rows)
        self.endRemoveRows()

        return success

    def rowCount(self, parent: QModelIndex = QModelIndex()) -> int:
        if parent.isValid() and parent.column() > 0:
            return 0

        parent_item: TreeItem = self.get_item(parent)
        if not parent_item:
            return 0
        return parent_item.child_count()

    def setData(self, index: QModelIndex, value, role: int) -> bool:
        if role != Qt.EditRole:
            return False

        item: TreeItem = self.get_item(index)
        result: bool = item.set_data(index.column(), value)

        if result:
            self.dataChanged.emit(index, index, [Qt.DisplayRole, Qt.EditRole])

        return result
    

    def setHeaderData(self, section: int, orientation: Qt.Orientation, value,
                      role: int = None) -> bool:
        if role != Qt.EditRole or orientation != Qt.Horizontal:
            return False

        result: bool = self.root_item.set_data(section, value)

        if result:
            self.headerDataChanged.emit(orientation, section, section)

        return result

    def setup_model_data(self, parent: TreeItem):
        parents = [parent]
        Data = [["Test 1",'diam',10,2,3,4,5,6],["Test 2",'len',55,2,3,4,5,6],["Test 3",'conc',64,2,3,4,5,6],["Test 4",'diam',9,2,3,4,5,6],["Test 5",'len',12,2,3,4,5,6]]
        for item in Data :
            parent : TreeItem = parents[0]
            col_count = self.root_item.column_count()
            parent.insert_children(parent.child_count(), 1, col_count)
            column_data = [item[0],item[1],item[2],item[3],item[4],item[5],item[6]]

            for column in range(len(column_data)):
                child = parent.last_child()
                child.set_data(column, column_data[column])

            parent2 : TreeItem = parent.last_child()
            parent2.insert_children(parent2.child_count(), 1, col_count)
            column_data = [item[0],item[1],item[2],item[3],item[4],item[5],item[6]]

            for column in range(len(column_data)):
                child = parent2.last_child()
                child.set_data(column, column_data[column])
        


if __name__=="__main__" :
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

我的每次尝试都崩溃了,每次都有不同的错误消息。大多数时候,我只能将物品放在其他物品上,但我永远不能将它们插入到其他物品中间。所以我认为从头开始是理解它如何工作以及如何正确实现它的最佳解决方案。我真的希望有人能找到答案,感谢您花时间阅读这篇文章。

qt drag-and-drop qtreeview pyside6
© www.soinside.com 2019 - 2024. All rights reserved.