天天看點

PyQt5 QComboBox和QTreeView結合的下拉樹狀圖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_())

           

效果展示

PyQt5 QComboBox和QTreeView結合的下拉樹狀圖ComboTree實作代碼示例效果展示
PyQt5 QComboBox和QTreeView結合的下拉樹狀圖ComboTree實作代碼示例效果展示

如果該文章對您有幫助請給部落客點個贊哈😄