承接該文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該控件無關的了。上述方法可移植性強,可以用到有類似需求的地方。