文章目录
前言
项目中需要使用一个多级下拉复选列表,考虑使用QTreeView实现,参考了网上的教程,修改了一些问题,在此做个记录。
参考博客:
QStandardItemModel/QTreeView实现多选框多级联动
Qt树形控件QTreeView使用2——复选框的设置
一、效果演示![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/c3eea9b1c540e7b0292c21ddb7d09d6f.gif)
二、实现步骤
1.UI部分代码
#include <QTreeView>
#include <QHeaderView>
#include <QStandardItem>
#include <QStandardItemModel>
#include <QHBoxLayout>
MainWidget::MainWidget(QWidget *parent)
: QWidget(parent)
{
setupUI();
setupConnection();
}
void MainWidget::setupUI()
{
QHBoxLayout *mainLayout = new QHBoxLayout;
m_treeView = new QTreeView();
m_treeView->header()->setVisible(false);
m_model = new QStandardItemModel(m_treeView);
QStandardItem *itemRoot = new QStandardItem(tr("All"));
itemRoot->setCheckable(true);
itemRoot->setTristate(true);
m_model->appendRow(itemRoot);
for (int i = 0; i < 3; i++) {
QStandardItem *itemRt = new QStandardItem(QString("RT%1").arg(i));
itemRoot->appendRow(itemRt);
itemRt->setCheckable(true);
itemRt->setTristate(true);
QStandardItem *itemRecv = new QStandardItem(tr("Recv"));
itemRt->appendRow(itemRecv);
itemRecv->setCheckable(true);
itemRecv->setTristate(true);
for (int j = 0; j < 3; j++) {
QStandardItem *itemSA = new QStandardItem(QString("SA%1").arg(j));
itemRecv->appendRow(itemSA);
itemSA->setCheckable(true);
}
QStandardItem *itemSend = new QStandardItem(tr("Send"));
itemRt->appendRow(itemSend);
itemSend->setCheckable(true);
itemSend->setTristate(true);
for (int j = 0; j < 3; j++) {
QStandardItem *itemSA = new QStandardItem(QString("SA%1").arg(j));
itemSend->appendRow(itemSA);
itemSA->setCheckable(true);
}
}
m_treeView->setModel(m_model);
mainLayout->addWidget(m_treeView);
mainLayout->addLayout(operateLayout);
setLayout(mainLayout);
}
void MainWidget::setupConnection()
{
connect(m_model,SIGNAL(itemChanged(QStandardItem*)),this,SLOT(onItemChanged(QStandardItem*)));
}
2. 状态更新
void MainWidget::onItemChanged(QStandardItem* item)
{
disconnect(m_model,SIGNAL(itemChanged(QStandardItem*)),this,SLOT(onItemChanged(QStandardItem*)));
changeChildsState(item);
changeParentsState(item);
connect(m_model,SIGNAL(itemChanged(QStandardItem*)),this,SLOT(onItemChanged(QStandardItem*)));
}
//更新子节点状态
void MainWidget::changeChildsState(QStandardItem* item)
{
Qt::CheckState state = item->checkState();
if (item->rowCount() > 0) {
for(int i = 0; i < item->rowCount(); i++) {
QStandardItem* child = item->child(i);
if (Qt::Checked == state)
child->setCheckState(Qt::Checked);
else if (Qt::Unchecked == state)
child->setCheckState(Qt::Unchecked);
changeChildsState(child);
}
}
}
//更新父节点状态
void MainWidget::changeParentsState(QStandardItem* item)
{
if(item->parent())
{
QStandardItem* parentItem = item->parent();
Qt::CheckState siblingState = checkSibling(item);
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);
}
changeParentsState(parentItem);
}
}
//获取兄弟节点的选择情况
Qt::CheckState MainWidget::checkSibling(QStandardItem *item)
{
QStandardItem *parent = item->parent();
if (parent == nullptr)
return item->checkState();
int brotherCount = parent->rowCount();
int checkedCount(0), unChencedCount(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)
++unChencedCount;
else
++checkedCount;
if (checkedCount > 0 && unChencedCount > 0)
return Qt::PartiallyChecked;
}
if (unChencedCount > 0)
return Qt::Unchecked;
return Qt::Checked;
}
总结
在代码中修改节点选中状态时,控件也会发送itemChanged信号,进而触发槽函数,这样会导致连续调用onItemChanged,导致不必要的开销,在数据量大的时候,会造成页面的卡顿。因此在onItemChanged先解绑信号槽,节点状态设置完成后,再重新绑定。