代码示例
#!/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_())
效果展示
如果该文章对您有帮助请给博主点个赞哈😄