QT QTreeWidget 实现模糊查询和多选的勾选状态

#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();

结果截图:
在这里插入图片描述
在这里插入图片描述

  • 4
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值