Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

drag/drop file-icon on tcl-script? on windows

143 views
Skip to first unread message

Eric Taylor

unread,
Apr 4, 2006, 7:58:36 PM4/4/06
to
I'm on windows. I have a few tcl scripts where I'd like to
drag a file onto the programs Icon, or it's shortcut, and
be able to use it like I do with most other programs.

If I look in procexp.exe it tells me thecommand line
for these droppable programs is:

program.exe drop-file-name-here-full-path

Is there a way to get the tcl program to launch and have argv(1..n) be
the dropped file names? Sure beats drilling down 5 levels in a file
dialog if you can simply reach the file with your mouse.

One little app in particular is one to convert .gifs to tcl code in a
file of the same name, with a different extension, like .tcl. Another
is one that would put the file name into the clipboard so I could
paste it into a tcl console window.

Any ideas?

sleb...@gmail.com

unread,
Apr 5, 2006, 1:08:44 AM4/5/06
to

Ah, a fan of "droplets" like me. Droplets was first popularised on
MacOS 7.5 and has since been a favourite among Mac users. Strangely the
concept never caught on on Windows except for Stuffit which originated
on the Mac. But windows apps do in fact support the droplet interface
like you said by simply passing the dropped file name as command line
parameters.

I write lots of droplets for personal use on Windows using Tcl. The
trick is to convert your script into an exe file. If it is a short,
simple script then the simplest way to do this is using freewrap which
itself support the droplet interface. Just drag and drop your .tcl
script onto freewrap and out comes an .exe file.

This is the general structure of my droplets:

bind . <Return> {catch {[focus] invoke}}

####
# Main body of code here..
####
proc some_process {} {
# do something on the file
}

if {$argc == 0} {
wm withdraw .
wm title . "My Droplet"
wm resizable . 0 0
set ok 0
pack [message .m \
-text \
{This program does something to some files.
Drag and drop files or directories onto the program icon} \
-width 200] -fill both
pack [button .b -text " OK " -command {set ok 1}]
wm deiconify .
focus -force .b
update
vwait ok
} else {
foreach dropped_file $argv {
some_process $dropped_file
}
}
exit

Remember to exit at the end otherwise Tk will enter the event loop and
never exits and you'll get an ugly blank window. Download freewrap form
freewrap.sourceforge.net, save the script above, paste your processing
routine into it and drop the script on freewrap. Now you can drop files
and directories onto your newly generated exe file.

alten

unread,
Apr 5, 2006, 3:17:04 AM4/5/06
to

Eric Taylor wrote:
> I'm on windows. I have a few tcl scripts where I'd like to
> drag a file onto the programs Icon, or it's shortcut, and
> be able to use it like I do with most other programs.

> Is there a way to get the tcl program to launch and have argv(1..n) be


> the dropped file names? Sure beats drilling down 5 levels in a file
> dialog if you can simply reach the file with your mouse.
>

> Any ideas?

Wrap your script in a batch file.
:: THIS IS FOR WINDOWS CMD.exe NOT W95 COMMAND.COM!
::if 0 {
if not {%1}=={} set myFiles=%*
start tclkit "%~f0" & exit

::NOW THE TCL STARTS . . . }

wm wi .
if {[info exists env(MYFILES)]} {
foreach f [string map {\\ \/} $env(MYFILES)] {
tk_messageBox -message "Doing something with \n$f"
}
}
tk_messageBox -message Done.
exit


I assume that you have tclkit.exe in your path.
something doesn't work with my 'file normalize', so I used 'string map'
instead. Something is also wrong in my 'exec', and the files won't
pass correctly to, for example, notepad.

sleb...@gmail.com

unread,
Apr 5, 2006, 6:51:41 AM4/5/06
to

Most people on windows have ActiveState so he's more likely to have
tclsh and wish in his path. Replace the start tclkit bit with start
wish and you will have correct behavior of file normalize.

Your batch file generated an error for me:
invalid command name :: while executing


:: THIS IS FOR WINDOWS CMD.exe NOT W95 COMMAND.COM!

The following batch file works perfectly:

::if 0 {
if not {%1}=={} set myFiles=%*

start wish "%~f0" & exit
:: }

wm wi .
if {[info exists env(MYFILES)]} {
foreach f [string map {\\ \/} $env(MYFILES)] {
tk_messageBox -message "Doing something with \n$f"
}
}
tk_messageBox -message Done.
exit

The ::if 0 { trick is neat ;-)

sleb...@gmail.com

unread,
Apr 5, 2006, 7:07:25 AM4/5/06
to
alten wrote:
> Eric Taylor wrote:
> > <snip>

> > Is there a way to get the tcl program to launch and have argv(1..n) be
> > the dropped file names? Sure beats drilling down 5 levels in a file
> > dialog if you can simply reach the file with your mouse.
> >
> <snip>

>
> I assume that you have tclkit.exe in your path.
> something doesn't work with my 'file normalize', so I used 'string map'
> instead.

I figured out what's wrong with your file normalize. You're trying to
use it on a "list of filenames" which won't work. Use it *inside* your
foreach loop instead and it will work as expected:

::if 0 {
start wish "%~f0" %* & exit
:: }

wm wi .
if {$argc > 0} {
foreach f $argv {
set f [file normalize $f]


tk_messageBox -message "Doing something with \n$f"
}
}
tk_messageBox -message Done.
exit

The thing to realise is that Windows already passes command line
arguments as a proper Tcl compatible list. You must do a foreach/lindex
on the arguments otherwise you'll end up with extra unwanted quote
characters {} which was what [file normalize] was choking on.

sleb...@gmail.com

unread,
Apr 5, 2006, 7:43:19 AM4/5/06
to
sleb...@yahoo.com wrote:
> alten wrote:
> > Eric Taylor wrote:
> > > <snip>
> > > Is there a way to get the tcl program to launch and have argv(1..n) be
> > > the dropped file names? Sure beats drilling down 5 levels in a file
> > > dialog if you can simply reach the file with your mouse.
> > >
> > <snip>
> >
> > I assume that you have tclkit.exe in your path.
> > something doesn't work with my 'file normalize', so I used 'string map'
> > instead.
>
> I figured out what's wrong with your file normalize. You're trying to
> use it on a "list of filenames" which won't work. Use it *inside* your
> foreach loop instead and it will work as expected:

I forgot to mention that in this case [file normalize] and your earlier
[string map] are redundant as Tcl can perfectly handle filenames in
native format as demonstrated by the following batch file:

::if 0 {
start wish "%~f0" %* & exit
:: }

pack [text .t] -fill both -expand 1


if {$argc > 0} {
foreach f $argv {

tk_messageBox -message "Opening something with \n$f"
set h [open $f r]
.t insert end [read $h]
.t see end
close $h
}
}

Notice that there is absolutely no need for [file normalize] in the
code above.

alten

unread,
Apr 5, 2006, 5:11:19 PM4/5/06
to
Ah. That will be it. When I was testing the 'exec notepad' I only used
one dropped file, so had no need for list-handling. The quotes will
have been there, stuffing it up.

Eric Taylor

unread,
Apr 5, 2006, 5:58:19 PM4/5/06
to
WoW! This is cool. And these examples certainly work!

I'm a bit curious though: How???

I think I understand it; it starts wish on the same batch file and the
dropped arguments, but when wish gets it, won't it get an error on
the first ::if

At least that's what happens if I type 2 colons into a console window.
It says something about an empty command name.

Or does this error just get ignored somehow?


Thanks a bunch!

eric

Eric Taylor

unread,
Apr 5, 2006, 6:45:24 PM4/5/06
to

AND speaking of the mac, I used to love a little program called something like
"Click Here it is!" which let you drag/drop any file on top of a file dialog box and it
would navigate to the enclosed directory for that file. Boy I'd love that one again on
windows.

Ok, here's my two contributions:

First, pic2tcl.bat

Create the batch file with the following, and then drag/drop a .gif or .jpg file(s)
onto the batch file and it will create a file of the same path, with a .tcl extension
that can be used as an inline image.

Second, 2clip.bat

The second, same idea, it will copy the file name of the dropped file (only 1 file)
into the clipboard. It will put up a dialog box because the clipboard gets cleared
when the program exits. It will also just quit in 30 seconds as well.


pic2tcl.bat
-------------------------------------


::if 0 {
start wish "%~f0" %* & exit
:: }

package require base64
proc trans {arg} {

set file $arg

set fd [open $file r]
fconfigure $fd -translation binary
set rawdata [read $fd]
close $fd
set fd [open [file root $arg].tcl w]
set b64data [base64::encode $rawdata]
puts $fd "image create photo -data {\n$b64data\n}"
close $fd
}


wm wi .
#console show


if {$argc > 0} {
foreach f $argv {
set f [file normalize $f]

#tk_messageBox -message "Doing something with \n$f"
trans $f
}
}
tk_messageBox -message Done.
exit


-----------------------------
2clip.bat
------------------------------

::if 0 {
start wish "%~f0" %* & exit
:: }
wm wi .
if {$argc > 0} {

clipboard clear
set f [lindex $argv 0]
clipboard append $f
after 30000 exit
tk_messageBox -message "Clipboard:\n$f"
}
exit

sleb...@gmail.com

unread,
Apr 5, 2006, 9:51:02 PM4/5/06
to
Eric Taylor wrote:
> WoW! This is cool. And these examples certainly work!
>
> I'm a bit curious though: How???
>
> I think I understand it; it starts wish on the same batch file and the
> dropped arguments, but when wish gets it, won't it get an error on
> the first ::if
>
> At least that's what happens if I type 2 colons into a console window.
> It says something about an empty command name.
>
> Or does this error just get ignored somehow?

That's namespaces. :: is the global namespace and the [if] command does
indeed belong to the global namespace. The namespace rules state that
if a proc or variable is used without declaring which namespace it
belongs to Tcl will first search for a matching proc or variable in the
local namespace and then in the global namespace. So actually [::if] is
a more specific form of [if]. You're telling Tcl that you want to use
the [if] command that belongs to the global namespace which in this
case resolves to the built-in [if] command.

The genius of this, I'm sure you realise, is that :: happens to be
ignored by the COMSPEC shell as line comments. I'd like to take this
opportunity to thank alten for showing us this nice hack.

As for the error you get when trying out an empty :: what you are
trying to do is invoke a proc/command with an empty name. This is the
same error you'll get when doing [{}]. To illustrate this point run the
following script:

# Proc with zero byte name:
proc {} {} {puts "Hello"}

# Call our {} proc normally:
{}
# Call our {} proc using namespaces:
::

sleb...@gmail.com

unread,
Apr 5, 2006, 10:16:06 PM4/5/06
to
Eric Taylor wrote:
> AND speaking of the mac, I used to love a little program called something like
> "Click Here it is!" which let you drag/drop any file on top of a file dialog box and it
> would navigate to the enclosed directory for that file. Boy I'd love that one again on
> windows.
>
> Ok, here's my two contributions:
>

If we're showing contributions then here's my 2. Mine will pop up a
window explaining what it does if you double click on it. I prefer it
that way since I often forget the function of a script when I have lots
of them ;-)

We need a wiki page for this.

__________

reformat.bat
Intelligently strips out newlines from a text file. Useful for
reformatting things like usenet posts & RFCs.
__________


::if 0 {
start wish "%~f0" %* &
exit
}

#############################################
# Text file reformatting utility
#############################################


bind . <Return> {catch {[focus] invoke}}

proc centerWindow {theWindow} {
set xx [winfo screenwidth $theWindow]
set yy [winfo screenheight $theWindow]

wm geometry $theWindow +[
expr ($xx/2)-([winfo width $theWindow]/2)]+[
expr ($yy/2)-([winfo height $theWindow]/2)]
update
}

proc paragraph {data} {
set data [string map {"\n\n" "\000" "\n\n\n" "\000"} $data]
set data [string map {"\n" " "} $data]
set data [string map {" " " " " " " " " " " "} $data]
set data [string map {"\000" "\n\n"} $data]
return $data
}

proc reformat {x} {
if [file isdirectory $x] {
foreach y [glob -nocomplain -directory $x -- *.txt] {
reformat $y
}
} else {
set base [file dirname $x]
set thefile [file tail $x]
if {[file extension $x] == ".txt"} {
set f [open $x r]
set data [paragraph [read $f]]
close $f
file mkdir $base/converted
set f [open $base/converted/$thefile w]
puts $f $data
close $f
}
}
}

if {$argc == 0} {
wm withdraw .

wm title . "reformat"


wm resizable . 0 0
set ok 0

set msg ""
append msg "Reformats whitespace in .txt files. "
append msg "Reformatted files are saved into "
append msg "a new directory named 'converted'.\n\n"
append msg "Drag and drop files or directories "
append msg "onto the program icon"

pack [message .m -text $msg -width 200] \
-fill both -pady 2
pack [button .b -text " OK " -command {set ok 1}] \
-pady 2
wm geometry . -800-800
wm deiconify .
update
centerWindow .
focus -force .b
vwait ok
} else {
foreach x $argv {
reformat $x
}
}
exit

__________

renumber.bat
Adds leading zeros to numbers in file names so that they can get sorted
properly.
__________


::if 0 {
start wish "%~f0" %* &
exit
}

#############################################
# File renaming utility
#############################################


bind . <Return> {catch {[focus] invoke}}

proc centerWindow {theWindow} {
set xx [winfo screenwidth $theWindow]
set yy [winfo screenheight $theWindow]

wm geometry $theWindow +[
expr ($xx/2)-([winfo width $theWindow]/2)]+[
expr ($yy/2)-([winfo height $theWindow]/2)]
update
}

proc renumber {x} {
if [file isdirectory $x] {
foreach y [glob -nocomplain -directory $x -- *] {
renumber $y
}
} else {
set base [file dirname $x]
set thefile [file tail $x]
set theroot [file root $thefile]
set theext [file extension $thefile]
regexp -- {[0-9]+$} $theroot num
set num [string trimleft $num "0"]
if {$num == ""} {
set num 0
}
set thefile [regsub -- {[0-9]+$} $theroot {%04d%s}]
set thefile [format $thefile $num $theext]
set thefile "$base/$thefile"

file rename -force $x $thefile
}
}

if {$argc == 0} {
wm withdraw .

wm title . "renumber"


wm resizable . 0 0
set ok 0

set msg ""
append msg "Renumbers files with leading zeros.\n\n"
append msg "Drag and drop files or directories "
append msg "onto the program icon"

pack [message .m -text $msg -width 200] \
-fill both -pady 2
pack [button .b -text " OK " -command {set ok 1}] \
-pady 2
wm geometry . -800-800
wm deiconify .
update
centerWindow .
focus -force .b
vwait ok
} else {
foreach x $argv {
renumber $x
}
}
exit

Eric Taylor

unread,
Apr 6, 2006, 1:02:28 PM4/6/06
to

"sleb...@yahoo.com" wrote:

>
> If we're showing contributions then here's my 2. Mine will pop up a
> window explaining what it does if you double click on it. I prefer it
> that way since I often forget the function of a script when I have lots
> of them ;-)

Ahh, like a Usage: command line hint on entering no arguments, yes.

Nicely organized, easy to use as a template.


>
>
> We need a wiki page for this.

Agreed. How about a page called droplets.


> __________
> ::if 0 {
> start wish "%~f0" %* &
> exit
> }

Even cleaner, don't need the :: before the }

Eric Taylor

unread,
Apr 6, 2006, 5:09:12 PM4/6/06
to

Eric Taylor wrote:

>
>
> Agreed. How about a page called droplets.
>

Done, it's called Droplets

alten

unread,
Apr 6, 2006, 5:16:52 PM4/6/06
to
Yes, I noticed later that I had unnecessarily added '::' (COMSPEC has
finished before that line), though it does no harm.

Cleaner still;

::if 0 {
start wish "%~f0" %*

exit
}

Linux users may be mislead. The trailing '&' in


start wish "%~f0" %* &

is unnecessary; it does not start a separate child shell, the 'start'
command does that.

In my original line:

start wish "%~f0" %* & exit

the '&' simply ends one command, allowing a new command 'exit' to live
on the same line (like ';' in Tcl). I think that is neater.

0 new messages