承接该文http://www.cnblogs.com/Romi/archive/2012/04/16/2452709.html,在该文基础上继续讲解QTreeWidget控件的使用,同时解决该文最后留下的问题。
QTreeWidget是实现树形结构的类,在很多软件中都可以看到类似树形结构的界面。
我做的一个示例如下图,用来处理图像,最顶层节点是图像的路径名,子节点是图像的各个波段,双击各个波段会显示图像各波段的灰度图像,同时还有删除指定节点(父节点和子节点同时删除)的功能。效果如下所示
要完成这样的功能需要注意一下几点:
①.在内存中保存各个节点,当然要在堆上分配内存,删除节点时,除了去除QtreeWidget控件上的节点外,还要讲存储在内存中的节点也要删除,否则会出现内存泄露的问题。
②.节点双击的事件响应,准确定位到是哪个图像的哪个波段。
下面详细叙述。
1.变量
需要有个变量记录图像的路径名,这里定义一个容器,数据类型为QString
QVector<QString> imgFile;
2.定义信号和槽
需要三个槽,
打开菜单:每使用打开菜单打开一幅图像就将该图像的路径名和波段数设计成父节点和子节点添加到QTreeWidget控件中。
删除节点的按钮:删除指定节点(该节点处于高亮状态,即选中状态)
双击某波段:显示该波段的灰度图像。
这里着重讲解与QTreeWidget相关的,因此有关显示图像的内容概不论述。
信号与槽链接如下:
打开菜单 connect(ui.Open,SIGNAL(triggered()),this,SLOT(menu_Open()));
双击某波段 connect(ui.treeWidget,SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),this,SLOT(showSelectedImg(QTreeWidgetItem*,int)));
删除节点 connect(ui.DeleteNode,SIGNAL(clicked()),this,SLOT(btn_DeleteNode()));
3.打开图像(添加节点)
imgFile.append(fileName);//影像路径添加进容器 QTreeWidgetItem *item=new QTreeWidgetItem(ui.treeWidget,QStringList(QString(fileName)));//添加节点 //添加子节点 for (int i=0;i<rasterNum;i++) { QTreeWidgetItem *item1=new QTreeWidgetItem(item,QStringList(QString("Band")+QString::number(i+1))); item->addChild(item1); }
其中rasterNum为路径名为fileName的图像的波段数。
这样在QTreewidget部件对象treeWidget中就增加了树节点及其子节点。每次打开都会在树形节点最后面添加。
这里需要提醒一点:这里的QTreewidgetItem指针对象指向的地址都分配在堆上,会不会造成内存泄露呢?因为函数结束后作为局部变量的指针当然是消失了,但保存QTreeWidgetItem节点的内存地址还在,有没有办法在需要的时候将其内存地址释放掉呢,答案是肯定的,将在后面删除节点时论述。
4.双击某波段显示波段图像
QTreeWidgetItem *parent=item->parent();//获得父节点 if(NULL==parent) return; progessBar->setValue(0);//进度条置0 int row=parent->indexOfChild(item);//获得节点在父节点中的行号(从0开始) QString fileName=parent->text(0);//获得父节点的文本字符(即影像路径) /* QString->const char* */ QByteArray ba=fileName.toLocal8Bit(); const char* filePath=ba.data();
这里贴出的是找到图像路径和子节点波段的方法,至于显示出图像就是根据图像路径和波段号显示出波段响应灰度图像,该问题不在讨论范畴,略去。
5.删除节点
这里的删除节点不是删除所有节点,而是删除与该节点有关的图像的所有节点,比如,鼠标指向了最开始图中第二个父节点的任意一个子节点,则就将该父节点和所有子节点删除。
QTreeWidgetItem* item=ui.treeWidget->currentItem();//获得当前节点 if(NULL==item)//没有选择节点 return; QTreeWidgetItem* parent=item->parent();//获得当前节点的父节点 int index;//top节点的索引号 if(NULL==parent)//item就是top节点 { QString fileName=item->text(0);//获得top节点的文本字符(即影像路径) for (int i=0;i<imgFile.size();i++) { if (fileName==imgFile.at(i)) { index=i; break; } } ui.treeWidget->takeTopLevelItem(index);//去除节点 Removes the top-level item at the given index in the tree and returns it imgFile.remove(index);//移除容器index处内容 //释放掉存放节点的内存空间 int childCount=item->childCount();//子节点数 for (int i=0;i<childCount;i++) { QTreeWidgetItem* childItem=item->child(0); delete childItem; childItem=NULL; } delete item; item=NULL; } else//parent才是top节点 { QString fileName=parent->text(0);//获得top节点的文本字符(即影像路径) for (int i=0;i<imgFile.size();i++) { if (fileName==imgFile.at(i)) { index=i; break; } } ui.treeWidget->takeTopLevelItem(index);//去除节点 Removes the top-level item at the given index in the tree and returns it imgFile.remove(index);//移除容器index处内容 //释放掉存放节点的内存空间 int childCount=parent->childCount();//子节点数 for (int i=0;i<childCount;i++) { item=parent->child(0); delete item; item=NULL; } delete parent; parent=NULL; }
主要方法就是根据图像路径名获得图像在top节点中的索引号,然后将top节点及其子节点删除,采用takeTopLevelItem(index)方法可以把treeWidget中的所因为index的节点去除掉,注意这里只是将节点从treeWidget中去除掉,其节点(父节点+子节点)仍然存在内存中,所以还要讲内存中的地址也要释放掉,否则就会出现所谓的内存泄露的问题。所采用的方法时使用QTreeWidgetItem指针找到存放节点的内存地址,然后将其delete掉,如此就释放掉内存空间了(有疑问的可以跟踪调试下),一定注意将要删除的节点全部delete掉,先释放子节点内存,再释放父节点内存。
还需要注意的是,每清除掉一个子节点的内存空间,对应父节点就会失去该子节点,因此每次都是清理掉父节点索引为0的子节点,即获得child(0)的子节点。此外不要忘了保存图像路径的向量imgFile中也要去除对应的索引内容。
总结
以上给出的是实现功能的核心实现部分,并非完整的程序,其他未论述的都是与QTreeWidget该控件无关的了。上述方法可移植性强,可以用到有类似需求的地方。
转载:http://www.cnblogs.com/Romi/archive/2012/08/08/2628163.html