作者:奇先生
8.3.2 QTreeWidgetItem
树形控件条目的内容是最复杂的,因为每一个条目涉及到内部多列数据的操作、父子节点操作,这些都是之前列表控件条目和表格控件条目不具备的特性。我们 首先介绍树形 控件条目的构造函数,然后按父子节点操作、通用数据操作和非通用数据操作三方面介绍树形控件条目。
(1)构造函数和复制函数
QTreeWidgetItem 构造函数较多,首先看不带父对象指针的构造函数:
QTreeWidgetItem(int type = Type)
QTreeWidgetItem(const QStringList & strings, int type = Type)
QTreeWidgetItem(const QTreeWidgetItem & other)
类型 type 一般用于派生类的自定义条目类型,基本用不到。第二个构造函数字符串列表 strings 就是条目内多列的文本,类似把表格控件一整行的多列文本塞到一个条目内部了。第三个是复制构造函数,复制时除了 type()、treeWidget()、parent(),其他的都复制。
克隆函数:
QTreeWidgetItem * QTreeWidgetItem::clone() const
clone()是按照本条目一模一样造出一个新的条目,是深拷贝,与本条目(包括子孙节点)不共享内存,函数返回的新条目也没有复制 type()、treeWidget()、parent() ,新条目是自由的,没归属。clone() 函数会克隆所有的子孙节点,并且新子孙节点之间的关系也一样,QTreeWidgetItem 源码中 使用压栈出栈 方式实现了子孙节点的遍历复制。
赋值 "=" 函数:
QTreeWidgetItem & operator=(const QTreeWidgetItem & other)
等于号函数与克隆函数有本质区别,它只拷贝 other 这一个节点内的数据到本节点里,包括显示的字符串和多列数据、标志位等,type() 和 treeWidget()、parent()的内容不会修改。等于号函数不涉 及任何子孙节 点,也不改变隶属的父节点。
顺便提一下小于号函数:
virtual bool operator<(const QTreeWidgetItem & other) const
字典序比较大小,否则都按照第0列的文本比较。
(2)父子节点操作
条目查看父节点的指针使用函数:
QTreeWidgetItem * QTreeWidgetItem::parent() const //常量,子节点不能改父节点指针
注意子节点是不能修改父节点指针的,只能父节点换子节点,不能子节点换父节点。
最常用的是节点控制自己的直接子节点,添加子节点使用函数:
void addChild(QTreeWidgetItem * child) //添加一个子节点到末尾
void addChildren(const QList<QTreeWidgetItem *> & children) //添加多个子节点末尾
void insertChild(int index, QTreeWidgetItem * child) //插入子节点序号 index 序号位置
void insertChildren(int index, const QList<QTreeWidgetItem *> & children)//插入多个子节点到 index 位置
直接子节点的计数(与孙辈或更低辈分的节点数目无关)用如下函数:
int childCount() const
根据序号获取直接子节点的指针使用函数(如果序号超界返回NULL):
QTreeWidgetItem * child(int index) const //序号查子节点指针
反过来,根据子节点指针查序号的函数如下(如果查不到序号返回-1):
int indexOfChild(QTreeWidgetItem * child) const
移除子节点使用如下函数:
void removeChild(QTreeWidgetItem * child) //根据子节点指针解除父子关系
QTreeWidgetItem * takeChild(int index) //根据子节点序号解除父子 关系,返回卸下后的自由节点指针
QList<QTreeWidgetItem *> takeChildren() //卸下所有子节点
注意这几个函数只是解除父子关系,卸下的子节点还存在内存中,如果要完全删除需要手动 delete 每个节点。
当节点有隶属的树形控件时,可以使用下面函数对子节点排序:
void sortChildren(int column, Qt::SortOrder order) // 根据指定列号column排序,升序或降序由order指定
如果条目不属于任何树形控件,那么该排序函数无效。
条目还可以控制自己的子节点指示器(条目显示时左边的加号 +)如何显示:
QTreeWidgetItem::ChildIndicatorPolicy childIndicatorPolicy() const //获取子节点指示器显示策略
void QTreeWidgetItem::setChildIndicatorPolicy(QTreeWidgetItem::ChildIndicatorPolicy policy) //设置子节点指示器显示策略
子节点指示器显示策略 QTreeWidgetItem::ChildIndicatorPolicy 有三种:
ChildIndicatorPolicy 枚举常量 | 数值 | 描述 |
QTreeWidgetItem::ShowIndicator | 0 | 无论有无子节点都显示指示符。 |
QTreeWidgetItem::DontShowIndicator | 1 | 始终不显示指示符。 |
QTreeWidgetItem::DontShowIndicatorWhenChildless | 2 | 条目有子节点就显示指示符,没子节点就不显示。 |
默认值是最后一个,有子节点就显示指示符,没有子节点就不显示指示符,这种方式也最为科学,一般不需要改指示符显示策略。
关于父子节点操作函数介绍到这,这些函数的特点就是只处理直接的子节点,与孙子辈、更低辈分节点无关,孙子辈由儿子辈去管理,以此类推,族谱树中各层节点只管理亲儿子,其他辈分都不管。 递归操作 就是这样,只管处理儿子辈代码,孙子辈的由儿子辈去管,层层下推,就是递归的过程。
(3)通用数据操作
通用数据一般是用于 QDataStream 保存条目的信息到文件中,也可以从文件中加载通用数据生成以前的树。树形控件条目的通用数据函数与前面章节列表条目、表格条目类似,但是多了指定列号的参数,因为每个属性条目有多列数据,每列数据有分多种角色,因此属性条目使用二维向量存储通用数据:
// One item has a vector of column entries. Each column has a vector of (role, value) pairs.
QVector< QVector<QWidgetItemData> > values;
对于通用数据的设置和读取,也有 data() 和 setData() 函数,只是多了列号:
QVariant QTreeWidgetItem::data(int column, int role) const
void QTreeWidgetItem::setData(int column, int role, const QVariant & value)
其他针对各个角色的读写函数如下表所示:
获取函数 | 设置函数 | 数据角色 | 描述 |
text(int column) | setText(int column, const QString &text) | Qt::DisplayRole | 条目显示的文本。 |
icon(int column) | setIcon(int column, const QIcon &icon) | Qt::DecorationRole | 条目显示的图标。 |
statusTip(int column) | setStatusTip(int column, const QString &statusTip) | Qt::StatusTipRole | 如果主界面有状态栏,鼠标悬停在该条目上时显示该状态信息到状态栏。 |
toolTip(int column) | setToolTip(int column, const QString &toolTip) | Qt::ToolTipRole | 鼠标悬停在该条目上时显示的工具提示信息。 |
whatsThis(int column) | setWhatsThis(int column, const QString &whatsThis) | Qt::WhatsThisRole | 如果主界面窗口标题栏有?帮助按钮,点击帮助按钮再点击该条目会显示该帮助信息。 |
font(int column) | setFont(int column, const QFont &font) | Qt::FontRole | 显示条目文本用的字体。 |
textAlignment(int column | setTextAlignment(int column, int alignment) | Qt::TextAlignmentRole | 文本的对齐方式。 |
backgroundColor(int column) | setBackgroundColor(int column, const QColor &color) | Qt::BackgroundColorRole | 文本背景色。 |
textColor(int column) | setTextColor(int column, const QColor &color) | Qt::TextColorRole | 文字颜色。 |
background(int column) | setBackground(int column, const QBrush &brush) | Qt::BackgroundRole | 条目的背景画刷。 |
foreground(int column) | setForeground(int column, const QBrush &brush) | Qt::ForegroundRole | 条目的前景画刷。 |
checkState(int column) | setCheckState(int column, Qt::CheckState state) | Qt::CheckStateRole | 条目自带的复选框选中状态,可以是三态复选框。 |
sizeHint(int column) | setSizeHint(int column, const QSize &size) | Qt::SizeHintRole | 条目显示的建议尺寸。 |
树形条目也有相应的数据流读写函数,就是用于读取或保存这些通用数据:
QDataStream & operator<<(QDataStream & out, const QTreeWidgetItem & item) //外部函数,将条目写入数据流
QDataStream & operator>>(QDataStream & in, QTreeWidgetItem & item) //外部函数,读取数据流中的条目数据
void QTreeWidgetItem::write(QDataStream & out) const //成员函数,将条目数据写入数据流
void QTreeWidgetItem::read(QDataStream & in) //成员函数,从数据流中读取条目数据
运算符重载函数 operator<<() 和 operator>>() 本质就是调用上面的 write() 和read() 函数。
(4)非通用数据操作
条目在构造函数指定的类型可以用如下函数获取,这个类型是只读的:
int QTreeWidgetItem::type() const
在添加到树形控件之后,都可以用如下函数查看条目隶属的树形控件:
QTreeWidget * QTreeWidgetItem::treeWidget() const //常量,节点不能自行更换隶属,要从树形控件增删节点
程序运行时除了树形控件本身的 QTreeWidget::selectedItems() 可以判断选中条目,每个条目对象自己也有函数获取高亮选中状态或设置是否高亮选中:(默认情况 下,树形条目自身是否高亮选中与子孙条目的情况无关)
bool QTreeWidgetItem::isSelected() const
void QTreeWidgetItem::setSelected(bool select)
树形条目初始化时也有默认的标志位,并且运行时可以修改标志位:
Qt::ItemFlags QTreeWidgetItem::flags() const
void QTreeWidgetItem::setFlags(Qt::ItemFlags flags)
树形条目构造时的默认标志位如下:
Qt::ItemIsSelectable
|Qt::ItemIsUserCheckable
|Qt::ItemIsEnabled
|Qt::ItemIsDragEnabled
|Qt::ItemIsDropEnabled
树形条目默认不能编辑,如果希望条目文本可以双击编辑,可以用下面一句代码:
item->setFlags( (item->flags()) | Qt::ItemIsEditable ); //双击条目会自动 开启文本编辑器
这个标志会对树形条目所有列的文本编辑都生效,开启后该条目每个列的数据都能双击编辑。对于开启 Qt::ItemIsEditable 标志位的条目,除了用户双击等操作启用编辑器,也可以用函数代码指定开启条目的某列数据编辑器:
void QTreeWidget::editItem(QTreeWidgetItem * item, int column = 0)
树形条目默认有 Qt::ItemIsUserCheckable 标志,可以复选,但是复选框默认却看不到,可以用下面代码真正地显示复选框:
item->setCheckState(0, Qt::Unchecked); //显示第0列的复选框,要指定列号
树形条目的复选框状态很特殊:
如果不使用三态复选(默认情况),那么当前条目的复选状态与子孙条目复选状态无关。
如果开启树形条目的三态复选,那么当前条目的复选状态与子孙有关:
如果所有子孙勾选,那么父节点勾选 Qt::Checked;
如果部分子孙勾选,那么父节点部分勾选 Qt::PartiallyChecked;
如果所有子孙不勾选,那么父节点不勾选 Qt::Unchecked。
三态勾选其实是真正反映父子勾选关系的,如果用复选框,那么有子孙的节点应该用三态的,无子孙的叶子节点用二态的,并且应当将所有树形控件的条目都显 示复选框:
// 迭代器
QTreeWidgetItemIterator it(ui->treeWidget);
while (*it)
{
//取出当前条目
QTreeWidgetItem *item = *it;
if(item->childCount() > 0 )//有子节点开启三态复选,没子节点是二态复选
{
item->setFlags( item->flags() | Qt::ItemIsTristate );
}
item->setCheckState(0, Qt::Unchecked); //正常应该只用第0列的复选框,代表一整行条目
//找下一个条目
++it;
}
迭代器 QTreeWidgetItemIterator 专门用于遍历树形控件或某个父节点的所有子孙条目,因为树形结构是分叉结构,不同于列表控件的一维遍历,也不同于表格控件的二维遍历,因此需要迭代器或递归算法来穷举子孙 条目,下面小节专门介绍这些内容。