本人并不专研Qt,只是写小项目时遇到问题和为了解决这个某个问题,特意学习了QTreeView的知识点。本人目的是为了实现一个显示网络数状拓扑结构图,简单的说就是实现树形结构链表,对比二叉树,我们知道二叉树中一个节点的最近子节点只有两个,一个左孩子和一个右孩子。但是本人的需求是每个节点都能有0个或0个以上的节点,故在寻找解决问题时偶然看到了QTreeView,因本人图形界面实现只学了Qt基础,所以选择用它。下图是本人写小项目的结果如下。
我们在Qt designer下拖入这个窗体Tree View
接下来需要一个模型对象QStandardItemModel,初始化时要把该对象导入该TreeView窗体中,同时要把这个对象的头文件包含进来,例子如下:
model = new QStandardItemModel(ui->treeView);//创建模型
ui->treeView->setModel(model);//导入模型
有时我们的需求会有每列都应该有个标签在头顶,实现方式如下:
model->setHorizontalHeaderLabels(QStringList()<
此时构建执行的效果:
尝试在里面添加第一个条目,需要一个QStandardItem对象,实现代码如下:
QStandardItem * item = new QStandardItem(tr("item one"));//创建一个条目对象
model->appendRow(item);//通过模型对象添加这个条目
效果如下:
QStandardItemModel模型对象添加条目的函数也有另一种方式,函数原型是void QStandardItemModel::setItem(int row, int column, QStandardItem *item)或void QStandardItemModel::setItem(int row, QStandardItem *item)
下面举例子从上面的代码添加测试代码如下:
model->setItem(0,0,new QStandardItem(tr("item two")));
model->setItem(2,0,new QStandardItem(tr("item three")));
结果会是如下:
原来的item one 不见了,原因是前面的setItem(0,0,**),的位置恰好就是索引的第0行第0列,因此把item one 占用了,item three的索引是第3行第0列,故item two和item three中间会隔一行。
如果想在item two 条目下再生成子条目,此时用的对象就不是model来添加而是子条目,而是用item two对象来添加子条目,函数也是appendRow,也可以用setChild函数。由上面的实现,发现本人用的是匿名对象设置条目,比如我们想获取项目名为item two的条目对象,假设我们知道item two的行号为0行则可以model->item(0);获取该对象,然后该对象再添加子条目,实现例子如下:
model->item(0)->appendRow(new QStandardItem(tr("item four")));
效果如下:
假设我们只知道有个item two的条目,却不知道行号,我们通过函数实现搜索到该对象,该函数原型是:
QList
该函数的返回值是一个list链表,故我们可以通过遍历每个对象来获取它,因为我们上面的历程只有一个item two,故这个链表的长度为1.实现参考如下:
QList list = model->findItems(tr("item two"));
for(int i = 0;itext());//打印该条目的文本
}
效果在调试终端显示如下:
如果要搜索到item four,由于它是条目的子条目,即item four是item two的子条目,所以调用的对象是条目对象而不是model对象。原理和model类似,QStandardItem对象常见的函数有:
int QStandardItem::row() const返回该条目所在父条目的行号
QStandardItem *QStandardItem::parent() const返回父条目对象
接下来如果想在item two后面第二列即相关信息1或相关信息2列那添加条目,综合上述的代码添加最下几行代码如下:
model = new QStandardItemModel(ui->treeView);
ui->treeView->setModel(model);
model->setHorizontalHeaderLabels(QStringList()<appendRow(item);
model->setItem(0,0,new QStandardItem(tr("item two")));
model->setItem(2,0,new QStandardItem(tr("item three")));
model->item(0)->appendRow(new QStandardItem(tr("item four")));
QList list = model->findItems(tr("item two"));
/*以下是添加部分*/
for(int i = 0;isetItem(item->row(),1,new QStandardItem(tr("item two msg")));
}
效果如下:
若要在item four的的位置后面添加相关信息1,思路首先是获取item four这个条目对象,既然要获取item four这个对象,前提又要获取去item two这个条目对象,那如果不知item two的位置,因此用数据结构的思路写一个查询函数的代码,实现如下:
QStandardItem *Widget::getItem(QStandardItemModel *model, QString s)
{
QStandardItem *getitem = NULL;
if(!model->hasChildren())//判断是否有孩子,没有则返回0
return NULL;
QList list = model->findItems(s);
qDebug() << tr("list is %1").arg(list.length());
if(list.length() == 0)//如果链表长度为0,即没找到文本为s的条目
{
//将搜索子条目是否存在文本为s的条目
for(int i = 0;i < model->rowCount()&& getitem == NULL;i++)//遍历model下的所有条目,如果getitem有获得对象,则退出循环
{
getitem = getItem(model->item(i),s);//寻找第i行条目下的子条目列中是否存在文本为s的条目。
}
}
else
{
return list.at(0);
}
return getitem;
}
QStandardItem *Widget::getItem(QStandardItem *item, QString s)
{
if(item == NULL)
return NULL;
qDebug() << tr("fine %1").arg(item->text());
QStandardItem *getitem = NULL;
if(item->text().compare(s) == 0)
return item;
if(!item->hasChildren())//判断是否有孩子,没有则返回0
return NULL;
for(int i = 0;i < item->rowCount() && getitem == NULL;i++)//遍历item下所有子条目,若果getitme有获得对象,则退出循环
{
QStandardItem * childitem = item->child(i);
getitem = getItem(childitem,s);//寻找这个子条目的所有子条目是否存在文本为s的条目。
}
return getitem;
}
两个函数的关系是重载关系,参数不同,以实现递归查找,注意所有项目名条目中不能有两个或两个以上的相同的文本,不然以上函数只能返回其中一个最先找的文本为s的条目对象。如要找到多个s,则返回的对象应该是个链表,如有兴趣大家自己去实现吧。
测试一下用例,代码如下:
QStandardItem * getitem = getItem(model,tr("item four"));
getitem->parent()->setChild(getitem->row(),1,new QStandardItem(tr("item four msg")));
效果如下:
分析结果该函数实现过程正常。