#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();
结果截图: