`
wx1569110409
  • 浏览: 18623 次
文章分类
社区版块
存档分类
最新评论

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

 
阅读更多

目录:

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的复选框及自动识别勾选的功能。
下面放出效果图:





转载于:https://my.oschina.net/2nmjeSMen3/blog/674364

分享到:
评论

相关推荐

    Qt QTreeView使用-QStandardItemModel的使用

    具体介绍见: ...Qt树形控件QTreeView使用2——复选框的设置: http://blog.csdn.net/czyt1988/article/details/19171727 利用C++11的function和bind功能,实现QStandardItemModel的通用遍历函数: ...

    QT实现的treeView有复选框

    总之,QT实现带有复选框的`QTreeView`涉及自定义模型、设置视图小部件、处理信号和槽以及保持模型和视图的一致性。通过这样的定制,我们可以创建一个功能丰富的用户界面,允许用户以直观的方式操作树形数据结构。

    QT QTreeWidget添加复选框

    当我们需要在树形结构中实现选择功能时,可以给QTreeWidget的每一项添加复选框,以便用户可以选择单个或多个节点。这个过程涉及到Qt的自定义item delegate以及信号和槽的机制。 首先,我们要理解QTreeWidgetItem类...

    QT 下拉框与复选框组合使用

    3. **复选框的多选**:若需实现下拉框中的多个选项可同时选中,可以使用QListWidget或QTreeView,它们支持多选并有类似复选框的功能。 通过以上介绍,你应该已经了解了如何在QT中组合使用下拉框和复选框。这不仅...

    PCL+QT源码:增加树形控件

    在QT的树形控件中,可以为每个节点添加复选框。当用户勾选某个点云节点的复选框时,可以触发一个信号,如`itemChanged`,并在槽函数中根据复选框的状态决定是否渲染相应的点云。PCL提供了多种渲染选项,如`pcl::...

    QT的QTreeWidget有checkbox时轻松选中

    每个项(QTreeWidgetItem)都可以有自己的图标、文字和数据,同时还可以设置复选框状态。 为了实现点击行的任意位置都能改变复选框的状态,我们需要覆盖QTreeWidget的默认事件处理机制。这通常通过重写`...

Global site tag (gtag.js) - Google Analytics