Cincinnati Linux Users Group
Making Linux Available to Everyone!
Abstract
This presentation was given by Monty Stein at the January meeting of
the Cincinnati Linux Users Group.
History
TCL (Tool Control Language) was developed by John Ousterhout
(oh-stir-hout) at the University of California (Berkeley) and who is
currently working at Sun Microsystems on further TCL development.
TCL was originally meant to be a command interpreter for tools, hence
the name. Instead of reinventing the wheel and writing a command
interpreter from scratch, TCL would be linked in and the needed tool
specific commands added. Because of this you are likely to encounter
TCL in the strangest of places.
Comments
All comments begin with the "#" character. The difference between TCL
and other languages is that TCL actually uses the "#" as a command
that ignores all it's arguments. This means that comments can only go
where the parser expects a command to start.
Quoting
A
All instances of $variables are expanded when encountered unless some type of
quoting is used.
| \ | singly quoted character follows |
| "" | allows variable expansion, newlines are allowed |
| {} | do not expand variables, when this is used with a control structure, it is
reinvoked to provide variable expansion. |
| [] | run contents as command and return the result |
Note: expr calls will perform variable expansion twice: once
when the expr is encountered and again when expr sees it's arguments.
Variables
Variables are assigned in TCL using set. The set command can also
be used to read variables; this is useful when the variable is too complex
to represent using the $ notation (such as in certain types of associative
arrays).
Simple types include integers, floats, strings and lists (counted from
0). Associative arrays are defined by parenthesis.
$ tclsh
% set a 10
10
% set b 4
4
% expr $a+$b
14
% set t("junk") "more junk"
more junk
% puts $t("junk")
more junk
% puts $t("no more junk")
can't read "t("no more junk")": no such element in array
% set l {10 20 30 40 50 "oh, look!"}
10 20 30 40 50 "oh, look!"
% lindex $l 3
40
Any globally scoped variables that are going to be used within a
function must be declared as global, otherwise a variable
with the same name will be created within the function's scope.
Arithmetic
All arithmetic except for simple increment is done through the
expr call. All the standard math functions (sin,cos,sqrt) are
supported.
$ tclsh
% set a 3
3
% set b 4
4
% expr sqrt($a*$a+$b*$b)
5.0
% incr b
5
% incr a -1
2
% expr sqrt($a*$a+$b*$b)
5.38516
%
Control structures
If/Switch statements
if { $a!=1 } {
#true stuff here
} elseif {$b==0} {
#other stuff here
} else {
#yet more stuff
}
Switch is similar to it's counterpart in C except is has a richer set
of matches. These are selected by flags that are passed just after
the switch command:
| -exact | Must be an exact match |
| -glob | Shell like expansions (*,? and the like) |
| -regexp | Regular expressions |
| -- | End of flags |
% set input "foople"
foople
% switch -regexp -- $input {
foo* { puts "we got a foo!" }
bar* { puts "that's in a bar" }
default { puts "that cannot be found" }
}
we got a foo!
%
Procedures
% proc distance {a b} {
set ret [expr sqrt($a*$a+$b*$b)]
return $ret
}
% set r [distance 1 2]
2.23607
Looping
While
% set a 0
0
% while {$a<3} {
puts $a
incr a
}
0
1
2
$
For
% for {set a 0} {$a<3} {incr a} {
puts $a
}
0
1
2
%
Foreach (walks a list)
% foreach a {0 1 2} {
puts $a
}
0
1
2
%
Tk Overview
Each object on the screen that has some sort of behavior associated
with it (even static text) is called a Widget. This behavior is
defined through bindings that are connected with various X window
events. For example, a button widget that receives a mouse-button-1
down event would change its appearance to look like it was pressed and
invoke some command.
Tk is a library originally intended to provide TCL with X11 window
interface. It has been linked with a number of other languages to
give them the same X abilities. It has a similar look-and-feel to
Motif.
The layout of the widgets inside their parent window in Tk is
handled by the packer (or placer) that uses a constraint system to
handle the fit and size of the various widgets. By using a constraint
system, events like the resizing of a window can have all the internal
widgets resize themselves automatically in a pleasing fashion.
Since the whole basis of interaction with Tk is through X events, the
same mechanism can be used to have events be received at a later time.
This way applications that need to poll some resource but still react
to user driven events (such as a mailer) can be written simply.
Tk itself
You can do a lot in a few lines of code (here using TCL):
#!/usr/bin/env wish
button .b -text "Press to Quit" -command exit
pack .b
would create:
All the widgets have a default action that is overridden with
configuration options that are passed in when the widget is defined or
at a later time.
In this case the parent window (by default ".") contains a button
widget that has an appearance and a command associated with it.
Lazy?
There are GUI builders (SpecTcl for example)
out there that can do a lot of the work for you.
How to run with Tk
The Tk libraries have been integrated into many languages such as TCL
(the language it was written for), Perl, Python, Scheme, and on and
on... Here are the basics for 3 of them:
TCL
To run Tk under TCL use the wish command.
All the examples included in this document use TCL.
Perl
#!/usr/bin/env perl
use Tk;
my($win) = MainWindow->new();
[...]
&Tk::MainLoop;
is all that you need. Then, $win is your toplevel to do with as
you please.
Tk::MainLoop should be the last thing the program does. That will
start Tk up.
Python
Under python, see if: "from
Tkinter import *" raises an exception. If not, your copy of
python has Tk compiled in.
The core functionality is generally inherited from Frame. Each widget
group would generally be a class that inherits from Frame and packs
itself into it's own window. Here is a minimal program:
#!/usr/bin/env python
from Tkinter import *
class Hello(Frame):
def __init__(self,parent=None):
Frame.__init__(self,parent)
f=Frame()
Button(f,text="Hello World",anchor=CENTER,command=f.quit).pack()
f.pack()
hello=Hello()
try:
hello.mainloop()
except KeyboardInterrupt:
hello.quit()
Widgets
Common widget functions
Configure
Any widget option can be changed using the configure call.
For example:
$ wish
% label .l -text Hello -bg blue
.l
% pack .l
%

% .l configure -bg red
%
Bind
The bind command allows a command to be attached to an X event for a
particular window or widget. The dot (".") window is the parent.
Bindings take the form of:
     < MODIFIERs - TYPE -
DETAIL >
Where MODIFIER is zero or more of:
| Alt |
Button1 or B1 |
Button2 or B2 |
Button3 or B3 |
Button4 or B4 |
Button5 or B5 |
| Control |
Double |
Lock |
Meta or M |
Mod1, M1 |
Mod2 or M2 |
| Mod3 or M3 |
Mod4 or M4 |
Mod5 or M5 |
Shift |
Triple |
  |
Where TYPE is one of:
| ButtonPress or Button |
ButtonRelease |
Circulate |
Colormap |
Configure |
Destroy |
| Enter |
Expose |
FocusIn |
FocusOut |
Gravity |
KeyPress or Key |
| KeyRelease |
Leave |
Map |
Motion |
Property |
Reparent |
| Unmap |
Visibility |
  |
  |
  |
  |
DETAIL identifies a particular button or key for the binding.
This example reports the coordinates that the pointer is hovering
over:
canvas .c -width 200 -height 200
label .coord -text "Move the mouse" -relief raised
pack .c .coord -fill x
bind . < Key-q> exit
bind .c < Motion> ".coord configure -text \"(%x %y)\""
Packer
The packer is what actually places the widgets on the screen. It uses
a constraint system to simplify the definition of the layout. When
the packer starts to pack inside of a window, all of the window's area
is considered to be "unpacked". Any new widgets are placed inside of
that unpacked area using the flags that are supplied when pack is
called. By default the widget is packed in the top of the space and
the remaining unpacked space is left at the bottom of the packed
widget. Any further packs will take place there.
In normal usage, the packer will handle events like window resizing
with all the widgets centering and expanding themselves automatically.
The more common flags that can be supplied when calling pack are:
| -side | left,right,top,bottom |
| -fill | x,y,both,none |
| -in | window |
| -expand | 0,1 |
An example that will handle window resizing gracefully:
#!/usr/bin/env wish
text .t -width 40 -height 10 -wrap word -yscrollcommand ".s set"
scrollbar .s -orient vertical -command ".t yview"
pack .t -side left
pack .s -side left -fill y

| and after stretching the window: |
 |
|
Not quite right, the text area doesn't know that it can fill the space, it
is restricted to the size passed in when it was created. Changing it's
packing should fix it:
|
 |
pack .t -side left -fill both
|
Still not right. The text area is restricted on how big it can grow by the
presence of the scrollbar. We can tell the packer to have it fill ALL the
available space when it is packed so it will push the scrollbar over.
|
|  |
pack .t -side left -fill both -expand 1
Specific Widgets
Frame
Frames act as containers for other widgets (and through bind can have
their own behavior). There is a special flag for pack to force a
wiget to be packed inside of a frame.
#!/usr/bin/env wish
frame .f -relief raised -borderwidth 4
button .f.b1 -text "Inside"
button .f.b2 -text "Frame"
pack .f.b1 .f.b2 -in .f -side left
pack .f
button .b -text "Outside Frame"
pack .b
Label
A label can display text or bitmap images. Multiple lines of text can
be displayed if the text string contains newlines. There is no
intended interaction with the widget.
#!/usr/bin/env wish
set tick_value 0
proc tick {} {
global tick_value
set tick_value [ incr tick_value ]
.ticker configure -text $tick_value
# wait 1 second to reinvoke
after 1000 tick
}
label .l1 -text "This" -relief raised
label .l2 -text "That" -relief sunken
label .l3 -bitmap @/usr/include/X11/bitmaps/calculator
label .ticker
pack .l1 -side left
pack .l2 -side right
pack .l3 .ticker
# start the ball rolling
tick
bind . < Key-q> exit
Entry
Entry widgets are the main way that a user would interact with an application
using text fields. Each one contains one line of text and by default allows
scrolling, insertion/deletion of characters and cut/paste.
set name "Your name"
set phone "x1234"
label .namelabel -text "Name"
entry .name -width 30 -textvariable name
label .phonelabel -text "Phone"
entry .phone -width 9 -textvariable phone
pack .namelabel .name .phonelabel .phone -side left
Button
Buttons are the simplest interactive widget. They display some text or a
bitmap and can invoke a command when pressed. If needed a button can be
disabled and then cannot be pressed and will display a grayed appearance.
#!/usr/bin/env wish
label .m -text "The Universe has crashed..."
pack .m
button .ok -text Ok -state disabled
button .cancel -text Cancel -command exit
pack .ok -side left
pack .cancel -side right

Listbox
Listboxes show a list of choices (like used in a file browser) and indicate
which choice that the pointer is over by highlighting it. To actually select
a choice, a binding must be set up to the function that will handle it.
#!/usr/bin/env wish
proc show_selection {y} {
puts [.l get [.l nearest $y]]
}
listbox .l
.l insert end 1 2 3 4 5 6 7 8 9 10
.l insert 0 "0"
pack .l
bind .l "show_selection %y"
Scrollbar
Scrollbars can be easily attached to any widget that supports scrolling such
as Listboxes and Canvases. The set command is the main interaction
into the scrollbar. See the manual pages for details on the arguments that
can be passed.
#!/usr/bin/env wish
proc show_selection {y} {
puts [.l get [.l nearest $y]]
}
listbox .l -height 5
.l insert end 1 2 3 4 5 6 7 8 9 10
.l insert 0 "0"
scrollbar .s -command ".l yview"
.l configure -yscrollcommand ".s set"
pack .l -side left
pack .s -side right -fill y
bind .l "show_selection %y"
|
 |
Radiobutton
Radiobuttons act like the buttons on your car's radio (duh!). All the
button selections are mutually exclusive: selecting one will deselect
all the others that are associated with it.
#!/usr/bin/env wish
proc change {} {
global color
.current configure -text $color -bg $color
}
frame .f
set color "blue"
foreach i {"blue" "red" "green"} {
radiobutton .$i -text $i -value $i -variable color -command change -anchor w
pack .$i -anchor w -side left -in .f
}
pack .f
label .current -text $color -bg $color
pack .current -fill x

Text
A text area is a free format text entry area. The example for the
packer shows the basic structure. It is that simple.
Canvas
Canvas is a free form drawing area for line graphics. Because of the
complexity, I'm not going to cover it here. See the manual pages for
all the gory details.
Putting it all together
Examples
File Browser

Source in:
TCL(htmlified)
Python(htmlified)
Interactive Spline Drawer

Source in:
TCL(htmlified)
Perl(htmlified)
Application Laucher Used for Demos
Source in:
Python(htmlified)
Grail browser
Home page:http://grail.cnri.reston.va.us/grail/
All the examples in one place
In TCL:
bind_example.tcl
browser.tcl
button_example.tcl
entry_example.tcl
frame_example.tcl
intro_example.tcl
label_example.tcl
listbox_example.tcl
packer_example1.tcl
packer_example2.tcl
packer_example3.tcl
radiobutton_example.tcl
scrollbar_example.tcl
spline.tcl
viewer.tcl
In Python:
browser.py
launcher.py
In Perl:
spline.pl
CLUG HOME
| Events
| Directions
| Members
| Mailing Lists
(archives,
FAQ)
Resources
| Search
| Library
| Presentations
| Contributions
| Bylaws
| Board Minutes
CLUG Contact: Jeff Gilton (jeff@jsgis.com)
Web Page Contact: webmaster@clug.org
Last Modified: