Qt使用的QTreeView对象实现树状链表简单显示。

简言:

  本人并不专研Qt,只是写小项目时遇到问题和为了解决这个某个问题,特意学习了QTreeView的知识点。本人目的是为了实现一个显示网络数状拓扑结构图,简单的说就是实现树形结构链表,对比二叉树,我们知道二叉树中一个节点的最近子节点只有两个,一个左孩子和一个右孩子。但是本人的需求是每个节点都能有0个或0个以上的节点,故在寻找解决问题时偶然看到了QTreeView,因本人图形界面实现只学了Qt基础,所以选择用它。下图是本人写小项目的结果如下。

 学习心得:

我们在Qt designer下拖入这个窗体Tree View

接下来需要一个模型对象QStandardItemModel,初始化时要把该对象导入该TreeView窗体中,同时要把这个对象的头文件包含进来,例子如下:

model = new QStandardItemModel(ui->treeView);//创建模型
ui->treeView->setModel(model);//导入模型

有时我们的需求会有每列都应该有个标签在头顶,实现方式如下:

model->setHorizontalHeaderLabels(QStringList()<<QStringLiteral("项目名")<<QStringLiteral("相关信息1")<<QStringLiteral("相关信息2"));

此时构建执行的效果:

尝试在里面添加第一个条目,需要一个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<QStandardItem *> QStandardItemModel::findItems(const QString &text, Qt::MatchFlags flags = Qt::MatchExactly, int column = 0) 

该函数的返回值是一个list链表,故我们可以通过遍历每个对象来获取它,因为我们上面的历程只有一个item two,故这个链表的长度为1.实现参考如下:

QList<QStandardItem*> list = model->findItems(tr("item two"));
for(int i = 0;i<list.length();i++)
{
    qDebug() << tr("list has ").append(list.at(i)->text());//打印该条目的文本
}

效果在调试终端显示如下:

如果要搜索到item four,由于它是条目的子条目,即item four是item two的子条目,所以调用的对象是条目对象而不是model对象。原理和model类似,QStandardItem对象常见的函数有:

  • QStandardItem *QStandardItem::child(int row, int column = 0) 返回子条目列中的某行或某行某列的一个条目,model的函数是Item。
  • bool QStandardItem::hasChildren() 判断返回是否有子条目,model的函数是hasItem
  • int QStandardItem::rowCount() 返回有多少个子条目
  • int QStandardItem::row() const返回该条目所在父条目的行号

  • QStandardItem *QStandardItem::parent() const返回父条目对象

 接下来如果想在item two后面第二列即相关信息1或相关信息2列那添加条目,综合上述的代码添加最下几行代码如下:

model = new QStandardItemModel(ui->treeView);
ui->treeView->setModel(model);
model->setHorizontalHeaderLabels(QStringList()<<QStringLiteral("项目名")<<QStringLiteral("相关信息1")<<QStringLiteral("相关信息2"));
QStandardItem * item = new QStandardItem(tr("item one"));
model->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<QStandardItem*> list = model->findItems(tr("item two"));
/*以下是添加部分*/
for(int i = 0;i<list.length();i++)//list.length在本历程中返回值是 1 ,故只遍历一次
{
    //item变量指向这个Item two这个对象
    QStandardItem* item = list.at(i);
    /*参数1:item-row()是获取在上一级model或条目下自己所在的行号
      参数2: 1  是第1列,从第0列开始计数,故是相关信息1下的条目列
      参数3: 是创建一个对象,文本信息是item two msg*/
    model->setItem(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<QStandardItem*> 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")));

效果如下:

分析结果该函数实现过程正常。 

  • 20
    点赞
  • 86
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值