#QT QTreeWidget 实现模糊查询和勾选状态
本文的主要代码基本都是总结2篇博客实现了模糊查询模糊查询和勾选状态QTreeWidget实现勾选基本上所有的操作都是递归操作,个人测试了性能,如果1w项左右的数据时,没啥问题,如果有几w,那么勾选所有的,效率很慢,需要几秒钟,大家可以测试,不废话了,直接上代码:如果有啥不懂的,可以在留言,会很详细的给出解释的
以下是头文件
#ifndef CTREEWEIGHTWITHSEARCH_H #define CTREEWEIGHTWITHSEARCH_H #pragma execution_character_set("utf-8") //解决中文乱码 #include <QLineEdit> #include <QTreeWidget> #include <QGridLayout> #include <QWidget> #include <QPointer> /** * @brief The CTreeWeightWithSearch class 带有搜索的节点树 * 1.可选是否需要搜索框 * 2.单选或者多选参数设置 */ class CTreeWeightWithSearch : public QWidget { Q_OBJECT public: explicit CTreeWeightWithSearch(QWidget *parent = nullptr,bool bShowSearchEdit=true,bool bCanMultiSelect=true,bool bShowCheckBox=true,bool bCheckedParentNeedCheckChildren=true); ~CTreeWeightWithSearch(); //测试数据,在使用时,在其他地方调用AddTreeRoot和AddTreeNode即可 void initData(); QTreeWidgetItem * AddTreeRoot(const QStringList &strColumnList,QIcon icon =QIcon(""),int nIconColumn = 0); QTreeWidgetItem * AddTreeNode(QTreeWidgetItem *parent,const QStringList &strColumnList,QIcon icon =QIcon(""),int nIconColumn = 0);//添加孩子节点 QList<QTreeWidgetItem *> getCheckedItems();//得到所有item的checked状态 //获取item孩子的勾选状态 void getItemChildOfCheckedState(QTreeWidgetItem *item); //根据模糊文字显示项(如果和模糊文字匹配的话) void hideOrShowItemBySearchText(const QString &strBlurrySearchText); void hideOrShowChildItemsBySearchText(QTreeWidgetItem *item,const QString &strBlurrySearchText);//显示或者隐藏孩子项 //显示父亲项(主要是模糊查询时,如果孩子被查询到,父亲没有被查询到,需要显示父亲) void updateShowParentItem(QTreeWidgetItem *item); bool &showSearchEdit(){return m_bShowSearchEdit;} bool &canMultiSelect(){return m_bCanMultiSelect;} bool &showCheckBox(){return m_bShowCheckBox;} bool &checkedParentNeedCheckChildren(){return m_bCheckedParentNeedCheckChildren;} private: void init(); void updateParentItem(QTreeWidgetItem* item);//更新父级项的选择状态 void setItemCheckState(QTreeWidgetItem* item);//设置选择项的状态 static const constexpr QSize m_sizeInitial =QSize(300,600);//初始化大小 //控件对象 QPointer<QLineEdit> m_pSearchLineEdit;//搜索控件 QPointer<QTreeWidget> m_pTreeWeight;//树控件 QPointer<QGridLayout> m_pGridLayout;//布局 //控制变量 QList<QTreeWidgetItem *> m_pListCheckedItems;//存放所有勾选状态的项 bool m_bShowSearchEdit;//是否显示搜索编辑框 bool m_bCanMultiSelect;//是否可以多选 bool m_bShowCheckBox;//是否显示复选框 bool m_bCheckedParentNeedCheckChildren;//父亲勾选时是否需要勾选孩子节点 signals: public slots: void on_treeItemCheckChange(QTreeWidgetItem* item,int column);//处理勾选的槽函数 void on_textChanged(const QString &);//处理搜索框变化的槽函数 }; #endif // CTREEWEIGHTWITHSEARCH_H
以下是CPP文件
#include <QDebug> #include "ctreeweightwithsearch.h" CTreeWeightWithSearch::CTreeWeightWithSearch(QWidget *parent,bool bShowSearchEdit,bool bCanMultiSelect, bool bShowCheckBox,bool bCheckedParentNeedCheckChildren) : QWidget(parent), m_pSearchLineEdit(new QLineEdit()), m_pTreeWeight(new QTreeWidget()), m_pGridLayout(new QGridLayout()), m_bShowSearchEdit(bShowSearchEdit), m_bCanMultiSelect(bCanMultiSelect), m_bShowCheckBox(bShowCheckBox), m_bCheckedParentNeedCheckChildren(bCheckedParentNeedCheckChildren) { setAttribute(Qt::WA_DeleteOnClose); init(); initData(); } CTreeWeightWithSearch::~CTreeWeightWithSearch() { if(m_pSearchLineEdit) { delete m_pSearchLineEdit; m_pSearchLineEdit = nullptr; } if(m_pTreeWeight) { delete m_pTreeWeight; m_pTreeWeight = nullptr; } if(m_pGridLayout) { delete m_pGridLayout; m_pGridLayout = nullptr; } } void CTreeWeightWithSearch::initData()//测试数据 { QTreeWidgetItem * beiJingItem = AddTreeRoot(QStringList() <<tr("北京"),QIcon(":/images/icon.png"));//想要显示图标,直接找一个对应的图标即可显示 for(int i = 0 ; i < 200; ++i) { QTreeWidgetItem *pTem = AddTreeNode(beiJingItem,QStringList() <<tr("朝阳")); QTreeWidgetItem *aTem = AddTreeNode(pTem,QStringList() <<tr("四合村")); AddTreeNode(aTem,QStringList() <<tr("四合小村")); QTreeWidgetItem *cItem = AddTreeNode(beiJingItem,QStringList() <<tr("海淀")); QTreeWidgetItem * HeiBeiItem = AddTreeNode(cItem,QStringList() <<tr("河北")); AddTreeNode(HeiBeiItem,QStringList() <<tr("石家庄")); } } /** * @brief CTreeWeightWithSearch::AddTreeRoot :添加根节点,可以是多个根节点,也可以只有一个根节点,看自己的需求是怎么样的 * @param strColumnList:数据列参数 * @param icon :设置图标 * @param nIconColumn :图标列(默认一般是第一列,尝试为0) * @return */ QTreeWidgetItem *CTreeWeightWithSearch::AddTreeRoot(const QStringList &strColumnList,QIcon icon, int nIconColumn) { QTreeWidgetItem * item=new QTreeWidgetItem(strColumnList); if(m_bShowCheckBox) { item->setCheckState(0,Qt::Unchecked);// } item->setIcon(nIconColumn,icon); m_pTreeWeight->addTopLevelItem(item);//作为顶级节点 return item; } /** * @brief CTreeWeightWithSearch::AddTreeNode 添加孩子节点 * @param parent * @param strColumnList * @return */ QTreeWidgetItem *CTreeWeightWithSearch::AddTreeNode(QTreeWidgetItem *parent, const QStringList &strColumnList,QIcon icon, int nIconColumn) { QTreeWidgetItem * item=new QTreeWidgetItem(strColumnList); if(m_bShowCheckBox) { item->setCheckState(0,Qt::Unchecked); } item->setIcon(nIconColumn,icon); parent->addChild(item); return item; } //获取所有选择的项 QList<QTreeWidgetItem *> CTreeWeightWithSearch::getCheckedItems() { m_pListCheckedItems.clear(); for(int i = 0; i < m_pTreeWeight->topLevelItemCount(); ++i) { QTreeWidgetItem *pParentItem(m_pTreeWeight->topLevelItem(i)); //如果父亲节点已选择,则孩子节点都是勾选状态 if(pParentItem->checkState(0) == Qt::Checked)//勾选了 { m_pListCheckedItems.push_back(pParentItem); } getItemChildOfCheckedState(pParentItem);//处理孩子节点的勾选状态 } return m_pListCheckedItems; } /** * @brief CTreeWeightWithSearch::getItemChildOfCheckedState 判断项的孩子是否勾选 * @param item */ void CTreeWeightWithSearch::getItemChildOfCheckedState(QTreeWidgetItem *item) { for(int i = 0; i < item->childCount(); ++i) { QTreeWidgetItem *childItem(item->child(i)); if(childItem->checkState(0) == Qt::Checked) { m_pListCheckedItems.push_back(childItem); } getItemChildOfCheckedState(childItem);//递归处理孩子节点的孩子 } } /** * @brief CTreeWeightWithSearch::hideOrShowItemBySearchText : 根据查询文字显示项 * @param strBlurrySearchText 查询文字 */ void CTreeWeightWithSearch::hideOrShowItemBySearchText(const QString &strBlurrySearchText) { for(int i = 0; i < m_pTreeWeight->topLevelItemCount(); ++i) { QTreeWidgetItem *pParentItem(m_pTreeWeight->topLevelItem(i)); if(pParentItem->text(0).contains(strBlurrySearchText,Qt::CaseInsensitive))//如果项目的第一列文字包含查询的文字 { if(pParentItem->isHidden()) pParentItem->setHidden(false); } else { pParentItem->setHidden(true); } hideOrShowChildItemsBySearchText(pParentItem,strBlurrySearchText);//处理孩子节点的勾选状态 } } void CTreeWeightWithSearch::hideOrShowChildItemsBySearchText(QTreeWidgetItem *item, const QString &strBlurrySearchText) { for(int i = 0; i < item->childCount(); ++i) { QTreeWidgetItem *childItem(item->child(i)); if(!childItem->text(0).contains(strBlurrySearchText,Qt::CaseInsensitive))//孩子没有匹配,隐藏孩子 { childItem->setHidden(true); } else { childItem->setHidden(false); updateShowParentItem(childItem);//显示父亲的状态 } hideOrShowChildItemsBySearchText(childItem,strBlurrySearchText);//处理孩子节点的勾选状态 } if(strBlurrySearchText == "")//如果没有输入文字,只显示最外一层,主要是数据多了,数据项过长 { if(item->isExpanded()) { item->setExpanded(false); } } } //如果孩子显示,就递归让父亲显示 void CTreeWeightWithSearch::updateShowParentItem(QTreeWidgetItem *item) { QTreeWidgetItem *parent(item->parent()); if(!parent) return; parent->setHidden(false); if(!parent->isExpanded()) { parent->setExpanded(true); } updateShowParentItem(parent);//递归显示父亲 } void CTreeWeightWithSearch::init() { resize(m_sizeInitial); //布局 if(m_bShowSearchEdit) { m_pGridLayout->addWidget(m_pSearchLineEdit,0,0,1,1); } m_pGridLayout->addWidget(m_pTreeWeight,1,0,1,1); m_pGridLayout->setContentsMargins(2,5,2,2); m_pGridLayout->setVerticalSpacing(10); setLayout(m_pGridLayout); //设置样式表 m_pSearchLineEdit->setPlaceholderText(tr("请输入")); m_pSearchLineEdit->setStyleSheet("border:1px groove #DCDFE6;border-radius:3px;padding:2px 4px;color:#606266;"); m_pTreeWeight->setHeaderHidden(true);// if(m_bShowCheckBox || (!m_bShowCheckBox && m_bCanMultiSelect))//可以多选 { m_pTreeWeight->setSelectionMode(QAbstractItemView::MultiSelection);//多选 } else { m_pTreeWeight->setSelectionMode(QAbstractItemView::SingleSelection);//单选 } m_pTreeWeight->setIconSize(QSize(14,14));//设置图标大小 m_pTreeWeight->setEditTriggers(QAbstractItemView::NoEditTriggers);// m_pTreeWeight->setFocusPolicy(Qt::NoFocus);//去除选择时的虚线框 //初始化槽函数 connect(m_pTreeWeight,SIGNAL(itemClicked( QTreeWidgetItem*,int)),this, SLOT(on_treeItemCheckChange( QTreeWidgetItem *,int)));//项点击事件 connect(m_pSearchLineEdit,SIGNAL(textChanged(const QString &)),this, SLOT(on_textChanged(const QString &))); } void CTreeWeightWithSearch::updateParentItem(QTreeWidgetItem *item) { QTreeWidgetItem *parent = item->parent(); if (!parent) return; int nSelectedCount = 0; int childCount = parent->childCount(); for (int i = 0; i < childCount; i++) //判断有多少个子项被选中 { QTreeWidgetItem* childItem = parent->child(i); if (childItem->checkState(0) == Qt::Checked || childItem->checkState(0) == Qt::PartiallyChecked) { nSelectedCount++; } } if (nSelectedCount <= 0) //如果没有子项被选中,父项设置为未选中状态 parent->setCheckState(0, Qt::Unchecked); else if (nSelectedCount > 0 && nSelectedCount < childCount) //如果有部分子项被选中,父项设置为部分选中状态,即用灰色显示 parent->setCheckState(0, Qt::PartiallyChecked); else if (nSelectedCount == childCount) //如果子项全部被选中,父项则设置为选中状态 parent->setCheckState(0, Qt::Checked); updateParentItem(parent); } void CTreeWeightWithSearch::setItemCheckState(QTreeWidgetItem *item) { int count = item->childCount(); //返回子项的个数 if (Qt::Checked == item->checkState(0))//勾选状态 { if (count > 0)//孩子数大于0 { for (int i = 0; i < count; i++) { item->child(i)->setCheckState(0, Qt::Checked); //处理孩子的勾选状态(有点啰嗦,不过实现了,如果节点有点多,如果选择最上层,可能需要几秒的时间) setItemCheckState(item->child(i)); } } else updateParentItem(item);//更新父亲的状态 } else if (Qt::Unchecked == item->checkState(0)) { if (count > 0)//孩子数大于0 { for (int i = 0; i < count; i++) { item->child(i)->setCheckState(0, Qt::Unchecked); setItemCheckState(item->child(i));//处理孩子的勾选状态 } } else //更新父亲的状态 updateParentItem(item); } } void CTreeWeightWithSearch::on_treeItemCheckChange(QTreeWidgetItem *item, int column) { if(m_bShowCheckBox && m_bCheckedParentNeedCheckChildren) //需要处理孩子与父亲勾选状态时,才处理 { if( column!=0)//不是0列,不处理 return; setItemCheckState(item); } } void CTreeWeightWithSearch::on_textChanged(const QString &strText) { hideOrShowItemBySearchText(strText); }
使用方式
*CTreeWeightWithSearch pTreeWeight = new CTreeWeightWithSearch();
pTreeWeight->show();
结果截图: