Jump to content

How to search in treeview (virtual)? It's too slow currently


Recommended Posts

Hi guys,

I need to search some key words in treeview, this treeview contains 10000 or up to 100000 lines, but I found that it's too slow to retrieve the result, I need the item and its full path result.

Can we:

Speed up the searching?
Stop the searching progress if I want?
Highlight the result item(s) in the treeview?
Expand the result item(s) in the treeview?
Have a progress control to see the search progress?

 

For example, if you search "zcyrjgwafv" in my script, it takes some seconds, it's too slow, if you search "zc", it's killing me


 

#include <GUIConstants.au3>
#include <WindowsConstants.au3>
#include <GuiTreeView.au3>
#include <StaticConstants.au3>
#include <ButtonConstants.au3>
#include <ListviewConstants.au3>
#include <EditConstants.au3>
#include <Misc.au3>
#include <ProgressConstants.au3>

Opt( "MustDeclareVars", 0)
Opt( "GUIOnEventMode", 0)

Global $tNMHDR, $hWndFrom, $iIDFrom, $iCode, $hGui
Global $idTreeView, $hTreeView, $aItems, $item, $txt, $root_parent
Global $msg, $show_all_file_new
Global $item_handle, $txt_1, $parent_1, $root_parent_1, $fullpath_1, $compare_result
Global Const $TREEVIEW_START = 1000


Showall()

Func Showall()


; Create GUI
$hGui = GUICreate( "Show", 400, 800, -1, -1, ($GUI_SS_DEFAULT_GUI) )

; Create TreeView
$filemenu = GUICtrlCreateMenu("File")
Global $fileitem_import = GUICtrlCreateMenuItem("Import", $filemenu)



$idTreeView = GUICtrlCreateTreeView( 0, 22, 400, 340, BitOR($GUI_SS_DEFAULT_TREEVIEW, $TVS_TRACKSELECT) )


$hTreeView = GUICtrlGetHandle( $idTreeView )


Global $searchitemBtn = GUICtrlCreateButton("Find item",5, 385, 70, 20, $BS_FLAT)
Global $searchfullBtn = GUICtrlCreateButton("Find path",5, 415, 70, 20, $BS_FLAT)
Global $sEARCHinput =   GUICtrlCreateInput("", 80, 385, 150, 20)


Global $edit_searchresult = GUICtrlCreateEdit("",5, 485,392, 290)


; Register WM_NOTIFY message handler To create the treeview item txt
GUIRegisterMsg( $WM_NOTIFY, "WM_NOTIFY" )
    ; Show GUI
    GUISetState(@SW_SHOW, $hGui)
    ; Loop
    While 1
       $Msg = GUIGetMsg()
        Switch $Msg
             Case $GUI_EVENT_CLOSE
                ExitLoop

             Case $fileitem_import
                _read_show_all_file()

             Case $searchitemBtn
                _searchitem()

             Case $searchfullBtn
                _searchpath()

        EndSwitch

    WEnd
    ; Cleanup
     GUIDelete( $hGui )

EndFunc


;read showall Mo file and transfer to #format
Func _read_show_all_file()

_GuiCtrlTreeView_DeleteAll($idTreeView)

$show_all_file_new = FileOpen(@scriptDir &"\" &"Treefile.txt")
 $aItems = FileReadToArray( $show_all_file_new )
 Global $iLineCount = @extended
;~  GUICtrlCreateLabel($iLineCount&"Lines", 550, 3)


  FileClose(@scriptDir &"\" &"Treefile.txt")

    ; Create TreeView structure

    CreateTreeView( $aItems )

    ; Expand all child items
;~     _GUICtrlTreeView_Expand( $hTreeView )

EndFunc

; Create TreeView structure
Func CreateTreeView( $aItems )

    ; TreeView level information
    Local $aLevels[100][2], $iLevel = 0, $iLevelPrev = 0
    ; $aLevels[$iLevel][0]/[1] contains the last item/parent of that level

    ; TreeView insert structure
    Local $tInsert = DllStructCreate( $tagTVINSERTSTRUCT ), $pInsert = DllStructGetPtr( $tInsert )
    DllStructSetData( $tInsert, "InsertAfter", $TVI_LAST )
    DllStructSetData( $tInsert, "Mask", $TVIF_HANDLE+$TVIF_PARAM+$TVIF_TEXT )
    DllStructSetData( $tInsert, "Text", -1 ) ; $LPSTR_TEXTCALLBACK


    ; Add TreeView root
    $aLevels[$iLevel][1] = NULL
    DllStructSetData( $tInsert, "Param", $TREEVIEW_START )

    DllStructSetData( $tInsert, "Parent", $aLevels[$iLevel][1] )
    $aLevels[$iLevel][0] = GUICtrlSendMsg( $idTreeView, $TVM_INSERTITEMW, 0, $pInsert )

    ; Add TreeView items
    For $i = 1 To UBound( $aItems ) - 1
        $iLevel = StringSplit( $aItems[$i], "#", 2 )[0]
        If $iLevel <> $iLevelPrev Then
            $aLevels[$iLevel][1] = $iLevel > $iLevelPrev ? $aLevels[$iLevelPrev][0] _ ; A child of the previous level
                                                         : GUICtrlSendMsg( $idTreeView, $TVM_GETNEXTITEM, $TVGN_PARENT, $aLevels[$iLevel][0] ) ; A sibling of the level
            $iLevelPrev = $iLevel
        EndIf
        DllStructSetData( $tInsert, "Param", $i+$TREEVIEW_START )
        DllStructSetData( $tInsert, "Parent", $aLevels[$iLevel][1] )
        $aLevels[$iLevel][0] = GUICtrlSendMsg( $idTreeView, $TVM_INSERTITEMW, 0, $pInsert )
     Next ; $aLevels[$iLevel][0]/[1] contains the last item/parent of that level


EndFunc


Func _searchitem()
   For $i = 0 To UBound( $aItems ) - 1
      Global $iLevel_item = StringSplit( $aItems[$i], "#", 2 )[1]
;~ If GUICtrlRead($sEARCHinput) <> 0 Then
       $compare_result = StringInStr( $iLevel_item, GUICtrlRead($sEARCHinput), 2)
;~    EndIf
If $compare_result <> 0 Then
;Determine full path of selected item in TreeView
     $item_handle = _GUICtrlTreeView_FindItem($idTreeView, $iLevel_item)
     $txt_1 = _GUICtrlTreeView_GetText($idTreeView, $item_handle)

    Do
         $parent_1 = _GUICtrlTreeView_GetParentHandle($idTreeView, $item_handle)
         $txt_1 = _GUICtrlTreeView_GetText($idTreeView, $parent_1) & "," & $txt_1
        $item_handle = $parent_1
    Until $parent_1 = 0
      $root_parent_1 = _GUICtrlTreeView_GetText($idTreeView, $parent_1)
      StringLen($root_parent_1)
      $fullpath_1=StringTrimLeft($txt_1,StringLen($root_parent_1)+1)

GUICtrlSetData($edit_searchresult,"", 1)
GUICtrlSetData($edit_searchresult,$iLevel_item&@CRLF, 1)

EndIf
Next
EndFunc

Func _searchpath()
   For $i = 0 To UBound( $aItems ) - 1
      Global $iLevel_item = StringSplit( $aItems[$i], "#", 2 )[1]
;~ If GUICtrlRead($sEARCHinput) <> 0 Then
       $compare_result = StringInStr( $iLevel_item, GUICtrlRead($sEARCHinput), 2)
;~    EndIf
If $compare_result <> 0 Then
;Determine full path of selected item in TreeView
     $item_handle = _GUICtrlTreeView_FindItem($idTreeView, $iLevel_item)
     $txt_1 = _GUICtrlTreeView_GetText($idTreeView, $item_handle)

    Do
         $parent_1 = _GUICtrlTreeView_GetParentHandle($idTreeView, $item_handle)
         $txt_1 = _GUICtrlTreeView_GetText($idTreeView, $parent_1) & "," & $txt_1
        $item_handle = $parent_1
    Until $parent_1 = 0
      $root_parent_1 = _GUICtrlTreeView_GetText($idTreeView, $parent_1)
      StringLen($root_parent_1)
      $fullpath_1=StringTrimLeft($txt_1,StringLen($root_parent_1)+1)


GUICtrlSetData($edit_searchresult,"", 1)
GUICtrlSetData($edit_searchresult,$fullpath_1&@CRLF, 1)

EndIf
Next
EndFunc


Func WM_NOTIFY( $hWnd, $iMsg, $wParam, $lParam )
;For adding item's text
    Local $tNMHDR = DllStructCreate( $tagNMHDR, $lParam )
    Switch HWnd( DllStructGetData( $tNMHDR, "hWndFrom" ) )
        Case $hTreeView
            Switch DllStructGetData( $tNMHDR, "Code" )
                Case $TVN_GETDISPINFOW ; Display TreeView item text
                    Local Static $tBuffer = DllStructCreate( "wchar Text[1000]" ), $pBuffer = DllStructGetPtr( $tBuffer )

                    Local $tDispInfo = DllStructCreate( $tagNMTVDISPINFO, $lParam ), $sText = StringSplit( $aItems[DllStructGetData($tDispInfo,"Param")-$TREEVIEW_START], "#", 2 )[1]


                    DllStructSetData( $tBuffer, "Text", $sText )
                    DllStructSetData( $tDispInfo, "Text", $pBuffer )
                    DllStructSetData( $tDispInfo, "TextMax", 2 * StringLen( $sText ) + 2 )
            EndSwitch
    EndSwitch
    #forceref $hWnd, $iMsg, $wParam


EndFunc

 

 

Treefile.txt

Link to comment
Share on other sites

Why do you search in the tree when you first import the data from a text-file?

Maybe additionally add the data to a scripting dictionary object (key = text, value = tree item id) while importing, and the results should be nearly instantly.

Link to comment
Share on other sites

But how to show the full path (root,parent,child) result?

37 minutes ago, KaFu said:

Why do you search in the tree when you first import the data from a text-file?

Maybe additionally add the data to a scripting dictionary object (key = text, value = tree item id) while importing, and the results should be nearly instantly.

 

Link to comment
Share on other sites

I agree with @KaFu. You would need to build the scripting dictionary within the CreateTreeView function as you're already looking for children and siblings.  Just add parents in a string separated with "|"

BTW, use ByRef when passing large arrays like $aItems in CreateTreeView.  It will prevent making a useless copy of the array.

Link to comment
Share on other sites

I see this func can be used for comparing/searching, but the keyword must be 100% matching with the item, otherwise it returns failure. Is it possible to change?

; Returns true if a specified key exists in the Dictionary object, false if it does not.
Func _ItemExists($v_key)
    Return $oDictionary.Exists ($v_key)
EndFunc   ;==>_ItemExists

 

Link to comment
Share on other sites

If you have a really large number of entries and need to perform efficient more or less sophisticated queries on it, then I'd switch to a database; immediate candidate is SQLite, with its AutoIt support.

This implies there is a significant first step to learn and implement something suited to your needs, but it will pay on the long run.

This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.
Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe here
RegExp tutorial: enough to get started
PCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta.

SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.
SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.
An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.
SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)
A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!
SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt)

Link to comment
Share on other sites

SQLite is a bit complex, I think "_ArraySearch" is what I wanted, still need to solve the loop through and look up each item, if it has more items are matching.

But the big problem is to return the whole path which is including the searched item from the treeview, it takes too much time. Do you have any idea to make it faster?

Link to comment
Share on other sites

I agree that SQLite seems a bit heavylifting, but here's an example of a hierarchical tree queried in SQL:

-- make a "who works for whom?" table
CREATE TABLE org(
  name TEXT PRIMARY KEY,
  boss TEXT REFERENCES org
) WITHOUT ROWID;
INSERT INTO org VALUES('Alice',NULL),('Bob','Alice'),('Cindy','Alice'),('Dave','Bob'),('Emma','Bob'),('Fred','Cindy'),('Gail','Cindy'),
('John','Dave'),('Pete','Gail'),('Vlad','Pete'),('Paul','Gail');

-- query that table "depth-first"
WITH RECURSIVE
  under_alice(level,name) AS (
    VALUES(0,'Alice')
    UNION ALL
    SELECT under_alice.level+1, org.name
      FROM org JOIN under_alice ON org.boss=under_alice.name
     ORDER BY 2 DESC
  )
SELECT level || '#' || name Input FROM under_alice;

-- result is a tree similar to what you work with:

Input
0#Alice
1#Cindy
2#Gail
3#Pete
4#Vlad
3#Paul
2#Fred
1#Bob
2#Emma
2#Dave
3#John

-- now query the tree of bosses from someone whose name contains 'la'
WITH RECURSIVE
  who(name,boss) as (select name,boss from org where name like '%la%'),  
  over_name(name,boss) AS (
    select name,boss from who
    UNION ALL
    SELECT org.name, org.boss
      FROM org JOIN over_name ON org.name = over_name.boss
  )
select name || '.' || bosses Hierarchy from ((select name from who) N, (SELECT group_concat(boss,'.') bosses FROM over_name));

-- result is:
Hierarchy
Vlad.Pete.Gail.Cindy.Alice

So yes this isn't completely obvious to someone who isn't used to SQL but it's perfectly workable, and very fast even on large tables.

Of course you'll have to create an index on boss to speed up real-world searches.

Needless to say this is just a toy example!

This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.
Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe here
RegExp tutorial: enough to get started
PCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta.

SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.
SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.
An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.
SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)
A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!
SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt)

Link to comment
Share on other sites

This should do it, with simplified search functions, results display immediately :

#include <Array.au3>
#include <GUIConstants.au3>
#include <GuiTreeView.au3>

Opt( "MustDeclareVars", 1)

Global $hGui, $idTreeView, $hTreeView, $aItems, $item, $txt, $root_parent
Global $msg, $show_all_file_new
Global $item_handle, $txt_1, $parent_1, $root_parent_1, $fullpath_1, $compare_result
Global Const $TREEVIEW_START = 1000

Showall()

;==============================================
Func Showall()

    ; Create GUI
    $hGui = GUICreate( "Show", 400, 800)

    ; Create TreeView
    Local $filemenu = GUICtrlCreateMenu("File")
    Global $fileitem_import = GUICtrlCreateMenuItem("Import", $filemenu)
    $idTreeView = GUICtrlCreateTreeView( 0, 22, 400, 340, BitOR($GUI_SS_DEFAULT_TREEVIEW, $TVS_TRACKSELECT) )
    $hTreeView = GUICtrlGetHandle( $idTreeView )
    Global $searchitemBtn = GUICtrlCreateButton("Find item",5, 385, 70, 20, $BS_FLAT)
    Global $searchfullBtn = GUICtrlCreateButton("Find path",5, 415, 70, 20, $BS_FLAT)
    Global $sEARCHinput =   GUICtrlCreateInput("", 80, 385, 150, 20)
    Global $edit_searchresult = GUICtrlCreateEdit("",5, 485,392, 290)

    ; Register WM_NOTIFY message handler to create the treeview item txt
    GUIRegisterMsg( $WM_NOTIFY, "WM_NOTIFY" )

    ; Show GUI
    GUISetState(@SW_SHOW, $hGui)

    ; Loop
    While 1
       $Msg = GUIGetMsg()
        Switch $Msg
             Case $GUI_EVENT_CLOSE
                ExitLoop

             Case $fileitem_import
                _read_show_all_file()

             Case $searchitemBtn
                _searchitem()

             Case $searchfullBtn
                _searchpath()
        EndSwitch
    WEnd

    ; Cleanup
     GUIDelete( $hGui )

EndFunc

;========================================================================
Func _read_show_all_file() ; read showall Mo file and transfer to #format

    _GuiCtrlTreeView_DeleteAll($idTreeView)

    $show_all_file_new = FileOpen(@scriptDir &"\" &"Treefile.txt")
    $aItems = FileReadToArray( $show_all_file_new )
     Global $iLineCount = @extended
    ;~  GUICtrlCreateLabel($iLineCount&"Lines", 550, 3)
    FileClose(@scriptDir &"\" &"Treefile.txt")

    ; _ArrayDisplay($aItems, "$aItems")

    ; Create TreeView structure
    CreateTreeView( $aItems )

    ; Expand all child items
    ;~ _GUICtrlTreeView_Expand( $hTreeView )

EndFunc

;=========================================================
Func CreateTreeView( $aItems ) ; Create TreeView structure

    ; TreeView level information
    Local $aLevels[100][2], $iLevel = 0, $iLevelPrev = 0
    ; $aLevels[$iLevel][0]/[1] contains the last item/parent of that level

    ; TreeView insert structure
    Local $tInsert = DllStructCreate( $tagTVINSERTSTRUCT ), $pInsert = DllStructGetPtr( $tInsert )
    DllStructSetData( $tInsert, "InsertAfter", $TVI_LAST )
    DllStructSetData( $tInsert, "Mask", $TVIF_HANDLE+$TVIF_PARAM+$TVIF_TEXT )
    DllStructSetData( $tInsert, "Text", -1 ) ; $LPSTR_TEXTCALLBACK

    ; Add TreeView root
    $aLevels[$iLevel][1] = NULL
    DllStructSetData( $tInsert, "Param", $TREEVIEW_START )

    DllStructSetData( $tInsert, "Parent", $aLevels[$iLevel][1] )
    $aLevels[$iLevel][0] = GUICtrlSendMsg( $idTreeView, $TVM_INSERTITEMW, 0, $pInsert )

    ; Add TreeView items
    Global $aPath[UBound($aItems)] ; very bad to declare Global in function (my bad !)
    Local $sPath = ""
    $aPath[0] = "Root"

    For $i = 1 To UBound( $aItems ) - 1
        $iLevel = StringSplit( $aItems[$i], "#", 2 )[0]

        Select
            Case $iLevel = $iLevelPrev ; test most frequent first
                ; $sPath = $sPath

            Case $iLevel > $iLevelPrev
                $aLevels[$iLevel][1] = $aLevels[$iLevelPrev][0] ; A child of the previous level
                $iLevelPrev = $iLevel
                $sPath = $aPath[$i - 1]

            Case Else ; $iLevel < $iLevelPrev
                $aLevels[$iLevel][1] = GUICtrlSendMsg( $idTreeView, $TVM_GETNEXTITEM, $TVGN_PARENT, $aLevels[$iLevel][0] ) ; A sibling of the level
                $iLevelPrev = $iLevel
                $sPath = StringLeft($sPath, StringInStr($sPath, "\", Default, $iLevel) - 1)
        EndSelect

        $aPath[$i] = $sPath & "\" & StringSplit( $aItems[$i], "#", 2 )[1]

        DllStructSetData( $tInsert, "Param", $i+$TREEVIEW_START )
        DllStructSetData( $tInsert, "Parent", $aLevels[$iLevel][1] )
        $aLevels[$iLevel][0] = GUICtrlSendMsg( $idTreeView, $TVM_INSERTITEMW, 0, $pInsert )
    Next ; $aLevels[$iLevel][0]/[1] contains the last item/parent of that level

    ; _ArrayDisplay($aLevels, "$aLevels")
    ; _ArrayDisplay($aPath, "$aPath")

EndFunc

;==============================================
Func _searchitem()

    Local $sSearch = GUICtrlRead($sEARCHinput)
    For $i = 0 To UBound( $aItems ) - 1
        If StringInStr( $aItems[$i], $sSearch) Then
            GUICtrlSetData($edit_searchresult, "", 1)
            GUICtrlSetData($edit_searchresult, StringSplit( $aItems[$i], "#", 2 )[1] & @CRLF, 1)
        EndIf
    Next

EndFunc

;==============================================
Func _searchpath()

    Local $sSearch = GUICtrlRead($sEARCHinput)
    For $i = 0 To UBound( $aItems ) - 1
        If StringInStr( $aItems[$i], $sSearch) Then
            GUICtrlSetData($edit_searchresult,"", 1)
            GUICtrlSetData($edit_searchresult, $aPath[$i] & @CRLF, 1)
        EndIf
    Next

EndFunc

;==============================================
Func WM_NOTIFY( $hWnd, $iMsg, $wParam, $lParam )
    ; For adding item's text

    #forceref $hWnd, $iMsg, $wParam
    Local $tNMHDR = DllStructCreate( $tagNMHDR, $lParam )
    Switch HWnd( DllStructGetData( $tNMHDR, "hWndFrom" ) )
        Case $hTreeView
            Switch DllStructGetData( $tNMHDR, "Code" )
                Case $TVN_GETDISPINFOW ; Display TreeView item text
                    Local Static $tBuffer = DllStructCreate( "wchar Text[1000]" ), $pBuffer = DllStructGetPtr( $tBuffer )
                    Local $tDispInfo = DllStructCreate( $tagNMTVDISPINFO, $lParam ), $sText = StringSplit( $aItems[DllStructGetData($tDispInfo,"Param")-$TREEVIEW_START], "#", 2 )[1]
                    DllStructSetData( $tBuffer, "Text", $sText )
                    DllStructSetData( $tDispInfo, "Text", $pBuffer )
                    DllStructSetData( $tDispInfo, "TextMax", 2 * StringLen( $sText ) + 2 )
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG

EndFunc

A pic to show what's inside the array $aPath :

TreeView.png.64cc9acace4d76141e3e1e90a5ccec4c.png

Good luck
 

Edited by pixelsearch
typo
Link to comment
Share on other sites

Funny, I did nearly the same using a string instead of an array - much dirtier of course :idiot:
This one expands & allows partial searches like "zcy" which may return several results (searching "zc" works too but shows a funny animation in the treeview)

#include <GUIConstants.au3>
#include <WindowsConstants.au3>
#include <GuiTreeView.au3>
#include <StaticConstants.au3>
#include <ButtonConstants.au3>
#include <ListviewConstants.au3>
#include <EditConstants.au3>
#include <Misc.au3>
#include <ProgressConstants.au3>

Opt( "MustDeclareVars", 0)
Opt( "GUIOnEventMode", 0)

Global $tNMHDR, $hWndFrom, $iIDFrom, $iCode, $hGui
Global $idTreeView, $hTreeView, $aItems, $item, $txt, $root_parent
Global $msg, $show_all_file_new
Global $item_handle, $txt_1, $parent_1, $root_parent_1, $fullpath_1, $compare_result
Global Const $TREEVIEW_START = 1000

Global $ref_txt

Showall()

Func Showall()

; Create GUI
$hGui = GUICreate( "Show", 400, 800, -1, -1, ($GUI_SS_DEFAULT_GUI) )

; Create TreeView
$filemenu = GUICtrlCreateMenu("File")
Global $fileitem_import = GUICtrlCreateMenuItem("Import", $filemenu)

$idTreeView = GUICtrlCreateTreeView( 0, 22, 400, 340, BitOR($GUI_SS_DEFAULT_TREEVIEW, $TVS_TRACKSELECT) )
$hTreeView = GUICtrlGetHandle( $idTreeView )

Global $searchfullBtn = GUICtrlCreateButton("Find path",5, 415, 70, 20, $BS_FLAT)
Global $sEARCHinput =   GUICtrlCreateInput("", 80, 385, 150, 20)
Global $edit_searchresult = GUICtrlCreateEdit("",5, 485,392, 290)

GuiCtrlSetData($sEARCHinput,"zcyrjgwafv")

; Register WM_NOTIFY message handler To create the treeview item txt
GUIRegisterMsg( $WM_NOTIFY, "WM_NOTIFY" )

    ; Show GUI
    GUISetState(@SW_SHOW, $hGui)
    ; Loop
    While 1
       $Msg = GUIGetMsg()
        Switch $Msg
             Case $GUI_EVENT_CLOSE
                ExitLoop
             Case $fileitem_import
                _read_show_all_file()
              Case $searchfullBtn
                _searchpath_2()
        EndSwitch
    WEnd
    ; Cleanup
     GUIDelete( $hGui )

EndFunc


;read showall Mo file and transfer to #format
Func _read_show_all_file()

_GuiCtrlTreeView_DeleteAll($idTreeView)

$show_all_file_new = FileOpen(@scriptDir &"\" &"Treefile.txt")
 $aItems = FileReadToArray( $show_all_file_new )
 Global $iLineCount = @extended
;~  GUICtrlCreateLabel($iLineCount&"Lines", 550, 3)
FileClose(@scriptDir &"\" &"Treefile.txt")

    ; Create TreeView structure

    CreateTreeView( $aItems )

    ; Expand all child items
;~     _GUICtrlTreeView_Expand( $hTreeView )

EndFunc



; Create TreeView structure
Func CreateTreeView( $aItems )

    ; TreeView level information
    Local $aLevels[100][2], $iLevel = 0, $iLevelPrev = 0
    ; $aLevels[$iLevel][0]/[1] contains the last item/parent of that level

    $line = "Root"
    $ref_txt = ""

    ; TreeView insert structure
    Local $tInsert = DllStructCreate( $tagTVINSERTSTRUCT ), $pInsert = DllStructGetPtr( $tInsert )
    DllStructSetData( $tInsert, "InsertAfter", $TVI_LAST )
    DllStructSetData( $tInsert, "Mask", $TVIF_HANDLE+$TVIF_PARAM+$TVIF_TEXT )
    DllStructSetData( $tInsert, "Text", -1 ) ; $LPSTR_TEXTCALLBACK


    ; Add TreeView root
    $aLevels[$iLevel][1] = NULL
    DllStructSetData( $tInsert, "Param", $TREEVIEW_START )

    DllStructSetData( $tInsert, "Parent", $aLevels[$iLevel][1] )
    $aLevels[$iLevel][0] = GUICtrlSendMsg( $idTreeView, $TVM_INSERTITEMW, 0, $pInsert )

    ; Add TreeView items
    For $i = 1 To UBound( $aItems ) - 1
        $split = StringSplit( $aItems[$i], "#", 2 )
        $iLevel = $split[0]
        $item = $split[1]

    $line = StringRegExp($line, '((?:\|?\w+){' & $iLevel & '})', 1)[0] & "|" & $item
    $ref_txt &= $line & @crlf

        If $iLevel <> $iLevelPrev Then
            $aLevels[$iLevel][1] = $iLevel > $iLevelPrev ? $aLevels[$iLevelPrev][0] _ ; A child of the previous level
                                                         : GUICtrlSendMsg( $idTreeView, $TVM_GETNEXTITEM, $TVGN_PARENT, $aLevels[$iLevel][0] ) ; A sibling of the level

            $iLevelPrev = $iLevel
        EndIf

        DllStructSetData( $tInsert, "Param", $i+$TREEVIEW_START )
        DllStructSetData( $tInsert, "Parent", $aLevels[$iLevel][1] )
        $aLevels[$iLevel][0] = GUICtrlSendMsg( $idTreeView, $TVM_INSERTITEMW, 0, $pInsert )

     Next ; $aLevels[$iLevel][0]/[1] contains the last item/parent of that level

EndFunc


Func _searchpath_2()
    $find = GUICtrlRead($sEARCHinput)

    $compare_result = StringInStr( $ref_txt, $find, 2)
    If $compare_result = 0 Then Return 0

    $res = StringRegExp($ref_txt, '(?m)^(.*' & $find & '.*)$', 3)
    GUICtrlSetData($edit_searchresult, "", 1)

    For $i = 0 to UBound($res)-1
        $item = StringRegExp($res[$i], '(?:^|\|)([^\|]*' & $find & '[^\|]*)(?:\||$)', 1)[0]
        GUICtrlSetData($edit_searchresult, $find & @CRLF, 1)
        GUICtrlSetData($edit_searchresult, $item & @CRLF, 1)
        GUICtrlSetData($edit_searchresult, $res[$i] & @CRLF, 1)
        ControlTreeView($hGui, "", $idTreeView, "Select", $res[$i])
    Next
EndFunc


Func WM_NOTIFY( $hWnd, $iMsg, $wParam, $lParam )
;For adding item's text
    Local $tNMHDR = DllStructCreate( $tagNMHDR, $lParam )
    Switch HWnd( DllStructGetData( $tNMHDR, "hWndFrom" ) )
        Case $hTreeView
            Switch DllStructGetData( $tNMHDR, "Code" )
                Case $TVN_GETDISPINFOW ; Display TreeView item text
                    Local Static $tBuffer = DllStructCreate( "wchar Text[1000]" ), $pBuffer = DllStructGetPtr( $tBuffer )

                    Local $tDispInfo = DllStructCreate( $tagNMTVDISPINFO, $lParam ), $sText = StringSplit( $aItems[DllStructGetData($tDispInfo,"Param")-$TREEVIEW_START], "#", 2 )[1]


                    DllStructSetData( $tBuffer, "Text", $sText )
                    DllStructSetData( $tDispInfo, "Text", $pBuffer )
                    DllStructSetData( $tDispInfo, "TextMax", 2 * StringLen( $sText ) + 2 )
            EndSwitch
    EndSwitch
    #forceref $hWnd, $iMsg, $wParam

EndFunc

 

Link to comment
Share on other sites

Thank you all.

@pixelsearch, yes, it's very fast now, but for this declaration

$aPath[0] = "Root"

 however, if the Treefile.txt first few rows are :  (This is a common situation in my daily work, and I can't change every original file to start with "root")

0#hello
0#Root
1#abgajvqduj
1#abhrheeufg
1#abiyjmwrxk
.....

 

something interesting happens,

see the Row0, it's "Root", not "hello", yes, this is due to $aPath[0]="Root",

image.png.4b5a1f77d4cd74372957cd6d9cf8d8bf.png

 

it will lose some items in the full path, see the Row 68, actually it should be \Root\abutrygviv\agvyidwsak

image.png.f3d6dc4e7eded207bfa1e9c1951e20fc.png

 

Link to comment
Share on other sites

@September As you have several lines #0 in your new text file, then the script needs to be amended :

1 line to delete :

; $aPath[0] = "Root"

1 line to modify :

; $sPath = StringLeft($sPath, StringInStr($sPath, "\", Default, $iLevel) - 1)
$sPath = StringLeft($sPath, StringInStr($sPath, "\", Default, $iLevel + 1) - 1)

By the way, here is the correct way to close the text file :

; FileClose(@scriptDir &"\" &"Treefile.txt")
FileClose($show_all_file_new)

The preceding changes should solve the search (Row 68 for example) when your text file starts with :

0#Hello
0#Root
1#abgajvqduj
1#abhrheeufg
...

514590723_TreeView1.png.2e456f91c068602e7af702b9c4f6dbff.png

The following could help in case you have several "filled" roots, for example :

0#Hello
1#aaaaaaaaa1
2#aaaaaaaaa2
3#aaaaaaaaa3
3#aaaaaaaaa4
1#aaaaaaaaa5
0#Root
1#abgajvqduj
1#abhrheeufg
...

787368558_TreeView2.png.daf527e52683853d29fb1917ce076e29.png

I think the following loop in Func CreateTreeView() should always start from 0 and not 1, to be compatible with the different treefile texts discussed above :

; For $i = 1 To UBound( $aItems ) - 1
For $i = 0 To UBound( $aItems ) - 1

Good luck
 

Link to comment
Share on other sites

About this 0#Root thing, if the solution from pixelsearch is not enough, then you might in the script replace this

$show_all_file_new = FileOpen(@scriptDir &"\" &"Treefile.txt")
$aItems = FileReadToArray( $show_all_file_new )
FileClose(@scriptDir &"\" &"Treefile.txt")

by this

$read = FileRead(@scriptDir &"\" &"Treefile.txt") ; read the file
$read = StringRegExpReplace($read, '(?s)^(.*?)(?=0#Root)', "") ; remove all from begining up to 0#Root
$aItems = StringRegExp($read, '\N+', 3)  ; then build the aItems array

which works nice in his first script (and btw in mine too)

Edited by mikell
typo
Link to comment
Share on other sites

2 hours ago, September said:

how to remove the duplicate first row at treeview

By deleting the following line. Now that the loop always starts at 0 and not at 1, it will be created within the loop :

; $aLevels[$iLevel][0] = GUICtrlSendMsg( $idTreeView, $TVM_INSERTITEMW, 0, $pInsert )

 

2 hours ago, September said:

how to highlight and expand the search items at treeview?

By keeping the unique handles of each treeview item during their creation => array $aPath becomes 2D :

For $i = 0 To UBound( $aItems ) - 1
    ...
    $aPath[$i][1] = $aLevels[$iLevel][0] ; handle of the item
    ...
Next

Then use _GUICtrlTreeView_Expand and _GUICtrlTreeView_SetSelected during the search.

#include <Array.au3>
#include <GUIConstants.au3>
#include <GuiTreeView.au3>

Opt( "MustDeclareVars", 1)

Global $hGui, $idTreeView, $hTreeView, $aItems, $item, $txt, $root_parent, $aPath
Global $msg, $show_all_file_new
Global $item_handle, $txt_1, $parent_1, $root_parent_1, $fullpath_1, $compare_result
Global Const $TREEVIEW_START = 1000

Showall()

;==============================================
Func Showall()

    ; Create GUI
    $hGui = GUICreate( "Show", 400, 800)

    ; Create TreeView
    Local $filemenu = GUICtrlCreateMenu("File")
    Global $fileitem_import = GUICtrlCreateMenuItem("Import", $filemenu)
    $idTreeView = GUICtrlCreateTreeView( 0, 22, 400, 340, BitOR($GUI_SS_DEFAULT_TREEVIEW, $TVS_TRACKSELECT) )
    $hTreeView = GUICtrlGetHandle( $idTreeView )
    Global $searchitemBtn = GUICtrlCreateButton("Find item",5, 385, 70, 20, $BS_FLAT)
    Global $searchfullBtn = GUICtrlCreateButton("Find path",5, 415, 70, 20, $BS_FLAT)
    Global $sEARCHinput =   GUICtrlCreateInput("", 80, 385, 150, 20)
    Global $edit_searchresult = GUICtrlCreateEdit("",5, 485,392, 290)

    ; Register WM_NOTIFY message handler to create the treeview item txt
    GUIRegisterMsg( $WM_NOTIFY, "WM_NOTIFY" )

    ; Show GUI
    GUISetState(@SW_SHOW, $hGui)

    ; Loop
    While 1
       $Msg = GUIGetMsg()
        Switch $Msg
            Case $GUI_EVENT_CLOSE
                ExitLoop

             Case $fileitem_import
                _read_show_all_file()

             Case $searchitemBtn
                _searchitem()

             Case $searchfullBtn
                _searchpath()
        EndSwitch
    WEnd

    ; Cleanup
     GUIDelete( $hGui )

EndFunc

;========================================================================
Func _read_show_all_file() ; read showall Mo file and transfer to #format

    _GuiCtrlTreeView_DeleteAll($idTreeView)

    $show_all_file_new = FileOpen(@scriptDir &"\" &"Treefile.txt")

    $aItems = FileReadToArray( $show_all_file_new )
    Global $iLineCount = @extended
    ;~  GUICtrlCreateLabel($iLineCount&"Lines", 550, 3)
    FileClose($show_all_file_new)

    ; _ArrayDisplay($aItems, "$aItems")

    ; Create TreeView structure
    CreateTreeView( $aItems )

    ; Expand all child items
    ;~ _GUICtrlTreeView_Expand( $hTreeView )

EndFunc

;=========================================================
Func CreateTreeView( $aItems ) ; Create TreeView structure

    ; TreeView level information
    Local $aLevels[100][2], $iLevel = 0, $iLevelPrev = 0
    ; $aLevels[$iLevel][0]/[1] contains the last item/parent of that level

    ; TreeView insert structure
    Local $tInsert = DllStructCreate( $tagTVINSERTSTRUCT ), $pInsert = DllStructGetPtr( $tInsert )
    DllStructSetData( $tInsert, "InsertAfter", $TVI_LAST )
    DllStructSetData( $tInsert, "Mask", $TVIF_HANDLE+$TVIF_PARAM+$TVIF_TEXT )
    DllStructSetData( $tInsert, "Text", -1 ) ; $LPSTR_TEXTCALLBACK

    ; Add TreeView root
    $aLevels[$iLevel][1] = NULL
    DllStructSetData( $tInsert, "Param", $TREEVIEW_START )

    DllStructSetData( $tInsert, "Parent", $aLevels[$iLevel][1] )
    ; $aLevels[$iLevel][0] = GUICtrlSendMsg( $idTreeView, $TVM_INSERTITEMW, 0, $pInsert )

    ; Add TreeView items
    Dim $aPath[UBound($aItems)][2] ; $aPath already declared Global in main
    Local $sPath = ""

    For $i = 0 To UBound( $aItems ) - 1
        $iLevel = StringSplit( $aItems[$i], "#", 2 )[0] ; "0" to "7" in example "Treefile several roots.txt"

        Select
            Case $iLevel = $iLevelPrev ; test most frequent first
                ; $sPath = $sPath

            Case $iLevel > $iLevelPrev
                $aLevels[$iLevel][1] = $aLevels[$iLevelPrev][0] ; A child of the previous level
                $iLevelPrev = $iLevel
                $sPath = $aPath[$i - 1][0]

            Case Else ; $iLevel < $iLevelPrev
                $aLevels[$iLevel][1] = GUICtrlSendMsg( $idTreeView, $TVM_GETNEXTITEM, $TVGN_PARENT, $aLevels[$iLevel][0] ) ; A sibling of the level
                $iLevelPrev = $iLevel
                $sPath = StringLeft($sPath, StringInStr($sPath, "\", Default, $iLevel + 1) - 1)
        EndSelect

        $aPath[$i][0] = $sPath & "\" & StringSplit( $aItems[$i], "#", 2 )[1] ; ex. `\Root" or "\Root\abgajvqduj" ...

        DllStructSetData( $tInsert, "Param", $i+$TREEVIEW_START )
        DllStructSetData( $tInsert, "Parent", $aLevels[$iLevel][1] )
        $aLevels[$iLevel][0] = GUICtrlSendMsg( $idTreeView, $TVM_INSERTITEMW, 0, $pInsert )
        $aPath[$i][1] = $aLevels[$iLevel][0] ; handle of the item
    Next ; $aLevels[$iLevel][0]/[1] contains the last item/parent of that level

    ; _ArrayDisplay($aLevels, "$aLevels")

    ; _ArrayDisplay($aPath, "$aPath", Default, Default, Default, _
    ;   "Path|Handle*") ; * at end of any header means numeric sort for the concerned column.

EndFunc

;==============================================
Func _searchitem()

    Local $sSearch = GUICtrlRead($sEARCHinput)
    For $i = 0 To UBound( $aItems ) - 1
        If StringInStr($aItems[$i], $sSearch) Then
            GUICtrlSetData($edit_searchresult, "", 1)
            GUICtrlSetData($edit_searchresult, StringSplit( $aItems[$i], "#", 2 )[1] & @CRLF, 1)
            _GUICtrlTreeView_Expand($idTreeView, $aPath[$i][1], True)
            _GUICtrlTreeView_SetSelected($idTreeView, $aPath[$i][1], True)
        EndIf
    Next

EndFunc

;==============================================
Func _searchpath()

    Local $sSearch = GUICtrlRead($sEARCHinput)
    For $i = 0 To UBound( $aItems ) - 1
        If StringInStr($aItems[$i], $sSearch) Then
            GUICtrlSetData($edit_searchresult,"", 1)
            GUICtrlSetData($edit_searchresult, $aPath[$i][0] & @CRLF, 1)
            _GUICtrlTreeView_Expand($idTreeView, $aPath[$i][1], True)
            _GUICtrlTreeView_SetSelected($idTreeView, $aPath[$i][1], True)
        EndIf
    Next

EndFunc

;==============================================
Func WM_NOTIFY( $hWnd, $iMsg, $wParam, $lParam )
    ; For adding item's text

    #forceref $hWnd, $iMsg, $wParam
    Local $tNMHDR = DllStructCreate( $tagNMHDR, $lParam )
    Switch HWnd( DllStructGetData( $tNMHDR, "hWndFrom" ) )
        Case $hTreeView
            Switch DllStructGetData( $tNMHDR, "Code" )
                Case $TVN_GETDISPINFOW ; Display TreeView item text
                    Local Static $tBuffer = DllStructCreate( "wchar Text[1000]" ), $pBuffer = DllStructGetPtr( $tBuffer )
                    Local $tDispInfo = DllStructCreate( $tagNMTVDISPINFO, $lParam ), $sText = StringSplit( $aItems[DllStructGetData($tDispInfo,"Param")-$TREEVIEW_START], "#", 2 )[1]
                    DllStructSetData( $tBuffer, "Text", $sText )
                    DllStructSetData( $tDispInfo, "Text", $pBuffer )
                    DllStructSetData( $tDispInfo, "TextMax", 2 * StringLen( $sText ) + 2 )
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG

EndFunc

A search on "abvd" expands automatically like this :

1836110888_TreeView3.png.3e2245a7ffeea1340d6ffa39a75cf4d2.png

If several matches exist, then they're all automatically selected and expanded.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...