一、概述
Qt中 treeview的典型使用方法是model/view。这里不介绍model、view、delegate的工作方式。
model 、delegate与treeview的关联过程如下:
ui.treeView->setModel( pModel );
ui.treeView->setItemDelegate( pDelegate );
二、设置TreeView的头部信息
1、设置表头
方法一:void QStandardItemModel::setHorizontalHeaderLabels( const QStringList & labels );
参数labels指定一个QStringList对象,该对象中的每个字符串对应一个列的名字。
方法二:void QStandardItemModel::setHorizontalHeaderItem(int column, QStandardItem * item);
参数 column指定列的索引,item指定列的内容
例子:分别使用上述两种方法设置TreeView头部信息
方法一:
QStringList ColumnStringList;
ColumnStringList << QStringLiteral("车库") << QStringLiteral("信息");
model->setHorizontalHeaderLabels( QStringList() << QStringLiteral("车库")<< QStringLiteral("信息") );
QStandardItem* pColumnItem = model->horizontalHeaderItem(0);
pColumnItem->setIcon( QIcon(":/TreeView_T/Resources/qt-logo.png") );
pColumnItem->setToolTip(QStringLiteral("这是第一列") );
pColumnItem = model->horizontalHeaderItem(1);
pColumnItem->setIcon(QIcon(":/TreeView_T/Resources/qt-logo.png") );
pColumnItem->setToolTip(QStringLiteral("这是第二列"));
方法二:
QStandardItem* pItem = new QStandardItem( QIcon(":/TreeView_T/Resources/qt-logo.png"), QStringLiteral("车库"));
pItem->setToolTip( QStringLiteral("这是第一列"));
QStandardItem* pItem1 = new QStandardItem( QIcon(":/TreeView_T/Resources/qt-logo.png"), QStringLiteral("信息") );
pItem1->setToolTip( QStringLiteral("这是第二列"));
model->setHorizontalHeaderItem( 0, pItem);
model->setHorizontalHeaderItem( 1, pItem1);
2、隐藏表头
通过QTreeView中的 header()->hide(),可以隐藏表头。
效果如下:
三、填充行与列
(1)、可以在行头部添加图标
例子:
QMap m_IconMap;//存放公共图标
QMap m_CarportIconMap; //车库公共图标
//初始化图标库
m_CarportIconMap[QStringLiteral("Port1")] = QIcon(QStringLiteral(":/cars1/Resources/cars1/3942000.ico"));
m_CarportIconMap[QStringLiteral("Port2")] = QIcon(QStringLiteral(":/cars1/Resources/cars1/3942001.ico"));
m_CarportIconMap[QStringLiteral("Port3")] = QIcon(QStringLiteral(":/cars1/Resources/cars1/3942002.ico"));
m_CarportIconMap[QStringLiteral("Port4")] = QIcon(QStringLiteral(":/cars1/Resources/cars1/3942003.ico"));
m_CarportIconMap[QStringLiteral("Port5")] = QIcon(QStringLiteral(":/cars1/Resources/cars1/3942004.ico"));
m_IconMap[QStringLiteral("Audi")] = QIcon( QStringLiteral(":/cars_logo/Resources/cars_logo/audi.ico") );
m_IconMap[QStringLiteral("Bmw")] = QIcon( QStringLiteral(":/cars_logo/Resources/cars_logo/bmw.ico") );
m_IconMap[QStringLiteral("Buick")] = QIcon( QStringLiteral(":/cars_logo/Resources/cars_logo/Buick.ico") );
m_IconMap[QStringLiteral("Cadillac")] = QIcon( QStringLiteral(":/cars_logo/Resources/cars_logo/cadillac.ico") );
m_IconMap[QStringLiteral("Ferrari")] = QIcon( QStringLiteral(":/cars_logo/Resources/cars_logo/ferrari.ico") );
m_IconMap[QStringLiteral("Ford")] = QIcon( QStringLiteral(":/cars_logo/Resources/cars_logo/ford.ico") );
m_IconMap[QStringLiteral("Hyundai")] = QIcon( QStringLiteral(":/cars_logo/Resources/cars_logo/hyundai.ico") );
m_IconMap[QStringLiteral("Lexus")] = QIcon( QStringLiteral(":/cars_logo/Resources/cars_logo/lexus.ico") );
m_IconMap[QStringLiteral("Mazda")] = QIcon( QStringLiteral(":/cars_logo/Resources/cars_logo/mazda.ico") );
m_IconMap[QStringLiteral("Mercedesbenz")] = QIcon( QStringLiteral(":/cars_logo/Resources/cars_logo/mercedesbenz.ico") );
m_IconMap[QStringLiteral("Nissan")] = QIcon( QStringLiteral(":/cars_logo/Resources/cars_logo/nissan.ico") );
m_IconMap[QStringLiteral("Toyota")] = QIcon( QStringLiteral(":/cars_logo/Resources/cars_logo/toyota.ico") );
m_IconMap[QStringLiteral("Volkswagen")] = QIcon( QStringLiteral(":/cars_logo/Resources/cars_logo/Volkswagen.ico") );
QStandardItem* pStandardItem = NULL;
QStandardItem* pStandardChildItem = NULL;
int i = 0;
QMap::const_iterator it1 = m_CarportIconMap.constBegin();
for (QMap::const_iterator it = m_IconMap.constBegin(); it != m_IconMap.constEnd(); ++it)
{
if (i % 3 == 0)
{
pStandardItem = new QStandardItem((it1 + (i / 3)).value(), (it1 + (i / 3)).key());
model->appendRow(pStandardItem);
}
pStandardChildItem = new QStandardItem( it.value(), it.key());
pStandardItem->appendRow( pStandardChildItem );
pStandardItem->setChild( pStandardChildItem->row(),1, new QStandardItem( QString("this is %1").arg( it.key()) ));
++i;
}
效果如下:
(2)、可以在行头添加单选框
使用QStandardItem时与checkbox相关的几个函数:
void QStandardItem::setCheckable(bool checkable); //设置Item是否有复选框功能
void QStandardItem::setCheckState(Qt::CheckState state); //设置Item的复选状态
Qt::CheckState有三种状态:Qt::Unchecked :未选中状态、Qt::PartiallyChecked:部分选中状态(层次模型中的子项有一部分处于选中状态)、Qt::Checked:选中状态
void QStandardItem::setTristate(bool tristate); //设置Item是否是三态的(Qt::Unchecked、Qt::PartiallyChecked、Qt::Checked)
Qt::CheckState QStandardItem::checkState() const ; //返回Item的复选状态
bool QStandardItem::isCheckable() const ; //判断Item是否是可复选的
bool QStandardItem::isTristate() const ; //判断Item是否是三态的
例子:
QStandardItem* pStandardItem = NULL;
QStandardItem* pStandardChildItem = NULL;
int i = 0;
QMap::const_iterator it1 = m_CarportIconMap.constBegin();
for (QMap::const_iterator it = m_IconMap.constBegin(); it != m_IconMap.constEnd(); ++it)
{
if (i % 3 == 0)
{
pStandardItem = new QStandardItem((it1 + (i / 3)).value(), (it1 + (i / 3)).key());
//设置Item为三态
pStandardItem->setTristate( true );
//设置Item为部分选中状态
pStandardItem->setCheckState( Qt::PartiallyChecked );
model->appendRow(pStandardItem);
}
pStandardChildItem = new QStandardItem( it.value(), it.key());
//设置Item具有单选能力
pStandardChildItem->setCheckable( true );
//设置Item不具有三态
pStandardChildItem->setTristate( false );
//设置Item为选中状态
pStandardChildItem->setCheckState( /*Qt::PartiallyChecked*/ Qt::Checked );
pStandardItem->appendRow( pStandardChildItem );
pStandardItem->setChild( pStandardChildItem->row(),1, new QStandardItem( QString("this is %1").arg( it.key()) ));
++i;
}
效果如下:
(3)三态复选框的智能关联(如果子项全选,父级也需要全选,如果子项部分选,父级就不完全选)
/*
QTreeView在QStandardItemModel设置复选框后,并不是按照规则的,需要通过代码设置
三态复选框主要体现在树形控件里,如果子项目全选,父级需要全选,如果子项目部分选择,父级就是不完全选
*/
/*
item checkbox单击响应函数
*/
void MyDelegate::treeItem_CheckChanged(QStandardItem* item)
{
if (item == nullptr)
{
return;
}
if (item->isCheckable() )
{
//如果item是存在复选框的,那么就进行下面的操作
Qt::CheckState state = item->checkState(); //获取当前的选择状态
if (item->isTristate())
{
//如果item是三态的,说明可以对子目录进行全选和全不选的设置
if (state != Qt::PartiallyChecked)
{
//当前是选中状态,需要对其子项目设置为全选
treeItem_CheckAllChild(item, state == Qt::Checked ? true : false);
}
}
else
{
//说明是两态的,两态会对父级的三态有影响
//判断兄弟节点的情况
treeItem_CheckChildChanged( item );
}
}
}
//
// \brief 递归设置所有的子项目为全选或全不选状态
// \param item 当前项目
// \param check true时为全选,false时全不选
//
void MyDelegate::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 MyDelegate::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 );
}
}
//
// \brief 根据子节点的改变,更改父节点的选择情况
// \param item
//
//此函数也是一个递归函数,首先要判断的是父级是否到达顶层
//,到达底层作为递归的结束,然后通过函数checkSibling判断当前的兄弟节点的具体情况
//
void MyDelegate::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 );
}
//
// \brief 测量兄弟节点的情况,如果都选中返回Qt::Checked,都不选中Qt::Unchecked,
// \不完全选中返回Qt::PartiallyChecked
// \param item
// \return
Qt::CheckState MyDelegate::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;
}
四、QStandardItemModel的角色控制
<一>、自定义角色的使用
在MFC中,树形控件CTreeCtrl是通过SetItemData函数来对节点设置一个指针的值,这个值可以是个指针、DWORD值、自定义的标志或者自定义类型的指针。Qt的TreeView比MFC的CTreeCtrl封装的更好,其功能也更强大,以至于可以给每个节点设定非常非常多的值(只要内存足够)。QTreeView只负责显示渲染,数据都有Model来负责管理。我们可以使用setData函数来为节点设置数据。
setData函数在Model和Item中都有,功能都一样。QStandardItemModel中setData函数的定义:
virtual bool setData( const QModelIndex & index, const QVarient & value, int role = Qt::EditRole);
QStandardItem中setData的定义是:
virtual void setData( const QVaritant & value, int role = Qt::UserRole + 1 );
参数:value:是要设置的值, role是一个int型数据,Qt中把这个称为role角色,实际就是对设定值的标定,因为item可以设置许多值,这就需要一个用以区分的标志,这个区分标志就叫角色。Qt已经把经常用到的角色内容定义好了,我们可以自定义角色标志,但不要和定义好的那些标志冲突,否则会不起作用。如果显示文字就用Qt::DisplayRole,要告诉QTreeView需要改变背景颜色,就标定Qt::BackgroundRole,要改变字体就标定Qt::FontRole等等。从中可以看出,role就是一个标示,用来标定存放在item李淼的值集体用于什么功能,系统默认的role参看Qt::ItemDataRole枚举。自定义的Role,就从Qt::UserRole开始往上加。
例子:
定义用户自定义的role:
#define ROLE_MARK_TYPE Qt::UserRole + 100
#define MARK_FOLDER 1
#define MARK_ITEM 2
为Item设置数据
QStandardItem* pStandardItem = NULL;
QStandardItem* pStandardChildItem = NULL;
int i = 0;
QMap::const_iterator it1 = m_CarportIconMap.constBegin();
for (QMap::const_iterator it = m_IconMap.constBegin(); it != m_IconMap.constEnd(); ++it)
{
if (i % 3 == 0)
{
pStandardItem = new QStandardItem((it1 + (i / 3)).value(), (it1 + (i / 3)).key());
pStandardItem->setCheckable( true );
//设置Item为三态
pStandardItem->setTristate( true );
//为Item设置自定义role
pStandardItem->setData( MARK_FOLDER, ROLE_MARK_TYPE ); //标记该item为文件夹
model->appendRow(pStandardItem);
}
pStandardChildItem = new QStandardItem( it.value(), it.key());
//设置Item具有单选能力
pStandardChildItem->setCheckable( true );
//设置Item不具有三态
pStandardChildItem->setTristate( false );
//为Item设置自定义role
pStandardChildItem->setData( MARK_ITEM, ROLE_MARK_TYPE ); //标记该Item为具体的项
pStandardItem->appendRow( pStandardChildItem );
pStandardItem->setChild( pStandardChildItem->row(),1, new QStandardItem( QString("this is %1").arg( it.key()) ));
++i;
}
在鼠标左键点击TreeView中的Item时,通过对话框显示当前点击的Item的类型:是文件夹还是具体的项?
bool MyDelegate::editorEvent(QEvent * event, QAbstractItemModel * model
, const QStyleOptionViewItem & option, const QModelIndex & index)
{
QStandardItemModel* pModel = static_cast( model );
if (event->type() == QEvent::MouseButtonPress)
{
if (static_cast(event)->button() == Qt::LeftButton)
{
if (model->data(index, ROLE_MARK_TYPE) == MARK_FOLDER)
{
QMessageBox::information(NULL, "TypeInfo", "this is folder!", QMessageBox::Ok);
}
else if (model->data(index, ROLE_MARK_TYPE) == MARK_ITEM )
{
QMessageBox::information(NULL, "TypeInfo", "this is item!", QMessageBox::Ok);
}
}
}
return QStyledItemDelegate::editorEvent( event, model, option, index );
}
例子:
bool MyDelegate::editorEvent(QEvent * event, QAbstractItemModel * model
, const QStyleOptionViewItem & option, const QModelIndex & index)
{
QStandardItemModel* pModel = static_cast( model );
if (event->type() == QEvent::MouseButtonPress)
{
if (static_cast(event)->button() == Qt::LeftButton)
{
if (model->data(index, ROLE_MARK_TYPE) == MARK_FOLDER)
{
QMessageBox::information(NULL, "TypeInfo", "this is folder!", QMessageBox::Ok);
}
else if (model->data(index, ROLE_MARK_TYPE) == MARK_ITEM )
{
QMessageBox::information(NULL, "TypeInfo", "this is item!", QMessageBox::Ok);
}
//设置Item的图标覆盖为某一颜色
pModel->itemFromIndex(index)->setData( QColor(0, 255, 0, 200), Qt::DecorationRole);
//设置气泡提示信息
pModel->itemFromIndex(index)->setData(QString("Tip:%1").arg(pModel->itemFromIndex(index)->text() ), Qt::ToolTipRole);
//设置背景色
pModel->itemFromIndex(index)->setData(QColor(232, 209, 57, 200), Qt::BackgroundRole);
}
}
return QStyledItemDelegate::editorEvent( event, model, option, index );
}
效果:
参看:http://www.tuicool.com/articles/FvaYNn
参看:http://www.it165.net/pro/html/201405/14029.html