It seems that [pathname selection] returns the currently selected items,
so just store that in a variable. But since that seems obvious maybe
that is not your question...
* GeorgeB <[email protected]>
| Hello,
| Newbie here, and I'm having some difficulties with treeview in TCL.
| I'm working off a demo example from Active State TCL.
| 1) I wondered if someone could help, I get the following error but don't know what to do to fix or how to fix.
| Error is invalid command error ERROR: invalid command name "populateTree"
| command bound to event: "populateTree .fc.tv.tree [.fc.tv.tree focus]"
| Code is below
The code you posted works for me (I don't get that error, and from the
code logic, I could not understand how populateTree could not be
defined), so there probably is something else wrong. Are you using
additional namespace code around that example?
| ttk::treeview $tw.tree -columns {fullpath type size date time} -displaycolumns {size date time} \
| -yscroll "$tw.vsb set" -xscroll "$tw.hsb set"
Note that it is always preferable to use [list] to build up callback commands:
... -yscroll [list $tw.vsb set] -xscroll [list $tw.hsb set]
In your example it depends on what $tw is set to whether it makes a difference, but if more complex arguments are passed in the callbacks,
the [list] approach is the better one.
| 2) How do I get a selection from multiple items treeview to be saved in a variable.
It seems that [pathname selection] returns the currently selected items,
so just store that in a variable. But since that seems obvious maybe
that is not your question...
HTH
R'
Hello,
Newbie here, and I'm having some difficulties with treeview in TCL.
I'm working off a demo example from Active State TCL.
1) I wondered if someone could help, I get the following error but don't know what to do to fix or how to fix.
Error is invalid command error ERROR: invalid command name "populateTree"
command bound to event: "populateTree .fc.tv.tree [.fc.tv.tree focus]"
Code is below
2) How do I get a selection from multiple items treeview to be saved in a variable.
# temp dir to mimic Network dir
set ::dir "C:/Dev"
proc populateRoots {tree} {
populateTree $tree [$tree insert {} end -text "Network File" \
-values [list $::dir directory]]
}
## Code to populate a node of the tree
proc populateTree {tree node} {
if {[$tree set $node type] ne "directory"} {
return
}
set path [$tree set $node fullpath]
$tree delete [$tree children $node]
foreach f [lsort -dictionary [glob -nocomplain -dir $path *]] {
set type [file type $f]
set id [$tree insert $node end -text [file tail $f] \
-values [list $f $type]]
if {$type eq "directory"} {
## Make it so that this node is openable
$tree insert $id 0 -text dummy ;# a dummy
$tree item $id -text [file tail $f]/
} elseif {$type eq "file"} {
set size [file size $f]
set ttime [file mtime $f]
## Format the file size nicely
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"
}
$tree set $id size $size
}
}
# Stop this code from rerunning on the current node
$tree set $node type processedDirectory
}
# ## Create the tree and set it up
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"
$tw.tree column size -stretch 0 -width 70
populateRoots $tw.tree
bind $tw.tree <<TreeviewOpen>> {populateTree %W [%W focus]}
# ## 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
Thank you in advance
George
On 18/09/2022 20:28, GeorgeB wrote:
Hello,
Newbie here, and I'm having some difficulties with treeview in TCL.
I'm working off a demo example from Active State TCL.
1) I wondered if someone could help, I get the following error but don't know what to do to fix or how to fix.
Error is invalid command error ERROR: invalid command name "populateTree"
command bound to event: "populateTree .fc.tv.tree [.fc.tv.tree focus]"
Code is below
2) How do I get a selection from multiple items treeview to be saved in a variable.
# temp dir to mimic Network dir
set ::dir "C:/Dev"
proc populateRoots {tree} {
populateTree $tree [$tree insert {} end -text "Network File" \
-values [list $::dir directory]]
}
## Code to populate a node of the tree
proc populateTree {tree node} {
if {[$tree set $node type] ne "directory"} {
return
}
set path [$tree set $node fullpath]
$tree delete [$tree children $node]
foreach f [lsort -dictionary [glob -nocomplain -dir $path *]] {
set type [file type $f]
set id [$tree insert $node end -text [file tail $f] \
-values [list $f $type]]
if {$type eq "directory"} {
## Make it so that this node is openable
$tree insert $id 0 -text dummy ;# a dummy
$tree item $id -text [file tail $f]/
} elseif {$type eq "file"} {
set size [file size $f]
set ttime [file mtime $f]
## Format the file size nicely
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"
}
$tree set $id size $size
}
}
# Stop this code from rerunning on the current node
$tree set $node type processedDirectory
}
# ## Create the tree and set it up
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"
$tw.tree column size -stretch 0 -width 70
populateRoots $tw.tree
bind $tw.tree <<TreeviewOpen>> {populateTree %W [%W focus]}
# ## 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
Thank you in advance
GeorgeThe error message says that the system doesn't know anything about the command populateTree. This is a little surprising since you have defined
it in the code you have posted.
Probably what has happened is that when you executed "proc populateTree
{tree node} {...}" you were inside a namespace, so you ended up defining <someNamespace>::populateTree; since the code associated with the event operates in the global namespace, it is looking for ::populateTree and
can't find it.
You can ensure that you define populateTree in the global namespace by changing your [proc] statment to "proc ::populateTree {tree node}...".
If you are interested to know where your current populateTree command is ending up, you could add "puts stdout [namespace current]" immediately
before the [proc] statement, which willwrite the current namespace to standard output when the script is first sourced.
Good luck sorting out your script, I hope this proves helpful,
Alan
On Monday, September 19, 2022 at 12:45:29 PM UTC+1, Alan Grunwald wrote:
On 18/09/2022 20:28, GeorgeB wrote:
Error is invalid command error ERROR: invalid command name "populateTree" >> >
Probably what has happened is that when you executed "proc populateTree
{tree node} {...}" you were inside a namespace, so you ended up defining
<someNamespace>::populateTree; since the code associated with the event
operates in the global namespace, it is looking for ::populateTree and
can't find it.
You can ensure that you define populateTree in the global namespace by
changing your [proc] statment to "proc ::populateTree {tree node}...".
If you are interested to know where your current populateTree command is
ending up, you could add "puts stdout [namespace current]" immediately
before the [proc] statement, which willwrite the current namespace to
standard output when the script is first sourced.
Alan, you genius....you have sorted the problem, thank you so much.
If you any tips or suggestion I'd greatly appreciate.
* GeorgeB <[email protected]>
| Hi Ralf,
| Thank you for you response.
| Please pardon as I'm very new to TCL.
| Yes there is an additional namespace around this code, and I have
| tried multiple examples all seem to give me an invalid name error.
Usually it is best to post a _complete_ example which actually shows the error. The code you posted did not use an additional namespace, so it
does not show the error.
If you define a proc inside a namespace, you need to specify that
namespace in callbacks, since those callbacks usually are invoked
outside of that namespace when they run later.
Instead of
bind $tw.tree <<TreeviewOpen>> {populateTree %W [%W focus]}
try
bind $tw.tree <<TreeviewOpen>> [namespace code [list populateTree %W [%W focus]]
The [namespace code ...] captures the current namespace for the
asynchronous invocation of the command later.
| Is there a way to define "populateTree" so that the error is not
| generated? or should the tree have it's own name space?
It's not how "populateTree" is defined, but how it is to be called.
If you define it inside a namespace, you need to specify that namespace
when calling it.
Note that procs which are defined in the same namespace can call other
procs in that namespace without specifying that namespace explicitly,
but for callbacks like [bind] you need to specfiy the namespace via [namespace code ...].
| 2) How do I get a selection from multiple items treeview to be saved in a variable.
| > It seems that [pathname selection] returns the currently selected items, | > so just store that in a variable. But since that seems obvious maybe
| > that is not your question...
| I've tried [pathname selection], it doesn't print anything in console
You need to replace 'pathname' by $tw.tree, then select something in the treeview, then issue that command:
set selected [$tw.tree selection]
puts $selected
I004 I005 I006
HTH
R'
| I've tried [pathname selection], it doesn't print anything in console
You need to replace 'pathname' by $tw.tree, then select something in the treeview, then issue that command:
set selected [$tw.tree selection]
puts $selected
I004 I005 I006
* Ralf Fassel <[email protected]>
| Instead of
| bind $tw.tree <<TreeviewOpen>> {populateTree %W [%W focus]}
| try
| bind $tw.tree <<TreeviewOpen>> [namespace code [list populateTree %W [%W focus]]
Nah, that's nonsense, since the [%W focus] needs to run later when the binding is triggered, not when it is defined.
Easiest solution seems to move the "%W focus" into populateTree:
proc populateTree {tree node} {
...
proc populateTree {tree {node ""}} {
if {$node eq ""} {
set node [$tree focus]
}
...
and then just do
bind $tw.tree <<TreeviewOpen>> [namespace code [list populateTree %W]]
HTH
R'
However if I use bind $tw.tree <<TreeviewOpen>> [namespace code [list populateTree %W [%W focus]]]
, where code = container name I get error
ERROR: invalid command name "%W"
in "%W focus"
| I've tried [pathname selection], it doesn't print anything in
console You need to replace 'pathname' by $tw.tree, then select
something in the treeview, then issue that command:
set selected [$tw.tree selection]
puts $selected
I004 I005 I006
I'd done exactly as you had suggested...but realised I forgot to
select items in the tree....doh!!!
I'm assuming I will have to map the id e.g. I004 etc to actual value
in the tree? i.e. if id = I004 and I004 relates to file config.txt
in tree then I'll have to map it somehow to each other .
Is it possible to selected a node and get all values underneath it?
On selection of the node is just gives me one single ID?
GeorgeB <[email protected]> wrote:
However if I use bind $tw.tree <<TreeviewOpen>> [namespace code [list populateTree %W [%W focus]]]
, where code = container name I get error
ERROR: invalid command name "%W"
in "%W focus"
That is because if you look at the above, you are asking tcl to execute
a command named %W (note the []') at the time you define the binding.
There is no command named %W defined (unless you defined a proc by that name), so you get "invalid command name "%W".
You would be best to follow Ralf's suggestion and perform the 'focus'
Now I just need to figure out how to print selection (single or multiple) into console. It always comes up blank...
If you any tips or suggestion I'd greatly appreciate.
Almost forgot, a great on-line manual too:
https://www.magicsplat.com/tcl-docs/docindex.html
* GeorgeB <[email protected]>
| > set selected [$tw.tree selection]
| > puts $selected
| > => I004 I005 I006
| I'd done exactly as you had suggested...but realised I forgot to select items in the tree....doh!!!
:-)
| I'm assuming I will have to map the id e.g. I004 etc to actual value in the tree?
| i.e. if id = I004 and I004 relates to file config.txt in tree then
| I'll have to map it somehow to each other .
That would be
$tw.tree item $item -text
or any other item option you are interested in:
foreach s [$tw.tree selection] {
puts "$s => [$tw.tree item $s -text]"
}
I004 => _csp
I005 => _rpm
I006 => _s
I007 => _sk
| Is it possible to selected a node and get all values underneath it? On
| selection of the node is just gives me one single ID? e.g. Network
| file
| |- Folder 1
| |- file0.txt
| |- file1.txt
| |- file2.txt
| |- file3.txt
| I would like to get all items underneath Folder 1 by selecting Folder 1 Iterate over
$tw.tree children $item
HTH
R'
On Tuesday, September 20, 2022 at 9:37:21 AM UTC+1, Ralf Fassel wrote:
* GeorgeB <[email protected]>
| > set selected [$tw.tree selection]
| > puts $selected
| > => I004 I005 I006
| I'd done exactly as you had suggested...but realised I forgot to
| select items in the tree....doh!!!
| :-)
| I'm assuming I will have to map the id e.g. I004 etc to actual value in the tree?
| i.e. if id = I004 and I004 relates to file config.txt in tree then
| I'll have to map it somehow to each other .
That would be
$tw.tree item $item -text
or any other item option you are interested in:
foreach s [$tw.tree selection] {
puts "$s => [$tw.tree item $s -text]"
}
:)
Thanks Ralf.
GeorgeB <[email protected]> wrote:
On Tuesday, September 20, 2022 at 9:37:21 AM UTC+1, Ralf Fassel wrote:
* GeorgeB <[email protected]>
| > set selected [$tw.tree selection]
| > puts $selected
| > => I004 I005 I006
| I'd done exactly as you had suggested...but realised I forgot to
| select items in the tree....doh!!!
| :-)
| I'm assuming I will have to map the id e.g. I004 etc to actual value in the tree?
| i.e. if id = I004 and I004 relates to file config.txt in tree then
| I'll have to map it somehow to each other .
That would be
$tw.tree item $item -text
or any other item option you are interested in:
foreach s [$tw.tree selection] {
puts "$s => [$tw.tree item $s -text]"
}
:)
Thanks Ralf.
Note also that you can add a list of tags to treeview items (read the
manpage for details). You can make use of tags for, among other uses, tracking how a given row relates to the other data you are marshalling.
Note that the tags associated with an item are a Tcl list, so use
list operators to manipulate them.
On Tuesday, September 20, 2022 at 2:50:06 PM UTC+1, Rich wrote:
Note also that you can add a list of tags to treeview items (read
the manpage for details). You can make use of tags for, among other
uses, tracking how a given row relates to the other data you are
marshalling.
Note that the tags associated with an item are a Tcl list, so use
list operators to manipulate them.
Thanks Rich,
One thing I'm trying to figure out is how to filter on file
extension.
Now I'm passing two folder (Folder 1 & Folder 2) to simulate the
structure (see below), the tree structure is created as one would
expect. However I was hoping to filter on *.ini files.
In doing so I was trying to figure out were it determines the file
names before it populates the tree.
For Folder with out sub folder e.g. Folder 2, applying filter to "foreach f [lsort -dictionary [glob -nocomplain -dir $path *.ini]]" works, but not for folder with sub folder i.e. Folder 1
|- Folder 1
|- Sub folder
|- file0.ini
|- file1.docx
| bind $tw.tree <<TreeviewOpen>> \
| [namespace code [list populateTree %W [list %W focus]]]
I don't think so, since that just builds a list, but does not *execute*
the "%W focus" when the binding is triggered. It just passes the string "$tw.tree focus" as second parameter to [populateTree].
Hence I was trying to ascertain where within the code (as posted in the first thread), does it create a node for Folder 1 > Sub Folder > file
Since I couldn't figure it out I came asking help.
On 9/20/22 11:30 AM, GeorgeB wrote:
For Folder with out sub folder e.g. Folder 2, applying filter to "foreach f [lsort -dictionary [glob -nocomplain -dir $path *.ini]]" works, but not for folder with sub folder i.e. Folder 1
|- Folder 1Hello,
|- Sub folder
|- file0.ini
|- file1.docx
I doubt that, as Rich pointed out, "it" knows that you mean to separate
files from folders. Here you are dealing with two kinds of nodes in the
tree widget, and I believe it is up to you to determine when to add a "folder" node and when to add a ".ini" file, or something else, and
where to add them in the tree hierarchy.
GeorgeB <[email protected]> wrote:
On Tuesday, September 20, 2022 at 2:50:06 PM UTC+1, Rich wrote:
Note also that you can add a list of tags to treeview items (read
the manpage for details). You can make use of tags for, among other
uses, tracking how a given row relates to the other data you are
marshalling.
Note that the tags associated with an item are a Tcl list, so use
list operators to manipulate them.
Thanks Rich,
One thing I'm trying to figure out is how to filter on fileRead the 'file' manpage, specifically the "extension" subcommand.
extension.
Now I'm passing two folder (Folder 1 & Folder 2) to simulate the
structure (see below), the tree structure is created as one would
expect. However I was hoping to filter on *.ini files.
In doing so I was trying to figure out were it determines the fileAs we have no idea what "it" is, we are at a loss to offer much in the
names before it populates the tree.
way of suggestions.
On 9/20/22 2:34 PM, GeorgeB wrote:
Hence I was trying to ascertain where within the code (as posted in the first thread), does it create a node for Folder 1 > Sub Folder > file
Since I couldn't figure it out I came asking help.
I took a look at the initial code you posted.
The first node, i.e., the root, is created when the widget is created by populateRoots. This proc creates a folder node associated with "C:/Dev".
Then according to the binding, whenever you click on a node,
populateTree is called. This one does a glob to find the files and
folders, and creates a node *at that level*, i.e., inside the node that
was clicked.
I am not fully sure what you are trying to achieve with ".ini" files but
the proper place to trim it might be in an if-statement inside the
foeach loop right there. Put an extra check for file extension inside
the block:
..
} elseif {$type eq "file"} {
..
and handle it as you wish.
The goal is to populate the tree with specific file type , in this case any files with .ini extension and ignore the rest.
On 9/20/22 2:34 PM, GeorgeB wrote:
Hence I was trying to ascertain where within the code (as posted in the first thread), does it create a node for Folder 1 > Sub Folder > file
Since I couldn't figure it out I came asking help.
I took a look at the initial code you posted.
The first node, i.e., the root, is created when the widget is created by populateRoots. This proc creates a folder node associated with "C:/Dev".
Then according to the binding, whenever you click on a node,
populateTree is called. This one does a glob to find the files and
folders, and creates a node *at that level*, i.e., inside the node that
was clicked.
I am not fully sure what you are trying to achieve with ".ini" files but
the proper place to trim it might be in an if-statement inside the
foeach loop right there. Put an extra check for file extension inside
the block:
..
} elseif {$type eq "file"} {
..
and handle it as you wish.
On Tuesday, September 20, 2022 at 8:11:34 PM UTC+1, saitology9 wrote:
On 9/20/22 2:34 PM, GeorgeB wrote:
Hence I was trying to ascertain where within the code (as posted in the first thread), does it create a node for Folder 1 > Sub Folder > file
Since I couldn't figure it out I came asking help.
I took a look at the initial code you posted.
The first node, i.e., the root, is created when the widget is created by populateRoots. This proc creates a folder node associated with "C:/Dev".
Then according to the binding, whenever you click on a node,
populateTree is called. This one does a glob to find the files and
folders, and creates a node *at that level*, i.e., inside the node that
was clicked.
I am not fully sure what you are trying to achieve with ".ini" files but the proper place to trim it might be in an if-statement inside the
foeach loop right there. Put an extra check for file extension inside
the block:
..
} elseif {$type eq "file"} {
..
and handle it as you wish.I was trying to re-write the code so that I could filter on a specific file type. I got 90% sorted :) but seem to have messed something else....
Now I can't seem to get the "Size Date Time" columns to populate correctly, basically Time populates under Size and Date and Time are empty.
I've re-created code here:
##########################
package require Tk
set tw .tree
catch {destroy $tw}
toplevel $tw
wm title $tw "Directory Browser"
wm iconname $tw "tree"
positionWindow $tw
set file_list {A B C D E}
ttk::treeview $tw.tree -columns {fullpath type size date time} -displaycolumns {size date time} -yscroll [list $tw.vsb set] -xscroll [list $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"
$tw.tree column #0 -anchor e -minwidth 150 -width 250 -stretch 1
$tw.tree heading size -text "Size"
$tw.tree column size -stretch 0 -width 70
$tw.tree heading time -text "Time"
$tw.tree column time -stretch 0 -width 70
$tw.tree heading date -text "Date"
$tw.tree column date -stretch 0 -width 70
$tw.tree insert {} end -id Folder -text "Folder 1"
foreach key [lsort $file_list] {
$tw.tree insert Folder end -text $key -values [list 80KB 18:00 21/09/2022]
}
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
grid columnconfigure $tw 2 -weight 1
grid rowconfigure $tw 2 -weight 1
set file_list {A B C D E}
$tw.tree insert Folder end -text $key -values [list 80KB 18:00 21/09/2022]
The goal is to populate the tree with specific file type , in this case any files with .ini extension and ignore the rest.You might look at the fileutil package from tcllib, in particular find or findByPattern
Something like:
package require fileutil
set iniFiles [fileutil::findByPattern C:/Dev -glob {*.ini}]
would give you a list of complete paths to all .ini files in and under the C:/Dev directory. Directory tree branches with no ini files woud not be in the list. I'm not sure if that is an advantage in your case or not.
Dave B
I was trying to re-write the code so that I could filter on a specific file type. I got 90% sorted :) but seem to have messed something else....
Now I can't seem to get the "Size Date Time" columns to populate correctly, basically Time populates under Size and Date and Time are empty.
ttk::treeview $tw.tree -columns {fullpath type size date time} ...
$tw.tree insert Folder end -text $key -values [list 80KB 18:00 21/09/2022]
On 9/21/22 4:35 PM, GeorgeB wrote:
I was trying to re-write the code so that I could filter on a specific file type. I got 90% sorted :) but seem to have messed something else....
Now I can't seem to get the "Size Date Time" columns to populate correctly, basically Time populates under Size and Date and Time are empty.
Glad to hear you got it sorted.
I don't recall ever using the ttk::treeview widget but it looks like the shift in columns you see is because you are creating the tree widget
with 5 columns but when you insert nodes into it, you provide data for
only three columns.
ttk::treeview $tw.tree -columns {fullpath type size date time} ...I don't know what your file list looks like (i.e., plain file names,
$tw.tree insert Folder end -text $key -values [list 80KB 18:00 21/09/2022]
full paths, a dictionary, etc.). But you can at least pass the file
name and its type (may be ".ini"?) as the first two elements of the list:
% $tw.tree insert Folder end -text $key -values [list $key ".ini" 80KB
18:00 21/09/2022]
On Wednesday, September 21, 2022 at 10:48:22 PM UTC+1, saitology9 wrote:
On 9/21/22 4:35 PM, GeorgeB wrote:
I was trying to re-write the code so that I could filter on a specific file type. I got 90% sorted :) but seem to have messed something else....
Now I can't seem to get the "Size Date Time" columns to populate correctly, basically Time populates under Size and Date and Time are empty.
Glad to hear you got it sorted.
I don't recall ever using the ttk::treeview widget but it looks like the shift in columns you see is because you are creating the tree widget
with 5 columns but when you insert nodes into it, you provide data for
only three columns.
ttk::treeview $tw.tree -columns {fullpath type size date time} ... $tw.tree insert Folder end -text $key -values [list 80KB 18:00 21/09/2022]I don't know what your file list looks like (i.e., plain file names,
full paths, a dictionary, etc.). But you can at least pass the file
name and its type (may be ".ini"?) as the first two elements of the list:
% $tw.tree insert Folder end -text $key -values [list $key ".ini" 80KB 18:00 21/09/2022]Yup (and Doh!), you are absolutely correct, I was passing only three elements.... for 5 columns. Apologies for overlooking the that bit...I shouldn't be working so late...
Thank you saitology9
So I did as suggested by saitology9,
if I pass 3 variables to line $tw.tree insert Folder end -text $key
-values [$key $key $s $t $d]
where s, t and d are variables for size time and date created by
reading files from a folder and processing to get the desired
attributes.
If I pass these to the $tw.tree insert Folder end -text $key -values
[A B $s $t $d] which is within foreach loop, I only get the last
value in the list for variables s, d and t
for e.g. all entries for the files are populated with 80KB 18:00
21/09/2022
Any advice.
GeorgeB <[email protected]> wrote:
So I did as suggested by saitology9,
if I pass 3 variables to line $tw.tree insert Folder end -text $key
-values [$key $key $s $t $d]
where s, t and d are variables for size time and date created by
reading files from a folder and processing to get the desired
attributes.
If I pass these to the $tw.tree insert Folder end -text $key -values
[A B $s $t $d] which is within foreach loop, I only get the last
value in the list for variables s, d and t
for e.g. all entries for the files are populated with 80KB 18:00
21/09/2022
Any advice.Make sure your code is as above, and not as you posted a few articles
ago:
$tw.tree insert Folder end -text $key -values [list 80KB 18:00 21/09/2022] Your result implies you still have the constants in your source.
Note also that the code you posted above is broken, it will not run.
You'll get better answers if you check that your code you are about to
post actually runs before you post it.
On Friday, September 23, 2022 at 2:42:28 AM UTC+1, Rich wrote:
GeorgeB <[email protected]> wrote:
So I did as suggested by saitology9,Make sure your code is as above, and not as you posted a few articles
if I pass 3 variables to line $tw.tree insert Folder end -text $key
-values [$key $key $s $t $d]
where s, t and d are variables for size time and date created by
reading files from a folder and processing to get the desired
attributes.
If I pass these to the $tw.tree insert Folder end -text $key -values
[A B $s $t $d] which is within foreach loop, I only get the last
value in the list for variables s, d and t
for e.g. all entries for the files are populated with 80KB 18:00
21/09/2022
Any advice.
ago:
$tw.tree insert Folder end -text $key -values [list 80KB 18:00 21/09/2022] >> Your result implies you still have the constants in your source.
Note also that the code you posted above is broken, it will not run.
You'll get better answers if you check that your code you are about to
post actually runs before you post it.
Hi Rich,
Apologies, please see code.
GeorgeB <[email protected]> wrote:
On Friday, September 23, 2022 at 2:42:28 AM UTC+1, Rich wrote:
GeorgeB <[email protected]> wrote:
So I did as suggested by saitology9,Make sure your code is as above, and not as you posted a few articles
if I pass 3 variables to line $tw.tree insert Folder end -text $key
-values [$key $key $s $t $d]
where s, t and d are variables for size time and date created by
reading files from a folder and processing to get the desired
attributes.
If I pass these to the $tw.tree insert Folder end -text $key -values
[A B $s $t $d] which is within foreach loop, I only get the last
value in the list for variables s, d and t
for e.g. all entries for the files are populated with 80KB 18:00
21/09/2022
Any advice.
ago:
$tw.tree insert Folder end -text $key -values [list 80KB 18:00 21/09/2022] >> Your result implies you still have the constants in your source.
Note also that the code you posted above is broken, it will not run.
You'll get better answers if you check that your code you are about to
post actually runs before you post it.
Hi Rich,
Apologies, please see code.Did you actually try running what you posted, *before* you posted it?
I.e., did you put only what you posted here into a file, all alone, and
try to launch it?
Because the code you posted again *does not run*:
Error in startup script: invalid command name "positionWindow"
while executing
"positionWindow $tw"
On 9/23/22 10:28 AM, GeorgeB wrote:
I'm coping the code again.
Alternatively, search the wiki for file browsers. There are several Tcl-only ones, and they will accept the option to limit the listings to certain files.
I'm coping the code again.
I'm coping the code again.
set ifname [rglob $rmDir *.ini {*.docx *.pptx *.txt *.xlsx}]
foreach ::dir [lsort $ifname] {
set split [file split $::dir]
set ::nodeid [lindex $split end-1]
set ::fname [lindex $split end-0]
if {$::nodeid == "Folder"} {..
On 9/23/22 1:55 PM, saitology9 wrote:
On 9/23/22 10:28 AM, GeorgeB wrote:
I'm coping the code again.
Alternatively, search the wiki for file browsers. There are several Tcl-only ones, and they will accept the option to limit the listings to certain files.
You haven't mentioned any context around this but you may be perfectly h
appy with a plain listbox. I would look into it as well.
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.
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
On Sunday, September 25, 2022 at 4:31:21 AM UTC+1, saitology9 wrote:
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@saitology9 Thank you so much, this worked wonderfully. Very much appreciated...
run_test
I was wondering if adding a checkbox to the tree would be a good idea or would it over complicate things.
But I read that one can use image of checkbox..
Question:
If I want to user to make a selection with mouse either selection top level or individual items (at same time toggle the image). Would I need to bind "<ButtonPress-1>" to selection?
On 9/26/22 2:27 PM, GeorgeB wrote:
I was wondering if adding a checkbox to the tree would be a good idea or would it over complicate things.
..
But I read that one can use image of checkbox..
Question:
If I want to user to make a selection with mouse either selection top level or individual items (at same time toggle the image). Would I need to bind "<ButtonPress-1>" to selection?
I haven't used this widget before so I can't help you there. But you
should be able to select multiple items as per usual MS Windows
conventions: shift-click and control-click to select/de-select multiple items, and then get the items via the "$tw selection" command.
| Sysop: | Keyop |
|---|---|
| Location: | Huddersfield, West Yorkshire, UK |
| Users: | 715 |
| Nodes: | 16 (2 / 14) |
| Uptime: | 152:48:33 |
| Calls: | 12,091 |
| Calls today: | 4 |
| Files: | 15,000 |
| Messages: | 6,517,662 |