pyqt5 qtreeview 结合自定义model添加按钮

 1. 序言

        在使用PyQt5开发的过程中,涉及到一个场景,需要在树中添加菜单按钮。经过研究学习,采用如下方案可以完成树中添加菜单按钮的功能,因过程中使用了自定义模型,也一并分享到平台,供大家参考。

效果如下:

2. 创建自定义模型

2.1 创建item成员类对象

        创建my_tree_item.py

class MyTreeItem:
    def __init__(self, data, parent=None):
        self.parentItem = parent  # 父节点
        self.childItems = []  # 子节点
        self.itemData = data  # 子节点数据
        self._row = -1  # 此节点位于父节点的位置

    def appendChild(self, child):
        """ 在当前节点插入子节点 """
        child.setRow(len(self.childItems))
        self.childItems.append(child)

    def child(self, row: int):
        """ 获取第row个子节点 """
        return self.childItems[row]

    def parent(self):
        """ 获取父节点 """
        return self.parentItem

    def childCount(self):
        """ 获取子节点数量 """
        return len(self.childItems)

    def columnCount(self):
        """ 统计节点列数 """
        return len(self.itemData)

    def data(self, column):
        """ 获取节点第column列的数据 """
        return self.itemData[column]

    def setRow(self, row: int):
        """ 设置该节点是其父节点的第几个节点 """
        self._row = row

    def row(self):
        """ 获取该节点是父节点的第几个子节点 """
        return self._row

    def setData(self, data, column):
        self.itemData[column] = data
2.2 创建树模型

        创建 my_tree_model.py

from PyQt5 import QtGui
from PyQt5.QtCore import QAbstractItemModel, QVariant, QModelIndex, Qt

from .my_tree_item import MyTreeItem


class MyTreeModel(QAbstractItemModel):
    def __init__(self, parent=None):
        super(MyTreeModel, self).__init__(parent)
        self.rootItem = None  # 最顶层根节点
        self.updateData()

    def data(self, index: QModelIndex, role: int = ...):
        """
        获取索引元素的数据
        :param index:
        :param role:
        :return:
        """
        if not index.isValid():
            return QVariant()

        # 在首列添加icon
        if role == Qt.DecorationRole and index.column() == 0:
            icon = QtGui.QIcon("./image.png")
            return icon
        # 树成员显示的数据
        item = index.internalPointer()
        if role == Qt.DisplayRole and index.column() == 0:
            return item.data(0)

        return QVariant()

    def setData(self, index: QModelIndex, value: typing.Any, role: int = ...) -> bool:
        if role == Qt.QEditRole:
            value_int = value

            self.rootItem[index.row()] = value_int

            self.dataChanged.emit(index, index)

            return True

    def flags(self, index: QModelIndex):
        """
        编辑
        :param index:
        :return:
        """
        if not index.isValid():
            return 0
        return Qt.ItemIsEnabled | Qt.ItemIsSelectable

    def headerData(self, section: int, orientation: Qt.Orientation, role: int = Qt.DisplayRole) -> QVariant:
        """
        标题的显示
        :param section:
        :param orientation:
        :param role:
        :return:
        """
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self.rootItem.data(section)
        return QVariant()

    def index(self, row: int, column: int, parent: QModelIndex = QModelIndex()) -> QModelIndex:
        """
        构造父节点下子节点索引
        :param row:
        :param column:
        :param parent:
        :return:
        """
        if not self.hasIndex(row, column, parent):
            return QModelIndex()

        if not parent.isValid():
            parentItem = self.rootItem
        else:
            parentItem = parent.internalPointer()

        childItem = parentItem.child(row)

        if childItem:
            return self.createIndex(row, column, childItem)
        else:
            return QModelIndex()

    def parent(self, index: QModelIndex) -> QModelIndex:
        """
        通过子节点获取父节点索引
        :param index:
        :return:
        """
        if not index.isValid():
            return QModelIndex()

        childItem = index.internalPointer()
        parentItem = childItem.parent()

        if parentItem == self.rootItem:
            return QModelIndex()

        return self.createIndex(parentItem.row(), 0, parentItem)

    def rowCount(self, parent: QModelIndex = QModelIndex()) -> int:
        """
        获取父节点下子节点行数
        :param parent:
        :return:
        """
        if not parent.isValid():
            parentItem = self.rootItem
        else:
            parentItem = parent.internalPointer()

        return parentItem.childCount()

    def columnCount(self, parent: QModelIndex = QModelIndex()) -> int:
        """
        获取父节点下子节点列数
        :param parent:
        :return:
        """
        return self.rootItem.columnCount()

    def setupModelData(self, parent):
        for i in range(5):
            primaryData = [f"节点{i}", "", ""]
            primaryItem = MyTreeItem(primaryData, parent)

            for j in range(3):
                childData = [f"子节点{j}", "", ""]
                childItem = MyTreeItem(childData, primaryItem)
                primaryItem.appendChild(childItem)

            parent.appendChild(primaryItem)

    def updateData(self):
        if self.rootItem:
            del self.rootItem
            self.rootItem = None

        rootData = ["根节点", ""]

        self.rootItem = MyTreeItem(rootData)
        self.setupModelData(self.rootItem)
2.3 结合模型添加按钮
from PyQt5.QtWidgets import QTreeView, QPushButton
from PyQt5.QtCore import QModelIndex

from .my_tree_model import MyTreeModel


class MyTreeView(QTreeView):
    def __init__(self, parent=None):
        super(MyTreeView, self).__init__(parent)
        """ 模型 """
        self.source_model = MyTreeModel()

        self.init()

    def init(self):

        self.setModel(self.source_model)

        self.setColumnWidth(0, 224)
        self.setColumnWidth(1, 40)
        self.initMenu()

    def initMenu(self) -> None:
        # 统计主目录数
        count_primary = self.source_model.rootItem.childCount()
        # 遍历主目录
        for i in range(count_primary):
            # 获取主目录索引
            index = self.source_model.index(i, 1, QModelIndex())
            item = self.source_model.rootItem.child(i)
            self.addMenuButton(item, index)

    def addMenuButton(self, treeItem, index):
        """

        :param treeItem:
        :param index:
        :param row:
        :return:
        """
        # 添加菜单按钮
        button = QPushButton("more")
        self.setIndexWidget(index, button)

        child_count = treeItem.childCount()
        if child_count != 0:
            for i in range(child_count):
                child_item = treeItem.child(i)
                child_index = self.source_model.index(i, 1, index)
                self.addMenuButton(child_item, child_index)
2.4 验证 
import sys
from PyQt5 import QtWidgets
from .my_tree_view import MyTreeView

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = MyTreeView()
    MainWindow.show()
    sys.exit(app.exec_())
  • 14
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在PyQt6中,可以使用自定义的模型来在QTreeView中显示数据。下面是一个使用自定义模型的示例代码。 ```python from PyQt6.QtCore import QAbstractItemModel, QModelIndex, Qt from PyQt6.QtWidgets import QApplication, QMainWindow, QTreeView class CustomModel(QAbstractItemModel): def __init__(self, data, parent=None): super().__init__(parent) self.rootItem = CustomItem(None, None) self.setupModelData(data, self.rootItem) def setupModelData(self, data, parent): for item in data: parent.appendChild(item) def index(self, row, column, parent=QModelIndex()): if not self.hasIndex(row, column, parent): return QModelIndex() if not parent.isValid(): parentItem = self.rootItem else: parentItem = parent.internalPointer() childItem = parentItem.child(row) if childItem: return self.createIndex(row, column, childItem) else: return QModelIndex() def parent(self, index): if not index.isValid(): return QModelIndex() childItem = index.internalPointer() parentItem = childItem.parent() if parentItem == self.rootItem: return QModelIndex() return self.createIndex(parentItem.row(), 0, parentItem) def rowCount(self, parent=QModelIndex()): if parent.column() > 0: return 0 if not parent.isValid(): parentItem = self.rootItem else: parentItem = parent.internalPointer() return parentItem.childCount() def columnCount(self, parent=QModelIndex()): if parent.isValid(): return parent.internalPointer().columnCount() else: return self.rootItem.columnCount() def data(self, index, role=Qt.ItemDataRole.DisplayRole): if not index.isValid(): return None item = index.internalPointer() if role == Qt.ItemDataRole.DisplayRole: return item.data(index.column()) elif role == Qt.ItemDataRole.TextAlignmentRole: return Qt.AlignmentFlag.AlignVCenter + Qt.AlignmentFlag.AlignLeft def headerData(self, section, orientation, role=Qt.ItemDataRole.DisplayRole): if orientation == Qt.Orientation.Horizontal and role == Qt.ItemDataRole.DisplayRole: return self.rootItem.data(section) def flags(self, index): if not index.isValid(): return Qt.ItemFlag.NoItemFlags return Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable class CustomItem: def __init__(self, data, parent): self.parentItem = parent self.itemData = data self.childItems = [] def appendChild(self, item): self.childItems.append(item) def child(self, row): return self.childItems[row] def childCount(self): return len(self.childItems) def columnCount(self): return len(self.itemData) def data(self, column): return self.itemData[column] def parent(self): return self.parentItem if __name__ == "__main__": app = QApplication([]) window = QMainWindow() treeView = QTreeView(window) data = [ CustomItem(["Item 1"], None), CustomItem(["Item 2"], None) ] model = CustomModel(data) treeView.setModel(model) window.setCentralWidget(treeView) window.resize(500, 500) window.show() app.exec() ``` 在这个示例中,定义了一个`CustomModel`类继承自`QAbstractItemModel`类,并重写了一些必要的方法。同时定义了一个`CustomItem`类作为模型的节点数据。 在`CustomModel`中,`index`方法用于获取给定行列的索引,`parent`方法用于获取给定索引的父索引,`rowCount`和`columnCount`方法用于获取给定索引的行数和列数,`data`方法返回指定索引的数据,`headerData`方法返回给定节的标题数据,`flags`方法定义每个索引的标志。 在主函数中,创建一个自定义模型`CustomModel`的实例,并将数据传递给模型。然后将该模型设置为`QTreeView`的模型,最后显示窗口并进入应用程序的事件循环。 这样,使用自定义模型的QTreeView就可以显示自定义数据了。您可以根据自己的需求扩展并修改此示例代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值