Senior Dogsbody Engineer  

背景

QML TreeView 是从QtQuick.Controls 1.4开始引入的,在QtQuick.Controls 1.3里并不支持,所以不得不自定义一个TreeView。

方法

可以用QtQuick.Controls 1.3里支持的ListView来实现TreeView的功能

 

  1 import QtQuick 2.5
  2 import QtQuick.Window 2.2
  3 import QtQuick.Controls 1.3
  4 import QtQuick.Controls.Styles 1.3
  5 import QtQml.Models 2.2
  6 import QtQuick.Layouts 1.1
  7 
  8 import IMTreeModel 1.0   //这是原来TreeView的model,在新的由ListView实现的TreeView里继续使用这个model,但需要将TreeModel转化成ListModel的形式
  9 
 10 Rectangle{ 
 11     IMTreeModel{
 12       id: treeModel
 13     }
 14 
 15     property int nodeLevel : 0
 16     property int appendedNodeNumberLevel0:0
 17     property int appendedNodeNumberLevel1:0
 18     property int appendedNodeNumberLevel2:0
 19     property int appendedNodeNumberLevel3:0
 20     property int appendedNodeNumberLevel4:0
 21     property int totalNodeNumberLevel0:0
 22     property int totalNodeNumberLevel1:0
 23     property int totalNodeNumberLevel2:0
 24     property int totalNodeNumberLevel3:0
 25     property int totalNodeNumberLevel4:0
 26     property int nodeIndex : 0
 27     property var indexArray : []
 28     property var selectedIndex
 29     property var selectedObjectNo : -1
 30     property var selectedObject
 31     property var filterIndex
 32 
 33     // 遍历TreeModel里的所有节点,把它们从tree的形式转化成list的形式
 34     function expandAll(index, model, update) 
 35     {
 36         var nodeName = treeModel.data(index, IMTreeModel.NAME).toString();
 37         var nodeType = treeModel.data(index, IMTreeModel.TYPE).toString();
 38         var nodeValue = treeModel.data(index, IMTreeModel.VALUE).toString();
 39 
 40         if(nodeLevel == 0)  // Root is level 0
 41         {
 42             if(!update) // Root is not leaf node, no value 
 43             {
 44                 listModel.append({"name":nodeName, "type":nodeType, "value":nodeValue, "level":nodeLevel, "index":nodeIndex++, "subNode":[]})  
 45                 indexArray.push(index)
 46             }
 47 
 48             appendedNodeNumberLevel0++
 49             if (model.hasChildren(index)) 
 50             {
 51                 nodeLevel++ 
 52                 totalNodeNumberLevel1 = model.rowCount(index) 
 53             }
 54         }
 55         else if(nodeLevel == 1)
 56         {
 57             if(update) //update the node
 58             {
 59                 if(listModel.get(appendedNodeNumberLevel0 - 1).subNode.get(appendedNodeNumberLevel1).value != nodeValue)
 60                 {
 61                     listModel.get(appendedNodeNumberLevel0 - 1).subNode.get(appendedNodeNumberLevel1).value = nodeValue
 62                 }
 63             }
 64             else //create the node
 65             {
 66                 listModel.get(appendedNodeNumberLevel0 - 1).subNode.append({"name":nodeName, "type":nodeType, "value":nodeValue, "level":nodeLevel, "index":nodeIndex++, "subNode":[]})  
 67                 indexArray.push(index)
 68             }
 69 
 70             appendedNodeNumberLevel1++
 71 
 72             if (model.hasChildren(index)) 
 73             {
 74                 nodeLevel++ 
 75                 totalNodeNumberLevel2 = model.rowCount(index) 
 76             }
 77             else
 78             {
 79                 if(totalNodeNumberLevel1 == appendedNodeNumberLevel1)
 80                 {
 81                     nodeLevel -- 
 82                     appendedNodeNumberLevel1 = 0
 83                 }
 84             }
 85         }
 86         else if(nodeLevel == 2)
 87         {
 88             if(update) //update the node
 89             {
 90                 if(listModel.get(appendedNodeNumberLevel0 - 1).subNode.get(appendedNodeNumberLevel1 - 1).subNode.get(appendedNodeNumberLevel2).value != nodeValue)
 91                 {
 92                     listModel.get(appendedNodeNumberLevel0 - 1).subNode.get(appendedNodeNumberLevel1 - 1).subNode.get(appendedNodeNumberLevel2).value = nodeValue
 93                 }
 94             }
 95             else //create the node
 96             {
 97                 listModel.get(appendedNodeNumberLevel0 - 1).subNode.get(appendedNodeNumberLevel1 - 1).subNode.append({"name":nodeName, "type":nodeType, "value":nodeValue, "level":nodeLevel, "index":nodeIndex++, "subNode":[]})  
 98                 indexArray.push(index)
 99             }
100             appendedNodeNumberLevel2++
101 
102             if (model.hasChildren(index)) 
103             {
104                 nodeLevel++ 
105                 totalNodeNumberLevel3 = model.rowCount(index)  
106             }
107             else
108             {
109                 if(totalNodeNumberLevel2 == appendedNodeNumberLevel2)
110                 {
111                     nodeLevel -- 
112                     appendedNodeNumberLevel2 = 0
113                 }
114             }
115         }
116         else if(nodeLevel == 3)
117         {
118             if(update) //update the node
119             {
120                 if(listModel.get(appendedNodeNumberLevel0 - 1).subNode.get(appendedNodeNumberLevel1 - 1).subNode.get(appendedNodeNumberLevel2 - 1).subNode.get(appendedNodeNumberLevel3).value != nodeValue)
121                 {
122                     listModel.get(appendedNodeNumberLevel0 - 1).subNode.get(appendedNodeNumberLevel1 - 1).subNode.get(appendedNodeNumberLevel2 - 1).subNode.get(appendedNodeNumberLevel3).value = nodeValue
123                 }
124             }
125             else //create the node
126             {
127                 listModel.get(appendedNodeNumberLevel0 - 1).subNode.get(appendedNodeNumberLevel1 - 1).subNode.get(appendedNodeNumberLevel2 - 1).subNode.append({"name":nodeName, "type":nodeType, "value":nodeValue, "level":nodeLevel, "index":nodeIndex++, "subNode":[]})  
128                 indexArray.push(index)
129             }
130 
131             appendedNodeNumberLevel3++
132 
133             if (model.hasChildren(index)) 
134             {
135                 nodeLevel++ 
136                 totalNodeNumberLevel4 = model.rowCount(index)  
137             }
138             else
139             {
140                 if(totalNodeNumberLevel3 == appendedNodeNumberLevel3)
141                 {
142                     nodeLevel -- 
143                     appendedNodeNumberLevel3 = 0
144                 }
145             }
146         }
147         else if(nodeLevel == 4)  // Level 4 is the last level
148         {
149             if(update) //update the node
150             {
151                 if(listModel.get(appendedNodeNumberLevel0 - 1).subNode.get(appendedNodeNumberLevel1 - 1).subNode.get(appendedNodeNumberLevel2 - 1).subNode.get(appendedNodeNumberLevel3 - 1).subNode.get(appendedNodeNumberLevel4).value != nodeValue)
152                 {
153                     listModel.get(appendedNodeNumberLevel0 - 1).subNode.get(appendedNodeNumberLevel1 - 1).subNode.get(appendedNodeNumberLevel2 - 1).subNode.get(appendedNodeNumberLevel3 - 1).subNode.get(appendedNodeNumberLevel4).value = nodeValue
154                 }
155             }
156             else //create the node
157             {
158                 listModel.get(appendedNodeNumberLevel0 - 1).subNode.get(appendedNodeNumberLevel1 - 1).subNode.get(appendedNodeNumberLevel2 - 1).subNode.get(appendedNodeNumberLevel3 - 1).subNode.append({"name":nodeName, "type":nodeType, "value":nodeValue, "level":nodeLevel, "index":nodeIndex++, "subNode":[]})  
159                 indexArray.push(index)
160             }
161 
162             appendedNodeNumberLevel4++ 
163 
164             //if (model.hasChildren(index)){}  //not needed because it is last level which means no chidren
165 
166             if(totalNodeNumberLevel4 == appendedNodeNumberLevel4)
167             {
168                 nodeLevel -- 
169                 appendedNodeNumberLevel4 = 0
170 
171                 if(totalNodeNumberLevel3 == appendedNodeNumberLevel3)
172                 {
173                     nodeLevel -- 
174                     appendedNodeNumberLevel3 = 0  
175                 }
176             }
177         }
178 
179         if (!model.hasChildren(index)) 
180             return
181 
182         var rows = model.rowCount(index)
183         for (var i = 0; i < rows; ++i)
184         {
185             expandAll(model.index(i, 0, index), model, update);
186         }
187     }
188      //把TreeModel转化为ListModel  
189      ListModel {  
190         id: listModel  
191         Component.onCompleted: {  
192             expandAll(treeModel.index(0, 0), treeModel, false) // false : create node tree
193         }  
194     }  
195 
196     Timer  //因为这个ListModel是由TreeModel转化而来,并不是真正的MVC形式,所以要手动实时更新ListView中的数据
197     {
198         id:modelUpdateTimer
199         interval: 1000
200         repeat: true
201         running:true
202         triggeredOnStart: true
203 
204         onTriggered:
205         {
206             nodeLevel = 0
207             appendedNodeNumberLevel0 = 0
208             appendedNodeNumberLevel1 = 0
209             appendedNodeNumberLevel2 = 0
210             appendedNodeNumberLevel3 = 0
211             appendedNodeNumberLevel4 = 0
212             totalNodeNumberLevel0 = 0
213             totalNodeNumberLevel1 = 0
214             totalNodeNumberLevel2 = 0
215             totalNodeNumberLevel3 = 0
216             totalNodeNumberLevel4 = 0
217             nodeIndex = 0
218 
219             expandAll(treeModel.index(0, 0), treeModel, true) // true : update node tree
220         }
221     }
222 
223    //Delegate  
224     Component {  
225         id: listDelegate  
226 
227         Column {  
228             id: listItem  
229             Rectangle {  
230                 id: rectLine
231                 width: listView.width  
232                 height: 20  
233                 color: index % 2 == 0 ? "white" : "#f7f5f3" 
234                         
235                 Connections{
236                     target: btnApplyFilter
237                     onClicked: {
238                         if(filterIndex == indexArray[index])
239                         {
240                             if(selectedObjectNo != -1)
241                             {
242                                 selectedObject.color = selectedObjectNo % 2 == 0 ? "white" : "#f7f5f3" 
243                             }
244 
245                             selectedObject = rectLine
246                             selectedObjectNo = index
247                             rectLine.color = '#308cc6'
248 
249                             selectedIndex = indexArray[model.index]
250                             fldValue.text = model.value        
251                             btnApplyValue.enabled = (model.value != "")     
252 
253                             filterIndex = ""            
254                             
255                             //set scrollbar position to make the selected item visible
256                             var pos = rectLine.height * index 
257                             if(pos > rectLine.height * indexArray.length - scrView.height)  
258                             {
259                                 pos = rectLine.height * indexArray.length - scrView.height
260                             }
261                             else if(pos < scrView.height)
262                             {
263                                 pos = 0
264                             }
265                             
266                             scrView.flickableItem.contentY = pos 
267                         }
268                     }
269                 }
270 
271                 Text {  
272                     id:tabName  
273                     width: 400  
274                     x : 5 + model.level * 15
275 
276                     anchors.leftMargin: 5  
277                     anchors.verticalCenter: parent.verticalCenter  
278                     text: (listItem.children.length > 2 ? listItem.children[1].visible ? qsTr("- ") : qsTr("+ ") : qsTr("  ")) + model.name
279                     elide: Text.ElideRight  
280                     color: "#202020"  
281                 }  
282                 Text {  
283                     id:tabType  
284                     width: 100 
285                     x: 400
286                     text: model.type  
287                     elide: Text.ElideRight  
288                     color: "#202020"  
289                 }                  
290                 Text {  
291                     id:tabValue
292                     width: 100 
293                     x: 500
294                     text: model.value  
295                     elide: Text.ElideRight  
296                     color: "#202020"  
297                 }                  
298                 MouseArea {  
299                     anchors.fill: parent  
300                     onClicked: {  
301                         for(var i = 1; i < listItem.children.length - 1; ++i) {  
302                             listItem.children[i].visible = !listItem.children[i].visible  
303                         }  
304 
305                         if(model.name == "Root")
306                         {
307                             tabName.text = (listItem.children[1].visible ? qsTr("- ") : qsTr("+ "))  + model.name
308                         }
309 
310                         if(selectedObjectNo != -1)
311                         {
312                             selectedObject.color = selectedObjectNo % 2 == 0 ? "white" : "#f7f5f3" 
313                         }
314 
315                         selectedObject = parent
316                         selectedObjectNo = index
317                         parent.color = '#308cc6'
318 
319                         selectedIndex = indexArray[model.index]
320                         fldValue.text = model.value        
321                         btnApplyValue.enabled = (model.value != "")     
322                     }  
323                 }  
324             }  
325               
326             Repeater {  
327                 id: repeater1
328                 model: subNode  
329                 delegate: listDelegate  
330             }  
331         }  
332     }  
333 
334     // Title of View
335     Rectangle {
336         id: recTitle
337         width: parent.width
338         height: 30
339         z:2
340         color: "#efebe7"
341 
342         RowLayout {
343             anchors.left: parent.left
344             anchors.verticalCenter: parent.verticalCenter
345             
346             Text {
347                 text: " Name"
348                 font.pixelSize: 18
349                 color: "#3c3c3c"
350                 Layout.preferredWidth: 390
351             }
352 
353             Text {
354                 text: "Type"
355                 font.pixelSize: 18
356                 color: "#3c3c3c"
357                 Layout.preferredWidth: 100
358             }
359 
360             Text {
361                 text: "Value"
362                 font.pixelSize: 18
363                 color: "#3c3c3c"
364             }
365         }            
366     }
367 
368     //View  
369     ScrollView{ //QtQuick.Controls 1.3里也不支持QML Scrollbar,所以在ListView外面用ScrollView包一层,这样就可以使用ScrollView自带的Scrollbar来实现ListView的滚动
370         id:scrView
371         y:recTitle.height
372         height: 520
373         width: parent.width
374 
375         ListView {  
376             id:listView  
377             anchors.fill: parent
378             model: listModel  
379             delegate: listDelegate 
380             focus: true 
381             clip: true
382         } 
383     }
384 
385     Text{
386         id:txtValue
387 
388         x:120
389         y:558
390         height: 25
391         text: "Value: "
392         horizontalAlignment:Text.AlignHCenter
393         verticalAlignment:Text.AlignVCenter
394     }
395 
396     TextField{
397         id:fldValue
398 
399         x:txtValue.x + txtValue.width
400         y:txtValue.y
401         height:txtValue.height
402     }
403 
404     Button{
405         id:btnApplyValue
406 
407         x:fldValue.x + fldValue.width + 2
408         y:fldValue.y
409         height:fldValue.height
410 
411         text:"Apply"
412         enabled: false
413 
414         onClicked:{
415             var nodeValue = treeModel.data(selectedIndex, IMTreeModel.VALUE).toString();
416             if(!treeModel.setData(selectedIndex, fldValue.text))
417             {
418                 fldValue.text = nodeValue    // if set data failed, restore old value
419             }
420         }
421     }
422 
423     Text{
424         id:txtFilter
425 
426         x:450
427         y:txtValue.y
428         height: txtValue.height
429         text: "Filter: "
430         horizontalAlignment:Text.AlignHCenter
431         verticalAlignment:Text.AlignVCenter
432     }
433 
434     TextField{
435         id:fldFilter
436 
437         x:txtFilter.x + txtFilter.width
438         y:txtFilter.y
439         height:txtFilter.height
440     }
441     
442     property bool bFound: false
443     
444     function findIndexByName( index, model, name)
445     {
446         var nodeName = treeModel.data(index, IMTreeModel.NAME).toString();
447         //console.log("==nodeName==", nodeName, index.row, index.column)
448         
449         var UC_NODENAME = nodeName.toUpperCase()
450         var UC_NAME = name.toUpperCase()
451 
452         if(UC_NODENAME.search(UC_NAME) != -1)  
453         {
454             bFound = true;
455             filterIndex = index
456             return;
457         }
458 
459         if (!model.hasChildren(index)) 
460         {
461             return;
462         }
463 
464         var rows = model.rowCount(index)
465         for (var i = 0; i < rows; i++)
466         { 
467             if(bFound)
468             {
469                 return;
470             }
471             
472             findIndexByName(model.index(i, 0, index), model, name);
473         }
474     }
475         
476     Button{
477         id:btnApplyFilter
478 
479         x:fldFilter.x + fldFilter.width + 2
480         y:fldFilter.y
481         height:fldFilter.height
482 
483         text:"Apply"
484         enabled:(fldFilter.text != "")
485         
486         onClicked:{
487             bFound = false;
488             findIndexByName(treeModel.index(0, 0), treeModel, fldFilter.text)
489         }            
490     }
491 }

 效果图:

 

posted on 2022-07-22 14:01  高级打杂工程师  阅读(1408)  评论(0编辑  收藏  举报