Be sure to try the latest: A embedded Bwise application on a web page (tcl-tk plugin required)!
There are various examples that will grow to more than examples as I proceed on this page, based on Tcl/Tk version 8.0 (or 1), which is freely available from for instance Scriptics, and runs on both Unix, Mac and Pc platforms. To run programs, only the 'wish' graphical interpreter is needed (and maybe some libraries), but the whole package is only a few megabytes in archive form. There is even a Netscape plugin and a combined version with Java, so apart from very good portability (the sources work over these platform usually without a single adaptation), there should be no problem sharing programs with a large audience.
I couldn't say I prefer it over Java, but Java gives you the hassle of compilation, actually without the benefit of generatig stand alone code, and ultimate speed, while tcltk gives you an interpreted language that can compete with Jave in terms of execution speed. Java is C++ like, TclTk is more or less scripting language + LISP +GUI, but has facilities (such as easy symbolic indirections) that can easily give it an object oriented flavour. I guess I like the retro feeling of sitting behind a interpreter remotely resembling another imperative language: back to Basic, or something.
Some of the examples below will make clear why Microsoft isn't to happy having its innovative content tested against the likes of these (why close up Explorer otherwise?).
Assume there is a canvas with name .c available:
canvas .c -bg gray20
pack .c -side bottom -expand y -fill both -anchor s
now make some text:
.c create text 300 200 -text "Block Editor V0.1" \
-font "helvetica 40" -tags tt -fill purple -anchor c
And do some cheap but flashy animation:
for {set y 0} {$y < 10} {set y [expr $y+1]}
{for {set x 0} {$x < 50} {set x [expr $x+1]}
{.c itemco tt -font "helvetica $x"; update}}
To save the history of the last session (sort of similar
of saving a 'core' image for lisp/smalltalk adepts, a bit more
readable though):
history keep 1000
set f [open "last.his" w]
puts $f [history]
close $f
I'll dig up some of the socket stuff I did some time ago, to
make two Tcl/Tk interpreters talk over the internet. Sort of
a unix 'talk' equivalent in a few lines of code. Stay tuned.
The simple version of setting up a connection, in this case on
two interpreters running on the same machine is:
****TCL SHELL 1:***
set s [socket -server fs 1235]
proc fs {fd host p} { puts $fd;
fileevent $fd readable "gets $fd l; puts stdout \$l" }
***TCL SHELL 2:***
set s2 [socket [info hostname] 1235]
proc s2 {s} {puts sock4 $s; flush sock4}
After which we can send a message from interpreter 2 to 1 by simply
using:
***SHELL 2:***
s2 "Does this message appear on the other end automatically?"
The same string should appear in the other window without further
programming.
Of course we have Tk too, so lets do some UI here, if we replace the above
procedure fs by:
proc fs {fd host p} { puts $fd; fileevent $fd readable "gets $fd l; puts stdout \$l;
.t insert end \$l; .t insert end \\n; .t see end" }
(first close the current connection, or use another in parallel),
a text window will capture the messages. The other end could be
made to have a text entry field by using:
entry .e -textvar et ; pack .e
bind .e <Return> {global et; s2 $et; .e sel range 0 end}
to type in any line (and be ready for the next) that appears on the
bottom of the text window on the other interpreter, which could
be on any ohter internet node and machine type...
A simple block generating routine, and its use to create two named
blocks:
proc crb {x y w h name} {eval .c create rect $x $y [expr $x+$w]
[expr $y+$h] -tags "{$name crb block}" -fill yellow
-outline darkblue;
eval .c create text $x [expr $y+$h] -anchor nw
-text $name -fill darkblue -tags "{$name crb label}" }
crb 100 50 50 50 t1
crb 160 50 50 50 t2
Note the use of tags, they make it easy to manipulate multiple items,
such as deleting a block by referencing its global name:
proc delb {name} {foreach i [.c find withtag $name] [.c del $i] }
delb t1
delb all
or
.c move t2 100 50
to move both text and block 't2' around on the canvas.
Some things have to be hand made, such as a routine to find all
canvas items with two matching tag names (I'll do a general list
based set Union operator soon):
proc find2l {l1 l2} {foreach i [.c find withtag $l1]
{if {[lsearch [.c itemcget $i -tags] $l2 ] >= 0} {return $i}} }
The blocks can be moved around by simpy picking the up, and the wires stay connected on the scrollable canvas. Double click blocks to select or deselect them, touch pins to select them (they'll turn green). When two pins are connected, use the wire button to put a wire in between. Blocks are created by using the block or scope button, and dragging them out of the upper left corner. You'll also need this file to make the code run, simply put it in the same directory with bwise.tcl, install bwise.tcl for using bin/wish.exe from the Tcl/Tk distribution, double click bwise, and the application starts.
Al this with under 10 KILO bytes of code...
(I have more prepared, such as changing names and properties
dynamically.)
I've been using sockets again to link a bwise window with a simple tcl/tk text window containing the (ascii) commands for generating drum tracks with the latest (gnu C) drum sample program which read commands of the form 'instrument start_time lenght amplitude' from its stdin.
This graphical representation can easily be transformed in the required command stream, enabling all kinds of sample and interactive processing...
A command is exectuted py pressing return, or by Activating te Eval button.
Here is an example connected Bwise diagram with one shell:
% proc rinf {{parent .} {do {} }} {foreach i [winfo children $parent] \ {if {$do != {}} {$do $i} {puts $i; }; rinf $i $do }} % proc pp {i} {for {set j 1} {$j<[llength [ split $i . ]]} {incr j} \ {puts -nonewline " "} ;puts "$i"} % rinf . pp .fb .fb.bnewb .fb.quit .fb.p .fb.bwire .fb.bcdrum .fb.bcscope .mw .mw.hscroll .mw.vscroll .mw.c .mw.c.shell0 .mw.c.shell0.e .mw.c.shell0.b .mw.c.shell0.t .mw.c.scope0 .mw.c.scope0.c .mw.c.scope0.s %Where mw.c is the canvas containing variuous bwise blocks, of which .mw.c.shell0 and .mw.scope0 are subwindows.
It is not too hard to gather this data in a list window, and call up the configuration data for an item in an associated text window, I'll gather the code up from my history file as I have time. Unfortunately, it is not possible to blindly read back the configuration data by automatically generating a configure command with the latest options, because some options cannot be set after they hae been once determined (such as the -class option). This makes interactive editing by using the rather short code to do the above a bit less attractive, some list of excluded options would be needed.
It should be possible to gather the pack data as well, so that an existing graphica structure can be captured in tcl/tk that generates it quite straightforwardly.
% proc rupdate {dt {pro {}}} { global rupdatesema; set rupdatesema [expr $rupdatesema -1] ; if {$rupdatesema > 0} {if {$pro != {}} {pro};after $dt "rupdate $dt $pro" } } % proc pro {} {global rupdatesema; puts $rupdatesema} % set rupdatesema 10 % rupdate 1000 pro 9 after#1 8 7 6 5 % after cancel [after info] % puts $rupdatesema 5 %A list of the scheduled events can be obtained by the info command, and fed back to cancel future events.
The procedure pro could also contain a command such as
$mc create text 200 200 -text "10" -tag tv -fill purple \
-tag tv -font "helvetica 80"
proc pro {} {global rupdatesema mc; $mc itemco tv -text $rupdatesema}
set rupdatesema 10
rupdate 1000 pro
to create a huge countdown counter, assuming the bwise.tcl
file was source-d previously.
NOTE: There are various conventions concerning the tags of Bwise objects, which I'll summarize in another place, but which roughly mean that the first tag of each object is its ('instance', or 'actual') name, and the second the group (or 'class' or 'type') ot belongs to. Wires have their end points in terms of connectivity defined by the tag 3 through 6, which are pairs of {blockname pinname}, and there are various tags which have special meaning, such as selection0, which is assigned to all blocks which are double clicked on. The order of the first set of tags is essential, and should not be changed. Blocks are grouped together (e.g. for motion) by their common first tag.
More editing facilities have been added, such as graphically adding wires. Select two pins, by clicking on them to make them green, and press the wire button. The pins will deselect again, and a wire is drawn. Click on a wire to toggle the selection of the pins it is connected to, press Wire again to remove the wire, while the pins stay selected (so the wire could be redrawn by pressing Wire again).
Blocks can be deleted by selecting a set of them by double clicking on them, giving them a red background, and pressing Del Sel (delete selection). They are now irrecoverably deleted.
To clear the canvas, simply make an empty canvas file by starting a Bwise, and immedeately pressing save (e.g. to empty.tcl), and loading this file when desired. The load is a destructive load, and no confirmation will be asked for!
Here is a picture of the example represented by the code adder.tcl which can be read in Bwise:
The blocks shown above were created on the command shell. To run Bwise with a command shell, and possibly combined with more code (your own ?),first start up 'wish', and use 'source bwise.tcl' from the right directory to start up. For source code development, it is usefull to note that the turnaround time of changing something in for isntance the Bwise code, and trying the results is a matter a seconds: save the new code, source it again, and you're set. This is because Bwise is made to be reloadable, and can even be quit without destroying the root window without when run with a console , so other tcl/tk code may continue to be active. It is convenient to keep a long history during development (e.g. use history keep 1000, and loading the latest (edited) source code is then simply a matter of !so or something, which works very fast because Bwise is small, and requires little installation.
After having started up with a shell, the adder shown above was
created by:
newblock adder0 0 0 30 80 {ina inb inc outs outc}
newblock buffer0 0 0 30 60 {in out clock}
newblock buffer1 0 0 30 50 {in out clock}
newblock buffer2 0 0 30 50 {in out clock}
newblock ground 0 0 20 20 {gound}
newblock connector0 0 0 10 80 {in1 in2 out clock}
Of course, all blocks have to be dragged into place, simply
by clicking on them and moving them. The connections were
made by selecting the pins and drawing the right wires (see below
for an alternative, using the Bwise procedure 'connect')
Calling the function gen_netlist will generate a type of net-list, where each connection in the canvas is stated on a new line, in a form that can be directly read by Bwise.
gen_netlist connect wire5 adder0 ina buffer0 out connect wire7 adder0 inb buffer1 out connect wire9 adder0 outs buffer2 in connect wire12 adder0 inc ground gound connect wire13 buffer0 in connector0 in1 connect wire14 buffer1 in connector0 in2 connect wire15 buffer2 out connector0 out connect wire16 buffer0 clock connector0 clock connect wire17 buffer1 clock connector0 clock connect wire18 buffer2 clock connector0 clockAfter storing and deleting all the wires, they can be regenerated by using the this data:
set wires [gen_netlist] $mc del [tag_and {wire}] eval $wiresThe Bwise procedure tag_and {list} returns al list of all graphical indices from the main Bwise canvas ($mc), that have all tags in the supplied input list: an 'and' function on the set of tags.
Of course the netlist can easily be used (and adapted) for other purposes.