文档章节

Qt树形控件QTreeView使用2——复选框的设置

尘中远
 尘中远
发布于 2016/05/12 23:52
字数 2396
阅读 155
收藏 0

目录:

Qt树形控件QTreeView使用1——节点的操作

Qt树形控件QTreeView使用2——复选框的设置

利用C++11的function和bind功能,实现QStandardItemModel的通用遍历函数


 通过QStandardItem和QStandardItemModel可以很简单方便的给QTreeView添加节点,但是,许多树形控件都需要树的节点需要一个复选框(checkBox),网上许多资料都是通过自定义model来实现的,而且不能很好的实现checkbox的父子关联(父节点选中子节点全部选中,父节点不选,子节点全部选),下面将介绍如何使用QStandardItem和QStandardItemModel实现复选框,且实现父子关联




1.使用QStandardItem使树形控件条目带上复选框

复选框在树形控件中经常见到,在QStandardItem中已经封装好了对复选框的一些设置
void QStandardItem:: setCheckable ( bool checkable )
void QStandardItem:: setTristate ( bool tristate )
void QStandardItem:: setCheckState ( Qt::CheckState state )
Qt::CheckState QStandardItem:: checkState () const
bool QStandardItem:: isCheckable () const
bool QStandardItem:: isTristate () const

从字面意思就知道这些函数是干什么的了,但这里要注意一些,checkBox有两种情况,
一种是两态,就是选中和不选中
一种是三态,选中、不选中、不完全选中,如图:


这种三态叫Tristate。
要设置条目有复选框只需要使用QStandardItem的函数setCheckable,无论是两态还是三态都需要先setCheckable,setCheckable默认是两态,如果希望是三态的话,需要再setTristate
示例代码如下:(树形视图节点的具体添加方法见上篇文章)
QStandardItemModel* model = new QStandardItemModel(ui->treeView);
    model->setHorizontalHeaderLabels(QStringList()<<QStringLiteral("项目名")<<QStringLiteral("信息"));
    QStandardItem* itemProject = new QStandardItem(m_publicIconMap[QStringLiteral("treeItem_Project")],QStringLiteral("项目"));
    model->appendRow(itemProject);
    model->setItem(model->indexFromItem(itemProject).row(),1,new QStandardItem(QStringLiteral("项目信息说明")));
    QStandardItem* itemFolder = new QStandardItem(m_publicIconMap[QStringLiteral("treeItem_folder")],QStringLiteral("文件夹1"));
    itemProject->appendRow(itemFolder);
    itemProject->setChild(itemFolder->index().row(),1,new QStandardItem(QStringLiteral("信息说明")));
    itemFolder = new QStandardItem(m_publicIconMap[QStringLiteral("treeItem_folder")],QStringLiteral("文件夹2"));
    itemProject->appendRow(itemFolder);
    for(int i=0;i<5;++i){
        QStandardItem* itemgroup = new QStandardItem(m_publicIconMap[QStringLiteral("treeItem_group")],QStringLiteral("组%1").arg(i+1));
        itemFolder->appendRow(itemgroup);
        for(int j=0;j<(i+1);++j){
            QStandardItem* itemchannel = new QStandardItem(m_publicIconMap[QStringLiteral("treeItem_channel")],QStringLiteral("频道%1").arg(j+1));
            itemgroup->appendRow(itemchannel);
            itemgroup->setChild(itemchannel->index().row(),1,new QStandardItem(QStringLiteral("频道%1信息说明").arg(j+1)));
        }
    }
    itemProject->setChild(itemFolder->index().row(),1,new QStandardItem(QStringLiteral("文件夹2信息说明")));
    itemProject = new QStandardItem(m_publicIconMap[QStringLiteral("treeItem_Project")],QStringLiteral("项目2"));
    model->appendRow(itemProject);
    for(int i =0;i<3;++i)
    {
        itemFolder = new QStandardItem(m_publicIconMap[QStringLiteral("treeItem_folder")],QStringLiteral("项目2文件夹%1").arg(i+1));
        itemFolder->setCheckable(true);
        itemFolder->setTristate(true);
        QStandardItem* itemFolderDes = new QStandardItem(m_publicIconMap[QStringLiteral("treeItem_group")],QStringLiteral("文件夹%1组").arg(i+1));
        itemProject->appendRow(itemFolder);
        itemProject->setChild(itemFolder->index().row(),1,itemFolderDes);
        for(int j=0;j<i+1;++j)
        {
             QStandardItem* item = new QStandardItem(m_publicIconMap[QStringLiteral("treeItem_dataItem")],QStringLiteral("项目%1").arg(j+1));
             item->setCheckable(true);
             itemFolder->appendRow(item);

        }
    }
    //关联项目属性改变的信号和槽
    connect(model,&QStandardItemModel::itemChanged,this,&Widget::treeItemChanged);
    //connect(model,SIGNAL(itemChanged(QStandardItem*)),this,SLOT(treeItemChanged(QStandardItem*)));
    ui->treeView->setModel(model);



代码中 m_publicIconMap 是QMap<QString,QIcon>对象,用于存放定义好的图标,在树形视图节点添加之前进行初始化,初始化代码如下:
m_publicIconMap[QStringLiteral("treeItem_Project")] = QIcon(QStringLiteral(":/treeItemIcon/res_treeItemIcon/Project.png"));
m_publicIconMap[QStringLiteral("treeItem_folder")] = QIcon(QStringLiteral(":/treeItemIcon/res_treeItemIcon/folder.png"));
m_publicIconMap[QStringLiteral("treeItem_folder-ansys")] = QIcon(QStringLiteral(":/treeItemIcon/res_treeItemIcon/folder-ansys.png"));
m_publicIconMap[QStringLiteral("treeItem_group")] = QIcon(QStringLiteral(":/treeItemIcon/res_treeItemIcon/group.png"));
m_publicIconMap[QStringLiteral("treeItem_channel")] = QIcon(QStringLiteral(":/treeItemIcon/res_treeItemIcon/channel.png"));

效果图:

2.三态复选框的智能关联

三态复选框的主要体现就在树形控件里,如果子项目全选,父级需要全选,如果子项目部分选,父级就是不完全选
下图是三态的正确表现方法



但QTreeView在QStandardItem设置复选框后,并不是按照规则的,这时需要进行代码设置

2.1 捕获复选框改变的信号

要对复选框进行操作,首先需要捕获树形视图的复选框改变发出的信号
通过QStandardItemModel设置的项目,任何改变都会触发void QStandardItemModel::itemChanged(QStandardItem * item)信号
因此需要定义一个槽函数和这个信号关联
private slots :
void treeItem_CheckChildChanged ( QStandardItem * item );


关联代码写在model创建之后的地方:
//关联项目属性改变的信号和槽
connect ( model ,&QStandardItemModel::itemChanged , this ,&Widget::treeItemChanged );
//connect(model,SIGNAL(itemChanged(QStandardItem*)),this,SLOT(treeItemChanged(QStandardItem*)));

这里使用最新的信号和槽的关联方法,记得在pro文件中加入如下,使得支持C++11
CONFIG+=c++11
槽函数的写法如下:
void Widget : : treeItemChanged ( QStandardItem * item )
{
}

下面开始实现三态的自动关联(父子节点checkbox自动关联)

2.2 父子节点复选框自动关联实现

void Widget : : treeItemChanged ( QStandardItem * item )
{
    if ( item == nullptr )
    return ;
    if ( item - > isCheckable ())
    {
        //如果条目是存在复选框的,那么就进行下面的操作
        Qt : : CheckState state = item - > checkState (); //获取当前的选择状态
        if ( item - > isTristate ())
        {
             //如果条目是三态的,说明可以对子目录进行全选和全不选的设置
            if ( state != Qt : : PartiallyChecked )
            {
                //当前是选中状态,需要对其子项目进行全选
                treeItem_checkAllChild ( item , state == Qt : : Checked ? true : false );
            }
        }
        else
        {
            //说明是两态的,两态会对父级的三态有影响
            //判断兄弟节点的情况
            treeItem_CheckChildChanged ( item );
        }
    }
}


首先要判断条目的状态,如果条目是有复选框的话,那么就进行操作。通过函数isCheckable()可以判断条目是否有复选框
在确认条目有复选框后,需要获取当前条目的选中状态,使用checkState ()函数可以判断当前条目的选中状态;
现在分两种情况:
1.如果条目是三态的,说明要判断它的子节点。条目选中时,所有子节点都将选中,条目不选中时,所有子节点都不选中
2.如果条目是两态的,说明可能会影响它的三态的父节点,当两态节点选中且其所有的兄弟节点都选中,三态父节点选中,若两态子节点和其兄弟节点都没选中,那么其三态父节点将不选中,若果兄弟节点有选中有不选中,三态父节点将是处于不完全选中状态

2.2.1 子节点递归全选

treeItem_checkAllChild 函数是用于使子节点全选的函数。这个函数实现如下:

///
/// \brief 递归设置所有的子项目为全选或全不选状态
/// \param item 当前项目
/// \param check true时为全选,false时全不选
///
void Widget::treeItem_checkAllChild(QStandardItem * item, bool check)
{
    if(item == nullptr)
        return;
    int rowCount = item->rowCount();
    for(int i=0;i<rowCount;++i)
    {
        QStandardItem* childItems = item->child(i);
        treeItem_checkAllChild_recursion(childItems,check);
    }
    if(item->isCheckable())
        item->setCheckState(check ? Qt::Checked : Qt::Unchecked);
}
void Widget::treeItem_checkAllChild_recursion(QStandardItem * item,bool check)
{
    if(item == nullptr)
        return;
    int rowCount = item->rowCount();
    for(int i=0;i<rowCount;++i)
    {
        QStandardItem* childItems = item->child(i);
        treeItem_checkAllChild_recursion(childItems,check);
    }
    if(item->isCheckable())
        item->setCheckState(check ? Qt::Checked : Qt::Unchecked);
}


通过这个功能实现,可以看看如何对树形节点的所有子节点进行遍历,一般树形节点的遍历是通过递归来实现的(递归的效率不是最高的,可以把递归拆解为循环)。
QStandardItem的child方法可以获取它的下级子节点,在这个方法之前现需要查明有多少个子节点,rowCount()方法是获取树形节点下一级的子节点个数(在树形视图中,每个节点的子节点算作这个节点的条目,第一个节点就是第一行,第二个就是第二行,以此类推,如果树形视图有多列的话,那么列也会起作用)。
treeItem_checkAllChild_recursion是个递归函数,通过这个函数可以把树形节点的所有子节点遍历一遍。
通过上面的这个方法,即可实现第一种情况。

2.2.2 父节点递归处理


treeItem_CheckChildChanged函数是用于处理第二种情况的,此函数主要对父级节点有影响,函数实现如下:
///
/// \brief 根据子节点的改变,更改父节点的选择情况
/// \param item
///
void Widget::treeItem_CheckChildChanged(QStandardItem * item)
{
    if(nullptr == item)
        return;
    Qt::CheckState siblingState = checkSibling(item);
    QStandardItem * parentItem = item->parent();
    if(nullptr == parentItem)
        return;
    if(Qt::PartiallyChecked == siblingState)
    {
        if(parentItem->isCheckable() && parentItem->isTristate())
            parentItem->setCheckState(Qt::PartiallyChecked);
    }
    else if(Qt::Checked == siblingState)
    {
        if(parentItem->isCheckable())
            parentItem->setCheckState(Qt::Checked);
    }
    else
    {
        if(parentItem->isCheckable())
            parentItem->setCheckState(Qt::Unchecked);
    }
    treeItem_CheckChildChanged(parentItem);
}


此函数也是一个递归函数,首先要判断的是父级是否到达顶层,到达底层作为递归的结束,然后通过函数checkSibling判断当前的兄弟节点的具体情况,checkSibling方法的实现如下:

///
/// \brief 测量兄弟节点的情况,如果都选中返回Qt::Checked,都不选中Qt::Unchecked,不完全选中返回Qt::PartiallyChecked
/// \param item
/// \return 如果都选中返回Qt::Checked,都不选中Qt::Unchecked,不完全选中返回Qt::PartiallyChecked
///
Qt::CheckState Widget::checkSibling(QStandardItem * item)
{
    //先通过父节点获取兄弟节点
    QStandardItem * parent = item->parent();
    if(nullptr == parent)
        return item->checkState();
    int brotherCount = parent->rowCount();
    int checkedCount(0),unCheckedCount(0);
    Qt::CheckState state;
    for(int i=0;i<brotherCount;++i)
    {
        QStandardItem* siblingItem = parent->child(i);
        state = siblingItem->checkState();
        if(Qt::PartiallyChecked == state)
            return Qt::PartiallyChecked;
        else if(Qt::Unchecked == state)
            ++unCheckedCount;
        else
            ++checkedCount;
        if(checkedCount>0 && unCheckedCount>0)
            return Qt::PartiallyChecked;
    }
    if(unCheckedCount>0)
        return Qt::Unchecked;
    return Qt::Checked;
}

checkSibling用于判断兄弟节点的关系,兄弟节点之间无外乎三种关系:
1.全选
2.全不选
3.部分选中
获取QStandardItem的兄弟节点有多种方法,这里是通过获取它的父级在获取父级的子节点来得到包括它自己的所有兄弟节点,另外QStandardItem可以通过函数QModelIndex index() const;获取Item对应的QModelIndex,QModelIndex有QModelIndex QModelIndex::sibling(int row, int column) const方法获取兄弟节点。
通过以上几个函数,即可实现QTreeView的复选框及自动识别勾选的功能。
下面放出效果图:





© 著作权归作者所有

共有 人打赏支持
尘中远
粉丝 1
博文 26
码字总数 47436
作品 0
朝阳
程序员
QT中的树型控件QTreeWidget和checkstate的使用

今天简单说一下Qt的树形控件,在Qt中树形控件的名称叫做QTreeWidget,而控件里的树节点的名称叫做QTreeWidgetItem。今天这 里讲的是如何创建具有复选框的树形控件: 当选中顶层的树形节点时,...

barsoom
2012/11/02
0
0
Qt控件上下布局,根据数据量自己扩充功能。

我要在固定大小的窗口里添加两个QTreeView控件,用QVBoxLayout进行上下布局。 要求两个QTreeView控件都要展开,以便用户查阅。而展开的时候每个控制不够显示全部内容。 现在有一个需求就是,...

临峰不畏
2013/12/24
408
2
动手改造柴哥的Html2Tree

原文发表在:http://www.birchlee.com/post/2011/10/25/31.aspx 最近在使用一些js,jQuery树形控件,大多数都是基于json格式,都是按照要求的数据格式才能出固定的树形。扩展性差,还要研究他...

birchlee
2011/10/25
0
0
带复选框的CTreeCtrl响应复选消息

1.在对话框中添加CTreeCtrl控件并勾选Check Boxes选项,为CTreeCtrl控件添加CTreeCtrl变量m_tree; 2.为CTreeCtrl控件添加NM_CLICK消息响应函数OnClickTree();代码如下: OnClickTree(NMHDR pNM...

barsoom
2013/01/30
0
0
《Qt 实战一二三》

简介 “我们来自Qt分享&&交流,我们来自QML分享&&交流”,不管你是笑了,还是笑了,反正我们是认真的。我们就是要找寻一种Hold不住的状态,来开始每一天的点滴分享,我们是一个有激情,有态度...

u011012932
2015/12/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Python深体验,常见的数据处理方式(必须要懂的)

1.缺失值处理 - 拉格朗日插值法 input_file数据文件内容(存在部分缺失值): from scipy.interpolate import lagrangeimport pandas as pdimport numpy as npinput_file = './dat...

无也Python
28分钟前
2
0
Spring MVC注解故障追踪记

Spring MVC是美团点评很多团队使用的Web框架。在基于Spring MVC的项目里,注解的使用几乎遍布在项目中的各个模块,有Java提供的注解,如:@Override、@Deprecated等;也有Spring提供的注解,...

Skqing
29分钟前
4
0
区块链入门教程以太坊源码分析cmd包分析

  兄弟连区块链入门教程以太坊源码分析cmd包分析。 ### cmd包概述 * geth 主要Ethereum CLI客户端。它是Ethereum网络(eth主网,测试网络或私有网)的入口点,使用此命令可以使节点作为ful...

兄弟连区块链入门教程
31分钟前
1
0
@Autowired 报红线

代码可正常跑,不过红线看着有点难受,解决方案 使用@Autowired(required=false) 或者@Resource. 这两者区别网上一大堆

斩神魂
37分钟前
3
0
DataTable中检索信息 (C#)

C#_从DataTable中检索信息 存在于内存中的虚拟表DataTable,绑定在数据显示控件后,如果想在再检索其中某些信息,可以利用DataTable.Select方法进行检索,避免了重复的读取数据库。Select方法...

CS-CS01
45分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部