bwise

by Theo Verelst ( http://theover.org ) who holds copyrights but grants free use and modification, preferably informing me in the case of modification, for non-commercial purposes when source is clearly mentioned by email, name and/or url; commercial use requires written permission, meaning I don't give the exploitation rights away just like that.

Download info for Bwise 0.3 is here: Bwise - a graphical programming setup TV (sept 11 03) a newer version is here: Bwise version 0.34, all in one file, 8.4 compatible . I expect a new version with some of the procs already available on other bwise pages will be available soon.

I'm making a page with applications, this page contains most menus and functions, bwise applications and examples. (april 4 2004) I updated the latter with a list of bwise application pages on top of the page.

When you feel like dropping me a note , concerning anything from a fun application to a highly paid american university position,... My new guestbook is a wiki itself: [L1 ] and [L2 ]. (email should also work fine, checked daily)


TV (Nov 6 2003) I made a list of pages about bwise on distributed linked bwise, and I'm making a page now on some of the rationale behind bwise.

TV: I found the above "Bwise applications and examples" comes up completely empty (at least for me), so I make copy: Bwise Applications and Examples II, which can be removed when not needed (anymore).

TV (April 17 '09) I found sometimes blocks like Mon cannot have their pins clicked because the letters get in the way, in that case check and resize the default Tk font could help:

 % font conf TkDefaultFont -size
 8
 % font conf TkDefaultFont -size 6

There are many obvious and less obvious, existing and explorational researchwise uses of a strong enough and muscled enough graphical programming package. What is there won't win a programming price for straightforward neatness, though it is worth a look at and makes sense. What I wanted was several things, which is for instance a reason for the 'drum' button for which you'll have to go to software on my older tcl/tk pages to get the drum sound renderer and samples, but the general idea, includign that I want strong command rendering in graphs and vv and standard block set is the main target here.

Bwise is a package written completely in tcl/tk, even is pretty basic for the major part, to make graphs with blocks and compose programs with them, by letting data flow and executing procedures on them. Though some of the code is pretty heavy on the extended eval use side (read impossible to read but with some effort fine to follow and compact), the idea is that very simple and basic Tcl use should suffice on the application side, where the blocks are put to use.

This page contains a description, example use and probably some function descriptions.


The starting screen is the main window recogniseable by the buttons and the scrollable gray canvas. Also the function editor pops up, which in the top part has a scrollable (press mouse and roll under or above window boundaries) list of all user functions, and in the middle part shows a function when a name in the list is double clicked.

The main window is shown in figure 1,

Image Bwise bw1.jpg

Figure 1: startup bwise main canvas and buttons.

where I used the middle mouse button on the bare canvas to pop up a block creation menu.

This is the result:

Image Bwise bw2.jpg

Figure 2: a single unconnected procedure block with one input and one output

After clicking middle mouse on the yellow block and selecting 'data', a data window for this block is shown, and left on top.

Image Bwise bw3.jpg

Figure 3: The data window with all variables defining the blocks' data and procedures

The window is a list of fields, one for each tcl variable starting with the name of the block followed by a dot, (in this case "proc1."). The blocks are automatically numbered uniquely when they are created, unless they are given a unique name which is possible by manually calling "new***", (in this case "newproc",) from the console or a console bwise block which can be created by pressing the paper icon in the top menu bar.

The variables proc1.in and procin.out contain the input and output data of the block, which are stored per block, even when they are connected up.

The most important variable contains the block function, called ***.bfunc (in this case proc1.bfunc). In fact this is a string which contains what could be seen as the body of a procedure, or however: a tcl (when desired also tk) script, in this case an assignment which simply sets the output variable equal to the input variable. Note that because of the dot "." in the name of the variables, the use of the dollar $ to access the content of the variable requires surrounding the whole name of the variable in curly braces, a bit annoying but it works. The left side of the "set" assignment doesn't require this.

One could also simply put the cursor in the editable field in the proc1 info window next to the 'proc1.bfunc' variable name and edit the function associated with the block, for instance to:

   set proc1.out [[expr ${proc1.in} * 2]]

when we now put the text cursor in the in proc1.in field by clicking on the entry to the right of the name in the proc1 info window and insert a 1 in the field, and press the "'eval'" button on the bottom of the window, the function in the bfunc field will be evaluated at the highest interpreter level, so that the proc1.out variable, which is real time linked with the entry in the info or data window, will be set to "2".

You could make a celcius to farenheit conversion as an example, I don't have that formula handy, or a dollar to euro, or preferably the reverse.

Now what's the block on the canvas doing in all this? It's mainly the graphical representation linked with the variables we just discussed. Calling up the block menu by clicking the middle mouse (or right if you have 2, I'm not sure that doesn't require you to change the 'canmeny' procedure in the 'bwise03.tcl' file for instance, I'll check it out), while pointing over the yellow block with proc1 written under it shows a menu entry "'eval'" which does the same as the button in the info window.

Now let us create a few more blocks by right clicking on a free spot on the canvas and selecting entry, mon, and another time proc, on different places on the canvas, and dragging the new blocks around by simply holding the left mouse button down somewhere in their yellow block.

Image Bwise bw4.jpg

Figure 4: having added some more blocks, calling up the block menu.

Now we click on first the Entry1.out pin with the left mouse button, and on the Proc1.in pin, so that both have turned green, and press the green 'wire' button: a wire appears between the Entry and the Proc1 blocks. When you move the blocks around, the wires stay connected to the same pins.

Pressing somewhere on the wire we just made will make both pins between which the wire runs green. Now when the 'wire' button is pressed again, the wire disappears, leaving the two pins green.

Clearly, we can repeat the wireing idea to connect all blocks up with eachother, for instance in order. We could also do this by commands in the console window:

   connect wirea Proc1 out Proc2 in
   connect wireb Proc2 out Mon1 in

The result is shown graphically in figure 5.

Image Bwise bw5.jpg

Figure 5: blocks connected up through wires, block menu popped up for first block in the chain.

The first block, 'Entry1', is shown to have been edited, it contains an entry as part of the block on the canvas, which moves along as part of the block, which can be given the text cursor by simply left clicking in the entry field. The value has become "1.0", which is immedeately available at the output of the block which has variable name "Entry.out" associated with it.

Image Bwise bw6.jpg

Figure 6: The data associated with each of the 4 blocks shown in the info windows.

Assume we have invoked the transfer menu on the block Entry1, then we would have tranfered that date from the output pin "out" from that block to the connected block(s) in this case the input "in" from block Proc1. Then Proc2.in would also show 1.0 instead of 1, or whatever it was before. We could now press the 'eval' button or evoke the menu on Proc1 with the same name, and have that block compute the outcome of its computation on its inputs data, which is "2.0".

Now we could perform a transfer operation on Proc1 by invoking the block menu, eval Proc2, transfer proc2, and eval Mon. And easier way to achieve this is to start at Entry1, and invoke propagate, run or funprop, which in this case have all similar effect: the data is transfered between the output (usually right side) of the block to the input (left) of the connected block(s), then that block is eval-ed, after which its output is transfered over all links present to connected blocks, etc.

We could check the info windows of al blocks and see the data is transfered and processed along the way, to finally arrive at the input of Mon1, after which finally the block function of Mon1 is executed, which is programmed to empty the little text widget in it, and display the latest input in it, in this case the outcome of

   ((${Entry1.out} +1)) = 2.0

In a slowly animated form (little pictures to save image size):

Image Bwise bwiseanis.gif

Figure 7: Animated actions and transfer.


  • How to create new (custom) blocks

Now let us add some other types of blocks, such as the shell, which like a console window in a bwise block, with as input a command, and as output the return value of the latest command.

We first use that shell (click paper icon first - do not click 'new block') to create a block with 2 inputs and outputs:

   eval [pro_args newproc {{in {i1 i2}} {out {o1 o2}}}]

which works the same as on the console window or in a normal tcl script. I used the support function 'pro_args' to create a well formed command from a number of non-default arguments, and immedieately execute that command through eval.

That created the 4 pin proc3 in figure 8, which I put in place and connected up. The function newproc makes a new procedure block, and it creates and initializes the associated variables.

Image Bwise bw8.jpg

Figure 8: A graph extended with an interactive tcl shell block

Furthermore 2 entries have been connected, and between one onather, currently simply data passing, function block has been inserted.

Image Bwise bw9.jpg

Figure 9: example merge block.

The above figure examplifies how the merge block works, the two inputs, which both need to have received data by transfering it from the connnected blocks are simply put after eachother in a string which is put in the output variable.

Image Bwise bw10.jpg

Figure 10: globbing files through a joining of command parts and a shell block

I filled in glob for globbing files, a normal tcl command, which needs an argument, which is usually a wildcard file description pattern. The pattern feeds through a block which might change, correct, or check it, is merged with the glob command, and the result is fed to the shell command input.

When the shell received input it is executed, making the glob command print the resulting matching files in the top little history window, which scrolls, and the outcome is then also sent over the out pin to the monitor block, which remembers only the last output.

Both these blocks have an initialization function, which is in the block menu, which is programmed to clear the window.

I made a larger canvas with a lot more examples of the different blocks, some old some new it is in figure 11.

Image Bwise bw11.jpg

Figure 11: Lots of blocks are possible.

Below, a list of block creation possibilities.

 newproc { {f {}} {name {}} {in {in}} {out {out}} {width {40}} {height {}} {tags {}} {x {10}} {y {10}} }

Make a new function, f is the code which gets executed in the block. These blocks are properly saved and restored, except their variables and functions are NOT. One may want to use save_vars from the bwise lib for that purpose, which works fine in principle, except then all variables are saved and restored, which is not always a brilliant combination with some counters and system variables, and should only happen immedeately in the same point in time as the reading of the canvas.

 newarray { {nx {3}} {ny {3}} {bn {array}} {fs {}} {ib {}} {ipi {}} {jb {}} {jpi {}} {x {100}} {y {120}} }

Make an array of blocks, with default arguments 3 rows and three columns and a function which concatenates the two per block inputs.

 newdisp { {name {display}} {x {0}} {y {0}} }

Put a display model on the canvas with 5x7 led simulations (old function)

 newentry { {width {60}} {height {30}} {tags {}} {name {}} {x {0}} {y {0}} {in {}} {out {out}} }

Make a entry block, with an empty block function and an entry text variable corresponding with the output pin. Entries cannot be properly restored after they've been saved, the best remedy is to simply make new ones after reloading a canvas which had them.

 newimage { {file} }

Make a block out of a rectangular image. Don't forget to

 package require Img

of some version unless you're reading gif or p?m images. After the img package is loaded images can almost allways be loaded easily, and then be more visual attractive blocks. Image block can be saved, don't remember which limitations.

 newmon { {keep {0}} {width {80}} {height {65}} {tags {}} {name {}} {x {0}} {y {0}} }

Creates a small scrollable text window which show the latest input, you can change the function but that requires tk knowledge. Keep 0 means that at every new input, the previous data is erased from the text window.

 newseq { {n {8}} {name {}} {del {500}} }

The sequencer block is yet another means of imposing a ordered firing regime, it has outputs which normally connect to trigger inputs on other blocks, which are dataless connections, only used to trigger block, i.e. call their blockfunction when one of the outputs of the sequencer block finds that block on the other end of the wire it has connected. The eight main outputs become active in sequence, beginning with q0, every half a second the next, active means that it makes the blocks which are connected to it run their blockfunction and immedeately after transfer their outputs to connected block inputs.

 newstack { {name {}} }

A stack is like a pile of list elements, the "machine" stack can put incoming packets (lists) on the pile when triggered to do something, it can sit and do nothing, or clear the pile to empty, or get the top element of the pile and put in on the output, by setting the mode input to the literals push, {} (empty list), clear or pop, respectively.

 newterm { {name {term}} {tags {}} {x {10}} {y {10}} }

A terminal is a block like a shell, except that it doesn't interpret or evaluate its input field, even though the button to activate it is still called Eval. When the button is pushed, or return is pressed with the cursor in the entry field, the data in that field on the bottom of the block on the canvas, which is linked with the variable associated with the "out" pin, is transfered to all connected blocks, and effectively, a "funprop" propagation is started. So that the popup menu doesn"t need to be used to start a "run" of the network. At that point, the block function doesn"t get executed, that only happens when data arrives at the "in" pin, in which case that data is shown in the above text window in blue. When the eval button is hit, the value in the entry field is inserted at the end of the text window in red, before funprop is started. The idea is that when the output is connected to a network which can be activated, that the result of running the connected set of blocks can be fed back into the input, so that just like in an ordinary shell, the command and the outcome are listed in the history of the window, except that here, we can make the command work by patching together all kinds of blocks. The init function clears the history window, and when newline is pressed, the command is made selected, so typing a new character except arrows makes the old text disappear, otherwise editable. Cutting and pasting works on both the entry and the history text.

 newtext { {width {60}} {height {40}} {tags {}} {name {}} }

Show a simple block which has a block function to put a text string in the block which is read from the input. Not attempt to eastetics, and large texts are thrown all over the canvas unclipped, but its an easy way to show text data, and the block from early versions on survives a save load cycle.

 createshell { {name {shell}} }

Like newterm, except it"s older code, with less argument options, the shell is always initially places in upper left corner, where it can be dragged out of. When no name is given, shell(number) is given to it, where shell_index, as with some other blockkinds is the global variable which keeps the count.

 procs_window {}

This is the window which automatically opens at startup on the right of the main bwise window. It lists all procedures except the ones whose name starts with tcl or tk, and those who are listed in the list in the file defaultproc.tcl at startup time. Double clicking a procedure name in the upper list, shows that procedure in the editable text window, overwriting whatever was in that window, which can de edited like a vanilla tk textwidget, and scrolled by dragging the cursor past the top or bottom (click somewhere to get rid of the selection). The procedure is shown as it is recorded in the normal tcl procedure way, it is not stored seperately and nothing extra is remembered about than but the normal agruments, defaults and procedure body, which are constructed into a normal procedure definition shown in the text window. The actual procedure is only updated when the 'Update Proc' button is pushed, in which case the whole content of the text window is simply executed as a tcl script, regardless of what is in it. Normally, all quotations, braces brackets, and everythings should be just as if one would read a script or have typed at the console prompt, except maybe newlines aren't exactly the same always, there was an issue with that I forgot, maybe the escaping. The formatting of the arguments is automatic and not stored somehow, which contentwise makes no difference. When a new procedure has been made, for instance by changing the name in the procedure definition and pressing 'Update Proc' or by reading in a script or using the console or a canvas shell, press 'Refresh List' to list all procedures in the upper list window, which then displays the first alphabetic procedure again. The 'Save Procs' button saves all tcl procedures which have been changed or created since program startup (or the creation of the function window) as a list of procedures in normal tcl form, so the list can be read as a normal script. After saving, the next time, still all changed procedures from the beginning of the session are saved each time the save button is pushed, but when a file from a previous session is overwritten, its original content is lost, and not merged, without any warning. File names can be name only for current global tcl dir, or an absolute path in normal unix notation, cut and paste works on the file name field with control C/V (on windows).

Image Bwise bw12.jpg

Figure 12: The function window

 save_procs { {n} }

Simply saves all procs in the same list as would be made above in file $n, no special provisions are needed for each proc, syntax is decent tcl syntax.

The Menu Buttons in the task bar equivalent contain a 'Save' button which saves the canvas, by listing the few index variables, the opened images, and then in normal tk form all elements on the canvas, including their tags. Which means that in principle, the whole canvas is saved, also elements not created by the menus or some of my block creation functions, and including blocks containing images, which should remain in the same relative or absolute path, or the saved tcl script must be edited, names are by basename reference, with no spaces allowed. The block variables are not saved this way, that is a point of attention.

Reading multiple canvas creation scripts on a row is in principle possible as long as there are no conflicts, they are just regular tcl scripts. Reading back all variables which were saved by save_vars is possible but since that concerns almost all variables conflicts easily arise. Saving just all variables starting with the root names of all blocks will be the first cure.

 tag_and { {taglist} }

returns the canvas id"s of all elements on the canvas which are assigned all tags in the list.

 savehis { {file} }

save all events in the history to a non-tcl text file. Bwise sets history length to 1000 at startup. Canvas shells are not generating events in this history.

 save_canvasvars { {n} }

Save the variables associated with the bwise blocks on the canvas (third tag in the tag list needs be 'block', first tag determines grouping), based on block names followed by a dot and then a wildcard. Didn't check this function recently, but it may do the trick. It won't save the array which holds the partly fired blocks and the transfered preceding block list.

 open_text { {n {}} }

Creates and editable text window with rudimentary filename, load and save buttons. Those work straight away using the name in the edit field, which can be altered also by double clicking and using the then appearing file selector, which only changes the editabl filed with tcl global dir local or full path name in normal unix form. Overwriting of the textwindow or the file happens straight away, with no warning. The global variable 'textname' contains the file name, for handy reference.

 grep { {pattern} {filenames {*}} }

Quite a useful function a la unix grep.

 gen_netlist { }

Generate a list of all connections on the canvas.

 delete_selblocks { }

delete all block which have been selected (given a red backdrop) by double clicking them, without asking for confirm. I'm not sure the associated variables are deleted, too.

 createscope { {name {scope}} }

A small oscilloscope (see waves) block is put in the upper left corner of the canvas, with a faint zero line, and an initially zero graph line. The graph can be scrolled over 1000 pixels, and has amplitude range of about plus to minus 40. The block can be initialized by the init function, which makes the graph empty, and the timebase at the left. Each time the block is triggered or the block function evaluated, the input is read and a line segment in the graph is drawn from the previous Y value to the input value at the current time base X position, which is incremented by one every time.

 block_get_pinnames { {block} {pintype {}} }

get all pins of a block returned as a list of type {} (all) typein or typeout.

 otherpins { {block} {pin} }

List all pins connected to a certain pin from a certain block.

The popup menus for the blocks mean the following:

Eval Call the block"s main function, called blockname.bfunc

Data open a small window with all variables listed starting with the blockname, and followed by editable fields with their content.

Propagate Transfer the data from all outputs of the block to the connected inputs, and start running all those blocks.

Transfer Put the value of all outputs of the block in the variables associated with the connected pints.

Run Same as Propagate, but call 'Eval' first. Run floods the network, and therefore can make blocks run many times for one invokation, when there are several paths from the block where the Run started, which also is quite elaborate for networks with moderate amounts of cross connections, i.e. of blocks not simply on a row, so then on run may take a long time to complete, doing a lot of useless work. There is currently no real provision to stop such run, except the to_be_run stack is somewhat limited in depth, and you may try to press the Wire button, so that the greenly lit pins in the animation are disconnected, which may stop the flood. Depending on the machine and graphics speed, you may easily or faintly see the progressing tranfers illustrated by the pins being lit up while the flooding proceeds, which of course makes the whole thing slower than without animation.

Init Call the block.bfunc_init function for the block, usually resets or clears the block.

Funprop Short for functional propagate, as if the outputs of the net are the output of functions, and one does a mathematical distributive analysis of that function to compute its value. Another type of activating the connected blocks on the canvas, which Eval the block, transfers the data, and repeats that for each of the connected blocks which has all of its inputs provided with (new) data until there are no more such blocks. After that, it may be that some blocks have a partially satisfied input pin set, in which case that is recorded by the global tcl array 'propst' (propagation state), which records which preceding (in the sense of pintype typein with connections) blocks for each blockname as array index blocks had their data already transfered, by listing them as array element. A block is considered to have fired and thus generated valid new output pin variables only all together, or not.

The idea of the array is that one may invoke the directly associated tcl function net_funprop several times, from several input branches, to in the end let the propagation continue to the overall output.

For use with for instance the term block, I made the variation function the net_funpropc which stops when fireing arrives back where it started, and only then eval-s the first block, which then loads the result in the text window.


I'll probably make a better saving and documented form soon, check my Theo Verelst and other bwise page for more tcl examples, also in bwise.


RJM: I have been experimenting a bit with Bwise. It seems to be a sort of Labview (concept). As a consequence, I have emphasized a part of the intro section above. Moreover, I modified the text regarding the creation of new blocks, because it was a bit confusing.

One question arises: why are block functions stored in variables, and not as procs? When stored in procs, byte compilation would increase execution speed.

TV (july 9 2004): Well, improvements are certainly welcome!

Concerning variables: the idea was that all data is available on the canvas, or directly related to it, so I linked a number of variables with the block base name, of which one is <blockname>.bfunc, containing the script which gets executed as block 'fire' function.

For blocks with one variable as output, it makes sense to see the blockfunction as a procedure/function with one return value, for blocks with more than one output, that is not natural, and possibly ambiguous or the pin binding becomes unclear -- (hm, using uplevel could solve this problem, or define ins and outs as lists, but probably it isn't worth doing so much to change -- RJM).

Also, given there are various possibilities of making the network 'run', it is not necessarily possible to compile the whole network statically.

A script lets the block directly access the relevant variables, by convention strictly the pin variables, without having to pass them to a function body, and I for the prototype certainly preferred toplevel evaluation, so *.bfunc simply contain toplevel scripts.

It *is* possible to make procs into blocks, using the bwise upgrade from Automatically generate Bwise blocks from procedures, by simply picking a proc from the list and pressing 'block'. Basically, that creates a block script which calls the proc in question with pin names corresponding to the proc argument names automatically.

I wasn't aware of labview when I started this sort of software, but I did have experience with khoros and AVS.

Sly: I can't see the pictures. The strange host 82.170.247.158 is unreachable.

TV Jan 2008. I'm moving. Curiously the previous ISP Tiscali has been taken over about the same day. IP address of the server has changed, the www.theover.org name points to the right server ( http://212.182.178.11 works, too, the url is redirected) again, so downloads are possible, I've updated the images on this page, but not yet on any others.

Sly: 212.182.178.11 is not working either. At least, for me. Why not to put these files on some public service? At last, imageshack or something.. :) The community may lose this page again if your homepage disappear in future.

TV Well, it did. A while. And then as I said during moving I hit some wires of the server and forgot to check it so it was off during the night.

Uhm. Pay me ? Or eh, support bwise some way :) ? In this world I'd have to be careful indeed that life's service to keep me continues, ain't it. But the server hosts a lot of interesting things (and tcl related images) so I don't plan on discontinuing it in general. And the average uptimes are exemplary, normally. Maybe a bwise.gnu.org or so ?

Sly: Well, I'm supporting it by jumping over the wiki pages and replacing 82.170.247.158 by 212.182.178.11.. Why not to use such an invention like text host names? ... and while I did it, the host dropped off again :) funny.

LV I don't see a mention, above, of whether bwise has been updated to support Tcl 8.5. Has it?

TV Well, it is very backward compatible thus far, I was in a way fine with tcl/tk 2. something or I don't remember what it was back 95 with some additions like sockets and then some, I don't think BWise is following some trend that requires it to update at all,. I'm sure rewrites are possible and tidying up, too, but most redos would make it bigger and less readable for me. Maybe I overlook some things, though.


To place nice new blocks on: Bwise block of the month .