Что нового

[Элементы GUI] Загрузка данных из объемного текстового файла в TreeView

JSman

Знающий
Сообщения
22
Репутация
5
AutoIt: 3.
Версия: 1.0

Категория: Элементы GUI

Описание:
Указанный ниже код добавляет содержимое текстового файла в элемент интерфейса TreeView. Пример отличается от приведенных ранее тем, что в нем осуществляется загрузка объемных текстовых файлов.
К примеру, вы хотите отобразить на экране ранее сохраненную файловую структуру вашего ПК, а именно результаты выполнения команды из консоли DIR: cmd.exe /c chcp 1251 && cmd.exe /c && dir /B /S /A:-D *.* > YourFile.txt
Пример реализован по следующему алгоритму: при чтении текстового файла осуществляется его конвертирование в БД SQLite, затем динамически подгружаемые при открытии узла данные отображаются в TreeView. Обязательно оцените скорость подгрузки данных :smile:.

Код/Пример:
Код:
#include <GUIConstantsEx.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <TreeViewConstants.au3>
#include <GuiTreeView.au3>
#include <SQLite.au3>
;#include <SQLite.dll.au3>

AutoItSetOption("MustDeclareVars", 1)

If $CmdLine[0] = 0 Then
	Dim $s_FileDB = FileOpenDialog('Открыть список', @ScriptDir, 'База данных (*.db; *.txt)', 3)
	If @error Then Exit
Else
	$s_FileDB = $CmdLine[1]
EndIf

_SQLite_Startup()
if StringRegExp($s_FileDB, "\.(?i)txt$") Then PreLoad($s_FileDB)
 
Func PreLoad($FileDB)

Dim $Index=0
Global $CacheLength = 1
Global $Cache[300][2]
Dim $ParentIndex=0
Dim $k=0

$Cache[0][0]=0
$Cache[0][1]="<ROOT>"

Dim $file, $line

$file = FileOpen($FileDB,0)

Dim $t, $f, $lastpart
Dim $timer = TimerInit()
Dim $s_FileDB = $FileDB & ".db"

Dim $h_DB = _SQLite_Open($s_FileDB)
Dim $s_Text = 'BEGIN;DROP TABLE IF EXISTS TREE;CREATE TABLE TREE ([ID] INT, [NAME] Text, [PARENT_ID] INT, [FTYPE] INT);COMMIT;'
_SQLite_Exec($h_DB, $s_Text)
Dim $i=1
$s_Text = "BEGIN;"

Dim $tmp

While 1
   If (Mod($i,32768)=0) Then
   $s_Text &= 'COMMIT;'
   	_SQLite_Exec($h_DB, $s_Text) 
	$s_Text="BEGIN;"
   EndIf

   $line = FileReadLine($file)
   
   if $line="" Then ExitLoop
   If @error = -1 Then ExitLoop
 
   $tmp = StringRegExpReplace($line, "\\[^\\]+$", "")
  ; $tmp = StringLeft($line, StringInStr($line, '\', 2, -1))
   $lastpart = StringReplace($line, $tmp & "\", "")
   If $tmp <> $line and $CacheLength>0 and $tmp=$Cache[$CacheLength][1] Then
   $Index+=1
   $s_Text &= 'INSERT INTO TREE VALUES (' & $Index & ', "' & $lastpart & '", ' & $ParentIndex & ', 0);'
   ContinueLoop
   EndIf

   $f = StringSplit($line, "\")
   $t = ""

		For $k=1 to $f[0]

		 if ($k=$f[0]) Then 
		   $Index+=1
		   $s_Text &= 'INSERT INTO TREE VALUES (' & $Index & ', "' & $f[$k] & '", ' & $ParentIndex & ', 0);'
		   ExitLoop
		 EndIf
		
	    If $k<>1 Then $t&="\"
		$t&=$f[$k]
		
		 If $Cache[$k][1]=$t Then 
			  $ParentIndex = $Cache[$k][0]
			  ContinueLoop
		 EndIf

		 if ($k=1) Then $ParentIndex=0
		$Index+=1
		$Cache[$k][1]=$t
		$Cache[$k][0]=$Index
		$CacheLength = $k
		$s_Text &= 'INSERT INTO TREE VALUES (' & $Index & ', "' & $f[$k] & '", ' & $ParentIndex & ', 1);'
		$ParentIndex = $Index
	 Next
WEnd

ConsoleWrite("Время на создание базы данных " & Round(TimerDiff($timer) / 1000, 2) & @LF)

FileClose($file)
$s_Text &= 'COMMIT;'
_SQLite_Exec($h_DB, $s_Text)  
_SQLite_Close($h_DB)
$s_Text = ""

EndFunc
;--------------------------------------------------------------------------------------------

Global $hTreeView, $fExpanded = 0

Dim $TreeNodeCollection[1000]
Dim $FolderCollection[1000]
Dim $ExpandedCollection[1000]
Dim $CollectionLength = 0

Dim $Loaded[1000]
Dim $LoadedLength = 0
Dim $hGui = GUICreate("Explorer", 540, 560, -1, -1, $WS_OVERLAPPEDWINDOW)

Dim $CountElements = 0

$hTreeView = GUICtrlCreateTreeView(0, 0, 540, 560, BitOR($TVS_DISABLEDRAGDROP, $TVS_CHECKBOXES, $GUI_SS_DEFAULT_TREEVIEW, $TVS_SHOWSELALWAYS, $TVS_HASLINES), $WS_EX_CLIENTEDGE)
Opt("GUIDataSeparatorChar", "\")

Dim $hImage = _GUIImageList_Create(16, 16, 5, 1) ; Создаём список иконок
_GUIImageList_AddIcon($hImage, @SystemDir & '\shell32.dll', -4)
_GUIImageList_AddIcon($hImage, @SystemDir & '\shell32.dll', -5)
_GUIImageList_AddIcon($hImage, @SystemDir & '\shell32.dll', 0)
_GUICtrlTreeView_SetNormalImageList($hTreeView, $hImage)

Dim $E = '', $i = 1, $sExt, $ico1, $aE, $h_DB, $iMsg, $L

While 1
	$i += 1
	$sExt = RegEnumKey("HKCR", $i)
	If @error Or StringLeft($sExt, 1) <> '.' Then ExitLoop
	$ico1 = _FileDefaultIcon($sExt)
	If Not @error Then
		Switch UBound($ico1)
			Case 2
				If StringInStr(';.exe;.scr;.ico;.ani;.cur;', ';' & $sExt & ';') Then
					ContinueLoop
				Else
					_GUIImageList_AddIcon($hImage, $ico1[1], 0)
					If @error Then ContinueLoop
				EndIf
			Case 3
				_GUIImageList_AddIcon($hImage, $ico1[1], $ico1[2])
				If @error Then ContinueLoop
		EndSwitch
		$E &= '|' & $sExt
	EndIf
WEnd
$E = StringTrimLeft($E, 1)
$aE = StringSplit($E, '|') ; Создаём массив расширений, позиция которых совпадает с позицией иконок

$h_DB = _SQLite_Open($s_FileDB)
GUISetState()
GUIRegisterMsg($WM_NOTIFY, "WM_Notify_Events")
LoadTree(0, 1, True)
;ConsoleWrite("-----------------------" & @LF)

Dim $i1=0
While 1
	$iMsg = GUIGetMsg()
	Select
		Case $iMsg = $GUI_EVENT_CLOSE
			_Exit()
		Case $fExpanded = 1
			; node closed so reset all nodes to current states

			$L = $CollectionLength
			For $i1 = 1 To $L
				$ExpandedCollection[$i1] = _GUICtrlTreeView_GetExpanded($hTreeView, $TreeNodeCollection[$i1])
			Next
			$fExpanded = 0
		Case $fExpanded = 2
			; node opened so check which changed
			$L = $CollectionLength
			
			For $i1 = 1 To $L
			   If _GUICtrlTreeView_GetExpanded($hTreeView, $TreeNodeCollection[$i1]) <> $ExpandedCollection[$i1] Then
					Dim $DB_Index = GetDBIndexByTVHandle($TreeNodeCollection[$i1])
					;ConsoleWrite($i1)
				
					$ExpandedCollection[$i1] = True
					;ConsoleWrite("Expanded!" & $DB_Index & "  $i=" & $i1 & "  $TreeNodeCollection[$i1]=" & $TreeNodeCollection[$i1] &  "  " & TVGetPathByHandle($TreeNodeCollection[$i1]) & @LF)
					

					ReLoad($DB_Index)
					ExitLoop
				EndIf
				
			Next
			$fExpanded = 0
	EndSelect
WEnd

Func _FileDefaultIcon($sExt)
	If $sExt = '' Or StringInStr($sExt, ':') Then Return SetError(1)

	Local $aCall = DllCall("shlwapi.dll", "int", "AssocQueryStringW", _
			"dword", 0x00000040, _ ;$ASSOCF_VERIFY
			"dword", 15, _ ;$ASSOCSTR_DEFAULTICON
			"wstr", $sExt, _
			"ptr", 0, _
			"wstr", "", _
			"dword*", 65536)

	If @error Then Return SetError(1, 0, "")

	If Not $aCall[0] Then
		$sExt = StringReplace($aCall[5], '"', '')
		$sExt = StringSplit($sExt, ',')
		Opt('ExpandEnvStrings', 1)
		$sExt[1] = $sExt[1]
		Opt('ExpandEnvStrings', 0)
		Return SetError(0, 0, $sExt)
	ElseIf $aCall[0] = 0x80070002 Then
		Return SetError(1, 0, "{unknown}")
	ElseIf $aCall[0] = 0x80004005 Then
		Return SetError(1, 0, "{fail}")
	Else
		Return SetError(2, $aCall[0], "")
	EndIf
EndFunc   ;==>_FileDefaultIcon

Func __ArraySearch(ByRef $avArray, $vValue, $iStart = 0, $iEnd = 0)
   If $iEnd=0 Then $iEnd=Ubound($avArray)-1

   Dim $i=0, $found=false
	
   For $i=$iStart To $iEnd
	  
	  If ($avArray[$i]=$vValue) Then 
		 $found=true
		 ;ConsoleWrite($I & @LF)
		 ExitLoop
	  EndIf
   Next
	  
	  If not $Found Then 
		 SetError(1)
		 Return -1
	  EndIf
		 
	  Return $i
	  
EndFunc

Func DBGetIndexByName($name, $parent)
	Dim $hQuery, $aRow
	_SQLite_Query(-1, "SELECT * FROM TREE WHERE PARENT_ID='" & $parent & "' AND NAME='" & StringReplace($name, "'", "''") & "';", $hQuery)
	If _SQLite_FetchData($hQuery, $aRow) = $SQLITE_OK Then Return $aRow[0]
	Return -1
EndFunc   ;==>DBGetIndexByName

Func DBGetIndexPath($path)
	If $path = "" Then Return 0
	Dim $p = 0
	Dim $parts = StringSplit($path, "\", 1)
	For $i = 1 To $parts[0]
		$p = DBGetIndexByName($parts[$i], $p)
	Next
	Return $p
EndFunc   ;==>DBGetIndexPath

Func DBGetFiles($parent)
	Dim $Result[1000], $ar_index = 0, $CurrentLength = 1000
	Dim $hQuery, $aRow
	_SQLite_Query(-1, "SELECT * FROM TREE WHERE PARENT_ID='" & $parent & "' AND FTYPE='0';", $hQuery)
	While _SQLite_FetchData($hQuery, $aRow) = $SQLITE_OK
		$ar_index += 1
		if $ar_index >= $CurrentLength Then
			$CurrentLength += 1000
			ReDim $Result[$CurrentLength]
		EndIf
		$Result[$ar_index] = $aRow[1]
	WEnd
	$Result[0] = $ar_index
	;Redim $Result[$ar_index+1]
	Return $Result
EndFunc   ;==>DBGetFiles

Func DBGetFolders($parent)
	Dim $Result[1000], $ar_index = 0, $CurrentLength = 1000
	Dim $hQuery, $aRow
	_SQLite_Query(-1, "SELECT * FROM TREE WHERE PARENT_ID='" & $parent & "' AND FTYPE='1';", $hQuery)
	While _SQLite_FetchData($hQuery, $aRow) = $SQLITE_OK
		$ar_index += 1
		if $ar_index >= $CurrentLength Then
			$CurrentLength += 1000
			ReDim $Result[$CurrentLength]
		EndIf
		$Result[$ar_index] = $aRow[1]
	WEnd
	$Result[0] = $ar_index
	;Redim $Result[$ar_index+1]
	Return $Result
EndFunc   ;==>DBGetFolders

Func DBGetFoldersIndexes($parent)
	Dim $Result[1000], $ar_index = 0, $CurrentLength = 1000
	Dim $hQuery, $aRow
	_SQLite_Query(-1, "SELECT * FROM TREE WHERE PARENT_ID='" & $parent & "' AND FTYPE='1';", $hQuery)
	While _SQLite_FetchData($hQuery, $aRow) = $SQLITE_OK
		$ar_index += 1
		if $ar_index >= $CurrentLength Then
			$CurrentLength += 1000
			;ReDim $Result[$CurrentLength]
		EndIf
		$Result[$ar_index] = $aRow[0]
	WEnd
	$Result[0] = $ar_index
	;Redim $Result[$ar_index+1]
	Return $Result
EndFunc   ;==>DBGetFoldersIndexes

Func DBGetByIndex($index)
	Dim $hQuery, $aRow
	_SQLite_Query(-1, "SELECT * FROM TREE WHERE ID='" & $index & "' ;", $hQuery)
	If _SQLite_FetchData($hQuery, $aRow) = $SQLITE_OK Then Return $aRow
	Return -1
EndFunc   ;==>DBGetByIndex

Func DBGetParent($index)
	Dim $hQuery, $aRow
	$aRow = DBGetByIndex($index)
	Return DBGetByIndex($aRow[2])
EndFunc   ;==>DBGetParent

Func DBGetPathByIndex($index)
	Dim $hQuery, $part = DBGetByIndex($index), $_index = -2, $path = "", $ar

	If $part[3] = 0 Then
		$path = $part[1]
	EndIf

	While 1
		If $index = 0 Then ExitLoop
		$ar = DBGetByIndex($index)
		$index = $ar[2]

		$path = $ar[1] & "\" & $path

	WEnd

	Return $path
EndFunc   ;==>DBGetPathByIndex

Func TVGetHandleByPath($path)
	Opt("GUIDataSeparatorChar", "\")
	Return _GUICtrlTreeView_FindItemEx($hTreeView, $path, 0)
EndFunc   ;==>TVGetHandleByPath

Func TVGetPathByHandle($handle)
	Return _GUICtrlTreeView_GetTree($hTreeView, $handle)
EndFunc   ;==>TVGetPathByHandle

Func GetDBIndexByTVHandle($index)
	Dim $Result = __ArraySearch($TreeNodeCollection, $index, 0, $CollectionLength)

	If $CollectionLength = 0 Or $Result = -1 Then Return 0
	Return $FolderCollection[$Result]

EndFunc   ;==>GetDBIndexByTVHandle

Func GetTVHandleByDBIndex($DBIndex)
	Dim $Result = __ArraySearch($FolderCollection, $DBIndex, 0, $CollectionLength)
	;ConsoleWrite("GetTVHandleByDBIndex " & GetDBIndexByTVHandle($Result) & "  " & $DBIndex & @LF)
	If $Result <> -1 Then Return $TreeNodeCollection[$Result]
	Return -1

EndFunc   ;==>GetTVHandleByDBIndex

Func LoadDataToTV($ParentNodeHandle = 0) ;ToDo
	Dim $DB_Index = GetDBIndexByTVHandle($ParentNodeHandle)
	Dim $_Folders, $_Files, $Result[1000], $i = 0, $L

	Dim $ar = DBGetFoldersIndexes($DB_Index)
	$_Folders = DBGetFolders($DB_Index)

	Dim $Flag = False

	If $ar[0] > 1 Then
		If GetTVHandleByDBIndex($ar[1]) > -1 Then $Flag = True
	EndIf
	
	$L = $_Folders[0]
	
	If $L >= 1000 Then ReDim $Result[$L]
	For $i = 1 To $L
		If not $Flag Then
			$Result[$i] = AddFolder($_Folders[$i], $ParentNodeHandle, $ar[$i])
		else
			$Result[$i] = GetTVHandleByDBIndex($ar[$i])
		endIf
		
	Next
	
	$Result[0] = $L
	
	$LoadedLength += 1
	$Loaded[$LoadedLength] = $ParentNodeHandle
	$Loaded[0] = $LoadedLength

	
	If $Flag Then
		ConsoleWrite(Round(TimerDiff($timer) / 1000, 2) & @LF)
		Return $Result
	EndIf
	
	$_Files = DBGetFiles($DB_Index)
	$L = $_Files[0] 
	For $i = 1 To $L
		AddFile($_Files[$i], $ParentNodeHandle)
	Next
	
	Return $Result
EndFunc   ;==>LoadDataToTV

Func LoadTree($ParentNodeHandle, $lvl = 1, $force = False)
	If $lvl = -1 Then Return

	If Not $force Then
		If __ArraySearch($Loaded, $ParentNodeHandle, 1, $LoadedLength) <> -1 Then
			Return
		EndIf
	EndIf
    _GUICtrlTreeView_Delete($hTreeView, _GUICtrlTreeView_GetFirstChild($hTreeView, $ParentNodeHandle))
 	Dim $Dirs = LoadDataToTV($ParentNodeHandle)
	If $Dirs[0] = 0 Then Return

	For $i = 1 To $Dirs[0]
		LoadTree($Dirs[$i], $lvl - 1)
	 Next
 

EndFunc   ;==>LoadTree

Func AddFolder($name, $ParentNodeHandle = 0, $DB_Index = -1)
	Local $Result = _GUICtrlTreeView_InsertItem($hTreeView, $name, $ParentNodeHandle)
	;Local $Result = _GUICtrlTreeView_AddChild($hTreeView, $ParentNodeHandle, $name)
	
	$CountElements += 1
	
	$CollectionLength += 1
	
	if $CollectionLength >= Ubound($TreeNodeCollection) Then
		ReDim $TreeNodeCollection[Ubound($TreeNodeCollection) + 1000]
		ReDim $ExpandedCollection[Ubound($TreeNodeCollection) + 1000]
		ReDim $FolderCollection[Ubound($TreeNodeCollection) + 1000]
	EndIf

	$TreeNodeCollection[$CollectionLength] = $Result
	$ExpandedCollection[$CollectionLength] = False
	$FolderCollection[$CollectionLength] = $DB_Index
	;ConsoleWrite ($DB_Index & @LF)

    AddFile("..", $Result)

	Return $Result
EndFunc   ;==>AddFolder

Func AddFile($name, $parent = 0)
	Dim $tmp1 = StringRegExpReplace($name, '.*(\.\S+)', '\1') ; ищем расширение
	Dim $ind = __ArraySearch($aE, $tmp1) + 2 ; ищем расширение в масиве, чтобы определить индекс
	If @error Then $ind = 2 ; если нет расширения
	   $CountElements += 1
					
	_GUICtrlTreeView_InsertItem($hTreeView, $name, $parent, $ind, $ind, $ind)
	;_GUICtrlTreeView_AddChild($hTreeView, $parent, $name, $ind, $ind)
EndFunc   ;==>AddFile

Func ReLoad($DB_Index)
	Dim $timer = TimerInit()
	$CountElements = 0
	
	LoadTree(GetTVHandleByDBIndex($DB_Index), 0)
	
	; Или так
	
	;Dim $ar = DBGetFoldersIndexes($DB_Index)
	;Dim $L = $ar[0]
	;For $j = 1 To $L
;		LoadTree(GetTVHandleByDBIndex($ar[$j]), 0)
;	Next

	ConsoleWrite("Time " & Round(TimerDiff($timer) / 1000, 2) & "  Elements added " & $CountElements & @LF)
EndFunc   ;==>ReLoad

Func WM_Notify_Events($hWndGUI, $iMsgID, $wParam, $lParam)

	#forceref $hWndGUI, $iMsgID
	Local $tagNMHDR = DllStructCreate("int;int;int;int", $lParam)
	If @error Then Return
	Local $iEvent = DllStructGetData($tagNMHDR, 3)
	Select
		Case $wParam = $hTreeView
			Switch $iEvent
				Case $TVN_ITEMEXPANDEDW, $TVN_ITEMEXPANDEDA
					$fExpanded = DllStructGetData($tagNMHDR, 4) ; 1 = node closed, 2 = node expanded
			EndSwitch
	EndSelect
	$tagNMHDR = 0
	Return $GUI_RUNDEFMSG

EndFunc   ;==>WM_Notify_Events

Func _Exit()
	_SQLite_Close($h_DB)
	_SQLite_Shutdown()
	Exit
EndFunc   ;==>_Exit

Автор(ы): JSman
 

Ciber SLasH

Новичок
Сообщения
13
Репутация
0
Не понятно, что модифицировать в скрипте, чтобы:
0) в любое время, по нажатию <ESC> программа закрывалась. Сейчас, на длинных списках её даже не закрыть, пока не построится всё дерево
1) отображался ProgressBar при построении базы. На длинных списках долго ждать появления окна. Скармливал список содержимого диска "C:\" (126 000 строк): http://f-bit.ru/776623
2) у Root-каталога сразу отображался "+" и сразу был раскрыт. Сейчас, сначала отображается прочерк возле Root-каталога, как только мышью подводишь к окну - появляется "+".
 
Верх