1. QTreeView简介
TreeView、TableView、ListView是几个最常用的视图控件,其中TreeView具有分级功能完全可替代后两者使用。三者使用时都遵循View<->Model架构,TreeView本身只负责显示,对数据操作针对Model进行,模型数据的改变自动在视图中得以体现。
2. 常用属性
2.1. 单选、多选
// QAbstractItemView::SingleSelection 单选
// QAbstractItemView::MultiSelection 允许多选
// QAbstractItemView::NoSelection 不允许选择
treeView->setSelectionMode(QAbstractItemView::SingleSelection);
2.2. 整行、整列选取
// QAbstractItemView::SelectItems 单元格Item选择
// QAbstractItemView::SelectRows 整行选择
// QAbstractItemView::SelectColumns 整列选择
treeView->setSelectionBehavior(QAbstractItemView::SelectRows);
2.3. 焦点策略
// Qt::TabFocus 0x1
// Qt::ClickFocus 0x2
// Qt::StrongFocus 0xb (TabFoucs|ClickFocus|0x8)
// Qt::WheelFocus 0xf (StrongFoucs | 0x4)
// Qt::NoFocus 无焦点虚线框
treeView->setFocusPolicy(Qt::NoFocus);
2.4. 根节点展开控制
用来控制是否显示根节点前的展开控制,若设置为false,则根节点无法展开(即使添加了子节点,也无法看到)。如此一来,TreeView可替代作为ListView来使用。
// 显示根节点前的控制(默认)
treeView->setRootIsDecorated(true);
2.5. 交替颜色控制
交替颜色使能时,可通过样式表控制交替行的颜色。
treeView->setAlternatingRowColors(true);
treeView->setStyleSheet("QTreeView::item:alternate {background: #90a0d0;}
// QSS
QTreeView::item:alternate {background: #90a0d0;}
2.6. 显示、隐藏行或列
// 隐藏第一列
treeView->setColumnHidden(0, true);
// 隐藏第二行
treeView->setRowHidden(1, true);
2.7. 文字折叠
// 表格边界处按字换行显示,默认为false;
// 注意:这个属性是否起作用依赖于Qt::TextElideMode,
// Qt::ElideLeft
// Qt::ElideRight
// Qt::ElideMiddle
// Qt::ElideNone (不显示省略号)
treeView->setWordWrap(true);
treeView->setTextElideMode(Qt::ElideNone);
2.8. 排序
// 开启列排序
treeView->setSortingEnabled(true);
// 显示标题栏排序指示符
treeView->header()->setSortIndicatorShown(true);
// 第二列升序排序
treeView->sortByColumn(1, Qt::AscendingOrder);
2.9. 缩进
本属性设定每级Item以像素为单位的缩进大小,对于顶层Item,缩进表示从TreeView视口(viewPort)左边缘到首列的距离。
// 全部缩进
treeView->setIndentation(0);
2.10. 表格编辑方式
// 表格不可编辑
treeView->setEditTriggers(QAbstractItemView::NoEditTriggers);
2.11. 滚动条显示
// 单列TreeView显示横向滚动条
treeView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
treeView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
3. 标题栏控制
3.1. 文字对齐
3.2. 标题栏显示、隐藏
// 隐藏表头
treeView->setHeaderHidden(false);
// 或
treeView->header()->hide();
treeView->header()->setHidden(true);
// 针对section单独控制,隐藏第二列标题
treeView->header()->setSectionHidden(1, true);
3.3. 标题栏高亮
treeView->header()->setHighlightSections(true);
3.4. 标题栏单元格对齐
// 顶部标题栏水平、垂直都居中对齐
treeView->header()->setDefaultAlignment(Qt::AlignCenter);
3.5. 标题栏尺寸设置
// 第一列固定列宽
treeView->header()->setSectionResizeMode(0, QHeaderView::Fixed);
treeView->setColumnWidth(0, 100);
// 第二列随内容尺寸自适应
treeView->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
// 最右侧一列自动随TreeView窗口尺寸扩展
treeView->header()->setSectionResizeMode(treeView->header()->count - 1, QHeaderView::Stretch);
3.6. 标题栏样式
// 颜色渐变标题栏,变化方向:(x1,y1) -> (x2,y2)
// 鼠标飘过、表头按下颜色背景设置
treeView->header()->setStyleSheet(
"QHeaderView::section {background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #3030B0, stop:1 #a0a0f0);"
"color: white; padding-left: 4px; border: 1px solid #6c6c6c;}"
"QHeaderView::section:horizontal:hover {color: white; background: #905090;}"
"QHeaderView::section:horizontal:pressed {color: white; background: #505090;}");
4. 示例
4.1. 建树
void MainWindow::initTreeView(QTreeView *treeView)
{
// 若数据模型已存在则清空,在本函数内重新分配
if (m_treeModel)
{
m_treeModel->clear();
delete m_treeModel;
m_treeModel = nullptr;
}
// 创建数据模型并绑定到treeView
m_treeModel = new QStandardItemModel(this);
treeView->setModel(m_treeModel);
// 创建标题栏,共四列,文字居中对齐
m_treeModel->setColumnCount(4);
QStringList headerItems;
headerItems << QString("姓名") << QString("语文") << QString("数学") << QString("英语");
m_treeModel->setHorizontalHeaderLabels(headerItems);
treeView->header()->setDefaultAlignment(Qt::AlignCenter);
// 第一列固定列宽
treeView->header()->setSectionResizeMode(0, QHeaderView::Fixed);
treeView->setColumnWidth(0, 150);
// 最后一列自动拉伸
treeView->header()->setSectionResizeMode(treeView->header()->count() - 1, QHeaderView::Stretch);
// 高亮标题栏文字(加粗)
treeView->header()->setHighlightSections(true);
// 单选
treeView->setSelectionMode(QAbstractItemView::SingleSelection);
// 整行选择
treeView->setSelectionBehavior(QAbstractItemView::SelectRows);
// 焦点策略:无焦点虚线框
treeView->setFocusPolicy(Qt::NoFocus);
// 交替颜色
treeView->setAlternatingRowColors(true);
// 开启列排序
treeView->setSortingEnabled(true);
// 显示标题栏排序箭头
treeView->header()->setSortIndicatorShown(true);
// 根节点是否可见,如果为false,根节点将不能展开,TreeView成为ListView
treeView->setRootIsDecorated(true);
// 表格不可编辑
treeView->setEditTriggers(QAbstractItemView::NoEditTriggers);
// 生成初始treeView数据
initTreeData(treeView, m_treeModel);
}
void MainWindow::initTreeData(QStandardItemModel *treeModel)
{
QStandardItem *treeRoot = m_treeModel->invisibleRootItem();
for (int k=0; k<3; k++)
{
QStandardItem *nodeClass = new QStandardItem(QString("班级23-%1").arg(k + 1, 2, 10, QChar('0')));
nodeClass->setIcon(QIcon(":/Images/iPhoto.png"));
// 新增顶层节点(不可见的根节点之下)
treeRoot->appendRow(nodeClass);
for (int i = 0; i < 5; i++)
{
QList<QStandardItem *> studentInClass;
for (int j=0; j<treeModel->columnCount(); j++)
{
QStandardItem *item = new QStandardItem;
item->setTextAlignment(Qt::AlignRight);
item->setData(QVariant(QString("col %1").arg(j)), Qt::UserRole + 1);
if (j == 0)
item->setIcon(QIcon(":/Images/Automator.png"));
if (j == 0)
item->setText(QString("name %1").arg(QRandomGenerator::global()->bounded(100, 1000)));
else if (j == 1)
item->setText(QString("%1").arg(QRandomGenerator::global()->bounded(60, 150)));
else if (j == 2)
item->setText(QString("%1").arg(QRandomGenerator::global()->bounded(60, 150)));
else if (j == 3)
item->setText(QString("%1").arg(QRandomGenerator::global()->bounded(60, 150)));
studentInClass.append(item);
}
// 班级下新增学生节点
nodeClass->appendRow(studentInClass);
}
}
}
4.2. 遍历节点
4.2.1 以Model方式遍历
从上往下遍历某节点下的所有子节点。
void MainWindow::treeTraverse(QTreeView *treeView, QModelIndex &parentIndex)
{
if (!parentIndex.isValid())
return;
QStandardItemModel *model = static_cast<QStandardItemModel *>(treeView->model());
// 获取父节点下的行数
int rowCount = treeView->model()->rowCount(parentIndex);
for (int i=0; i<rowCount; i++)
{
QModelIndex childIndex = model->index(i, 0, parentIndex);
QStandardItem *childItem = model->itemFromIndex(childIndex);
// 访问子节点
qDebug() << model->data(childIndex); // 默认获取Qt::DisplayRole
qDebug() << childItem->data(); // 默认获取用户私有数据 Qt::UserRole+1
// 递归子节点
if (model->hasChildren(childIndex))
treeTraverse(treeView, childIndex);
}
}
void MainWindow::do_treeDemoTraverse()
{
// 循环遍历顶层节点
for (int i=0; i<m_treeModel->rowCount(); i++)
{
// 取得顶层节点索引
QModelIndex index = m_treeModel->index(i, 0);
treeTraverse(ui->treeView, index);
}
}
4.2.2. 以Item方式遍历
void MainWindow::do_treeDemoTraverse2()
{
// 传入根节点QStandardItem指针
treeTraverse2(m_treeModel->invisibleRootItem());
}
void MainWindow::treeTraverse2(QStandardItem *parentNode)
{
if (!parentNode)
return;
for (int i = 0; i < parentNode->rowCount(); i++)
{
QStandardItem *child = parentNode->child(i);
// 二者等效
qDebug() << child->text();
qDebug() << child->data(Qt::DisplayRole);
if (child->hasChildren())
treeTraverse2(child);
}
}
4.3. 反向访问
由下往上遍历某节点的父系节点,直到根节点。
void MainWindow::treeTraverseBackward(QStandardItemModel *model, QModelIndex &index)
{
if (!index.isValid())
return;
//访问数据
qDebug() << model->data(index);
QModelIndex parentIndex = index.parent();
while (parentIndex.isValid())
{
qDebug() << model->data(parentIndex);
parentIndex = parentIndex.parent();
}
}
4.4. 磁盘目录建树
设定根目录,检索目录下的所有子目录以及文件,对所有子目录建立目录节点,对所有子目录再进行递归,对文件建立文件节点,文件节点无下级因此无需递归。整个目录递归完成后即可完成生成目录树。
```c++
//参数为主函数中添加的item和路径名
void MainWindow::addAllFiles(QStandardItem *parentNode, QString path)
{
if (!parentNode)
return;
QDir dir(path); //遍历各级子目录
QDir dirFiles(path); //遍历子目录中所有文件
// 先遍历文件夹 添加进widget
// fileList将保存root下面所有文件(包含子目录下的文件)
QFileInfoList fileList = dir.entryInfoList(QDir::Files);
QFileInfoList folderList = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
for (int i = 0; i != folderList.size(); i++) //自动递归添加各目录到上一级目录
{
QString dirPath = folderList.at(i).absoluteFilePath(); //获取目录绝对路径
QFileInfo dirInfo = folderList.at(i);
QString dirName = dirInfo.fileName(); // 获取短目录名
QStandardItem *childItem = new QStandardItem(QIcon(":/Images/Open.png"), dirName);
childItem->setData(QVariant(dirPath)); // 绝对路径放入私有数据 Qt::UserRole + 1
parentNode->appendRow(childItem); // 将当前短目录名添加成path的子项
// 递归子文件夹
addAllFiles(childItem, dirPath);
}
// 添加当前目录(parentNode)下的所有直接文件
QStringList suffixFilter;
//suffixFilter << "*.txt" << "*.c" << "*.h" << "*.go" << "*.py" << "*.cpp" << "*.hpp";
suffixFilter << "*.*";
QFileInfoList filesInCurrDir = dirFiles.entryInfoList(suffixFilter, QDir::Files, QDir::Name);
for (int i=0; i<filesInCurrDir.size(); i++)
{
//将当前目录中所有文件添加到treeView中
QFileInfo fi = filesInCurrDir.at(i);
QString shortName = fi.fileName();
QStandardItem *childItem = new QStandardItem(QIcon(":/Images/Open.png"), shortName);
childItem->setIcon(QIcon(":/Images/New.png"));
childItem->setText(shortName);
childItem->setData(QVariant(fi.absoluteFilePath()));
parentNode->appendRow(childItem); // parentNode下添加所有文件
}
}
// dirName: 目录全路径名
void MainWindow::openFolder(QString &dirName)
{
QDir dir(dirName);
if (dir.exists())
{
QFileInfo fi(dirName);
QStandardItem *item = new QStandardItem(QIcon(":/Images/Open.png"), fi.fileName());
item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable |
Qt::ItemIsEnabled | Qt::ItemIsAutoTristate);
// 绝对路径存入Qt::UserRole + 1
item->setData(QVariant(dirName));
// 添加顶层节点
m_treeModel->appendRow(item);
// 顶层节点下添加所有子目录与文件
addAllFiles(item, dirName);
ui->treeView->expandAll();
}
}