On 9/24/22 4:30 AM, GeorgeB wrote:
>
> Context around the code : generate a tree structure (or something similar) by looking a two folders, filter on *.ini files and dump user selection to a file.
>
> Using tree.tcl I can achieve all the steps expect filter on *.ini files. I should be able to achieve all the objectives but I'm struggling to understand proc{} , how they operate.
>
> hence attempted to re-write.
>
> The need to filter on *.ini is because there is no way to add checkbox or is tricky to add with treeview (or so I read), I did find some examples but looks quite daunting.
>
> Hope this explains.
>
> I'll have a look at listbox.
>
> Thanks in advance.
Here is a working prototype based on your initial code. You can create
multiple trees displaying different folders, each with a different
filter. It has some extra features that may be helpful such as
auto-expansion for non-empty folders, color-coding, etc.
You can see how proc's help you organize your code and make it reusable.
I think *every* programming language has a similar construct.
----------------------------------------------------------------
package req Tk
proc populateRoots {tree ROOT SHOW_TYPES} {
populateTree $tree \
[$tree insert {} end -text "Network File" \
-values [list $ROOT directory] \
-open 1] \
$SHOW_TYPES
}
## Code to populate a node of the tree
proc populateTree {tree node SHOW_TYPES} {
if {[$tree set $node type] ne "directory"} {
return
}
set path [$tree set $node fullpath]
$tree delete [$tree children $node]
### display so that folders comes before files at the same level
### files and folders are displayed in alphabetical order
set folders [lsort -dictionary [glob -nocomplain -type d -dir
$path *]]
set files [lsort -dictionary [glob -nocomplain -type f -dir
$path *]]
foreach f [list {*}$folders {*}$files] {
set type [file type $f]
if {$type eq "directory"} {
### this is a folder
### Make it so that this node is openable
set id [$tree insert $node end -text [file tail $f] \
-values [list $f $type] \
-open 0]
$tree insert $id 0 -text dummy ;# a dummy
$tree item $id -text [file tail $f]/
### this folder has sub-folders
### process them automatically
### so the user does not have to click on each one
### to find where actual files are
populateTree $tree $id $SHOW_TYPES
} elseif {($type eq "file") && \
([file extension $f] in $SHOW_TYPES)} {
### get basic info on the file to display
### Format the file size nicely
set size [file size $f]
if {$size >= 1024*1024*1024} {
set size [format %.1f\ GB [expr {$size/1024/1024/1024.}]]
} elseif {$size >= 1024*1024} {
set size [format %.1f\ MB [expr {$size/1024/1024.}]]
} elseif {$size >= 1024} {
set size [format %.1f\ kB [expr {$size/1024.}]]
} else {
append size " bytes"
}
### format the date and time nicely
set ttime [file mtime $f]
set fdate [clock format $ttime -format "%Y-%m-%d"]
set ftime [clock format $ttime -format "%H-%M-%S"]
### display the file in the tree
set id [$tree insert $node end \
-tags SHOW_FILE \
-text [file tail $f] \
-values [list $f $type $size $fdate $ftime]]
### only open folders that are not empty
### THIS FOLDER HAS A DISPLAYABLE FILE IN IT
### AUTOMATICALLY OPEN IT
$tree set $id size $size
$tree item [$tree parent $id] -open 1 -tags FOLDER_HAS_ITEM
} else {
# a file type that will not be shown
}
}
# Stop this code from rerunning on the current node
$tree set $node type processedDirectory
}
# ## Create the tree and set it up
proc display_tree {tw ROOT SHOW_TYPES} {
catch { destroy $tw.tree }
catch { destroy $tw.vsb }
catch { destroy $tw.hsb }
catch { destroy $tw.dummy }
ttk::treeview $tw.tree -columns {fullpath type size date time} \
-displaycolumns {size date time} \
-yscroll "$tw.vsb set" -xscroll "$tw.hsb set"
ttk::scrollbar $tw.vsb -orient vertical -command "$tw.tree yview"
ttk::scrollbar $tw.hsb -orient horizontal -command "$tw.tree xview"
$tw.tree heading \#0 -text "Directory Structure"
$tw.tree heading size -text "File Size" -anchor w
$tw.tree heading date -text "Last Mod Date" -anchor w
$tw.tree heading time -text "Last Mod Time" -anchor w
$tw.tree column size -stretch 1 -width 20
$tw.tree column date -stretch 1 -width 20
$tw.tree column time -stretch 1 -width 20
### you can make files and non-empty folders more visible
### by playing around with colors and fonts as below
### change or remove as needed
$tw.tree tag config SHOW_FILE -foreground blue -font bold
$tw.tree tag config FOLDER_HAS_ITEM -foreground red -font bold
populateRoots $tw.tree $ROOT $SHOW_TYPES
bind $tw.tree <<TreeviewOpen>> {populateTree %W [%W focus]
$SHOW_TYPES}
# ## Arrange the tree and its scrollbars in the toplevel
lower [ttk::frame $tw.dummy]
pack $tw.dummy -fill both -expand 1
grid $tw.tree $tw.vsb -sticky nsew -in $tw.dummy
grid $tw.hsb -sticky nsew -in $tw.dummy
grid columnconfigure $tw.dummy 0 -weight 1
grid rowconfigure $tw.dummy 0 -weight 1
}
proc run_test {} {
wm iconify .
### first window
set ROOT_1 "C:/Dev"
set SHOW_TYPES [list .ini .txt .docx]
catch {destroy .t1 }
toplevel .t1
wm title .t1 $ROOT_1
wm geom .t1 500x400
display_tree .t1 $ROOT_1 $SHOW_TYPES
### second window with different files to show
set ROOT_2 "C:/Windows/INF"
set SHOW_TYPES [list .ini .csv]
catch {destroy .t2 }
toplevel .t2
wm title .t2 $ROOT_2
wm geom .t2 800x600
display_tree .t2 $ROOT_2 $SHOW_TYPES
}
### start here
run_test