This tutorial describes obj, a minimalistic object system for Tcl.
As I made it for my project on computer-generated cartoons, the examples in this tutorial deal of a simple system of morphing lines. We create a class line and a class morph which interpolates two lines.
The code lines in this tutorial show the dialog of interactive shell tclsh. The percent sign % is the prompt. Completed lines without leading prompt are responses of Tcl as you are used by the shell.
Either you have just copied the file into some directory, then input:
% source obj-0.1.tm %
Or, if you have put it to your package path, then input:
% package require obj 0.1 %
First, we create our class line:
% obj class line class line %
Every line has coords for the start and end point stored in option -coords. The default value shall be {10 10 100 10}:
% obj configure line -coords {10 10 100 10}
class line has option -coords with default {10 10 100 10}
%
Now is the time for all brave ... err ... well, we try our first object creation:
% set line1 [obj new line -coords {10 10 10 100}]
obj inst::3
%
No smoke at the keyboard. Phew. Now let us check if coordinates have value as expected:
% $line1 cget -coords 10 10 10 100 %
Our line shall be able to appear and disappear on an arbitrary canvas widget. (Note that inside methods, the variable $self refers to the object itself):
% obj method line appear canvas {
$canvas delete $self
$canvas create line [$self cget -coords] -tags $self
}
method line appear
% obj method line disappear canvas {
$canvas delete $self
}
method line disappear
%
Now try it out:
% package require Tk 8.5.1 % pack [canvas .c] -expand yes -fill both % $line1 appear .c 1 %
Screenshot:

As expected: a vertical line appears.
To avoid inappropriate values for option -coords, we always validate them on change:
% obj validatemethod line -coords val {
foreach c $val {
if {![string is double -strict $c]} then {
return -code error\
[list $self expects for -coords only numbers\
but received $c]!
}
}
}
validatemethod line -coords
%
If we happen to try to configure our line1 with inappropriate values for -coords, an error is raised:
% $line1 configure -coords {x 2 3 4}
obj inst::3 expects for -coords only numbers but received x!
%
This method destructively moves object's coords towards another line object:
% obj method line morph {other f} {
set coords {}
foreach {x0 y0} [$self cget -coords] {x1 y1} [$other cget -coords] {
lappend coords [expr {$x0+($x1-$x0)*$f}] [expr {$y0+($y1-$y0)*$f}]
}
$self configure -coords $coords
}
method line morph
%
Now, the basics are done. We start our morphing class
obj class morph class morph %
An instance of morph needs a starting line, an ending line, a resulting line, coords for each of them, and a state:
% obj configure morph -coords {10 10 10 100}
class morph has option -coords with default {10 10 10 100}
% obj configure morph -startcoords {10 10 10 100}
class morph has option -startcoords with default {10 10 10 100}
% obj configure morph -endcoords {100 10 100 100}
class morph has option -endcoords with default {100 10 100 100}
% obj configure morph -state 0.0
class morph has option -state with default 0.0
%
Objects can have private data. If one private date is the name of another object, then we can treat this as a component. The constructor of morph shall install components start, end, line:
% obj constructor morph {x0 y0 x1 y1 x2 y2 x3 y3} {
$self private start [new line -coords "$x0 $y0 $x1 $y1"]
$self private end [new line -coords "$x2 $y2 $x3 $y3"]
$self private line [new line]
}
constructor morph
%
Now, let us test it:
% set m [new morph 10 10 10 100 50 50 30 100] obj inst::5 %
We can always see all names of private data of the instance:
% $m private start end line %
See the value of private curve:
% $m private line obj inst::8 %
We can invoke any defined method of private line:
% $m component line cget -coords 10 10 100 10 %
The methods appear, disappear are delegated to component line:
% obj delegate method appear morph line class morph delegates method appear to component line. % obj delegate method disappear morph line class morph delegates method disappear to component line. %
The option -coords relates to component line:
% obj delegate option -coords morph start class morph delegates option -coords to component start. %
The option -startcoords relates to option -coords of component start, the same for end, so delegate options with explicitly set name of target options:
% obj delegate option -startcoords morph start -coords class morph delegates option -startcoords to component start. % obj delegate option -endcoords morph end -coords class morph delegates option -endcoords to component end. %
Option -coords is set by configuremethod -state (see below). Setting it directly should be forbidden:
% obj read-only morph -coords option -coords of class morph is read-only. %
Our morphing object shall change its component line depending of option -state, thus we define the appropriate configuremethod:
% obj configuremethod morph -state s {
$self component line configure -coords\
[$self component start cget -coords]
$self component line morph [$self private end] $s
}
configuremethod morph -state
%
Now, test it:
% $m configure -state 0.5 % $m appear .c 10 %
The morphed component line appears on canvas .c.
To watch the state of $m continuously, we create a scale which interactively sets the state:
% scale .c.s -orient horizontal
.c.s
% place .c.s -anchor sw -relwidth 1.0 -rely 1.0
% .c.s configure -command [list apply {{obj num} {
$obj configure -state [expr {$num / 100.0}]
$obj appear .c
}} $m]
%
Screenshot:

What remains? Just fine-tuning as you wish.
Have fun with object-oriented programming!
© Wolf-Dieter Busch | Home | Sitemap | Urheber | A-Z