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?
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.
> 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.
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 ;-)
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.
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.
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
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
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:
::
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
"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 wrote:
>
>
> Agreed. How about a page called droplets.
>
Done, it's called Droplets
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.