PyQt5 下拉树状图ComboTree实现

代码示例

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

# ======================================================
# @File    : combo_tree
# @Author  : forward_huan
# @Date    : 2023/2/16 21:54
# @Desc    :
# ======================================================
import sys

from PyQt5 import QtCore
from PyQt5.QtCore import QModelIndex, QEvent
from PyQt5.QtGui import QStandardItemModel, QMouseEvent, QStandardItem
from PyQt5.QtWidgets import QComboBox, QTreeView, QApplication


class ComboTree(QComboBox):
    clicked = QtCore.pyqtSignal()

    def __init__(self, readonly=False, item_min_h=None):
        super(ComboTree, self).__init__()
        self.__skip_next_hide = False
        self.readonly = readonly
        self.item_min_h = item_min_h
        self.currentTextChanged: QtCore.PYQT_SIGNAL = self.currentTextChanged
        self.currentIndexChanged: QtCore.PYQT_SIGNAL = self.currentIndexChanged
        self._init_tree_view()
        self.set_readonly(readonly)
        self.setModel(QStandardItemModel())

    def _init_tree_view(self):
        self.tree_view = QTreeView()
        self.tree_view.setEditTriggers(QTreeView.NoEditTriggers)
        self.setView(self.tree_view)
        self.tree_view.setHeaderHidden(True)
        self.tree_view.viewport().installEventFilter(self)
        if self.item_min_h is not None:
            self.tree_view.setMinimumHeight(self.item_min_h)

    def is_item_clicked(self, event: QMouseEvent):
        """
        判断树形结构中是否点击的是展开折叠图标

        树形结构是由两部分组成的,前面的缩进部分是branch, 即展开折叠图标,后面的才是item,
        item起点处于展开折叠图标后面。

        QTreeView().visualRect(model_index) 获取item的起点
        :param event:
        :return:
        """
        model_index = self.view().indexAt(event.pos())
        return self.view().visualRect(model_index).contains(event.pos())

    def get_standard_item(self, text, col=0):
        """
        获取指定text的节点

        :param text:
        :param col:
        :return:
        """

        def wrapper(item: QStandardItem, ret):
            if ret[0] is not None:
                return
            if item.text() == text:
                ret[0] = item
                return
            for i in range(item.rowCount()):
                wrapper(item.child(i, col), ret)

        model = self.model()
        if not isinstance(model, QStandardItemModel):
            return False
        result = [None]
        for row in range(model.rowCount()):
            wrapper(model.item(row, col), result)
        return result[0]

    def set_readonly(self, readonly):
        """
        设置只读摸式
        :param readonly:
        :return:
        """
        self.readonly = readonly
        self.setEditable(readonly)

    def set_pre_expand(self, standard_item: QStandardItem):
        """
        设置当前的所有父节点展开

        :param standard_item:
        :return:
        """
        parent: QStandardItem = standard_item.parent()
        while isinstance(parent, QStandardItem):
            self.tree_view.expand(parent.index())
            parent: QStandardItem = parent.parent()

    def select_index(self, index: QModelIndex):
        """
        设置某一项选中

        :param index:
        :return:
        """
        self.setRootModelIndex(index.parent())
        self.setCurrentIndex(index.row())

    def setCurrentText(self, text: str, col=0) -> None:
        standard_item = self.get_standard_item(text, col)
        if isinstance(standard_item, QStandardItem):
            self.select_index(standard_item.index())

    def showPopup(self) -> None:
        if self.readonly:
            return
        self.setRootModelIndex(QModelIndex())
        self.clicked.emit()
        super(ComboTree, self).showPopup()

    def hidePopup(self):
        if self.readonly:
            return

        if self.__skip_next_hide:
            self.__skip_next_hide = False
        else:
            super(ComboTree, self).hidePopup()

    def eventFilter(self, obj, event):
        if event.type() == QEvent.MouseButtonPress and obj is self.view().viewport():
            self.__skip_next_hide = not self.is_item_clicked(event)
        return False


def get_model():
    model = QStandardItemModel()
    item1 = QStandardItem("item1")
    model.appendRow(item1)
    item2 = QStandardItem("item2")
    model.appendRow(item2)

    item1_sub1 = QStandardItem("item1_sub1")
    item1_sub2 = QStandardItem("item1_sub2")
    item1.appendRows([item1_sub1, item1_sub2])

    item2_sub1 = QStandardItem("item2_sub1")
    item2_sub2 = QStandardItem("item2_sub2")
    item2.appendRows([item2_sub1, item2_sub2])
    return model


if __name__ == '__main__':
    app = QApplication(sys.argv)
    combo_tree = ComboTree(item_min_h=100)
    combo_tree.setWindowTitle("下拉树状图")
    combo_tree.resize(300, 30)
    combo_tree.setModel(get_model())
    combo_tree.show()
    sys.exit(app.exec_())

效果展示

在这里插入图片描述
在这里插入图片描述




如果该文章对您有帮助请给博主点个赞哈😄

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值