A Guide to Using Dylan/TK

Introduction and Warning

Dylan/TK is an experimental TK binding for the Mindy Dylan interpreter. Although it is definitely usable, it is not intended to be an "industrial strength" product. The implementation strategy virtually guarantees that there will be some rough edges.

This binding operates by starting a "wish" interpreter as a slave process and passing both Tcl routines and data between the Mindy interpreter and the slave process. However, Dylan/TK provides an abstraction layer that should vastly reduce or even eliminate the need for users to be familiar with the Tcl extension language. For example, the following Dylan code:

  define constant text-frame
    = make(<frame>, height: 500, fill: "both", side: "bottom", expand: #t);
  define constant text-window
    = make(<text>, in: text-frame, relief: "sunken", font: normal-font,
           fill: "both", side: "right", expand: #t);
  define constant text-scroll
    = scroll(text-window, in: text-frame, fill: "y");
could be used to produce the effect as the following Tcl/Tk code:
  frame .text-frame -height 500
  pack .text-frame -fill both -side bottom -expand 1
  text .text-frame.text -relief sunken -font normal-font
  pack .text-frame.text -in .text-frame -fill both -side right -expand 1
  scrollbar .text-frame.scroll -command {.text-frame.text yview }
  .text-frame.text configure -yscrollcommand {.text-frame.scroll set }
  pack .text-frame.scroll -in .text-frame -fill y
while being considerably clearer to the average Dylan programmer.

Note: In order for this implementation to work you must have a copy of "wish" available on your system. This implementation was developed upon version 3.6 of "Tcl/Tk" but should work any version Tk implementation after 3.3 and before 4.0. Since version 4 introduces incompatible changes, you should not

(at present) expect all features to work under 4.0 or later

General Principles

In order to use Dylan/Tk, you should import module Tk from library Tk. This defines the <window> class, a variety of subclasses representing most of the Tk widgets, and a variety of support routines. All of these will be explained in some detail below. However, the user will sometimes find that he can gain more information by going to the book "Tcl and the Tk Toolkit" by John Osterhout, which explains the widgets' behavior in more depth than is practical for this document.

Any Dylan program which uses the Tk library will automatically create a top level window (which is accessible via the *root-window* variable) and a separate thread to process events upon that window. You build an interface by making widgets and "packing" them into windows -- either the root window, newly created top-level windows, or into subwindows of these windows.

An simple application might be the following:

  define method main (program-name :: <string>, #rest args);
    make(<message>, text: "Hello, world!", aspect: 500,
         side: "top");
    make(<button>, text: "Okay", relief: "raised",
         command: curry(destroy-window, *root-window*),
         expand: #t);
    map-window(*root-window*);
  end method main;
This creates a text message and a pushbutton, packs them into the root window, and then exits, and makes the root window visible. It then exits the main thread, leaving just the event processing thread active. When the button is pressed, it will destroy the root window (and all its subwindows). Since there are no windows left, the event thread exits, and the program terminates. We could just as easily specified "command: exit", which would force the Dylan program to exit, killing off all threads, and thus destroying the windows. Go ahead and try it, if you like.

Widgets, like any other Dylan object, are typically created via "make". There are three different varieties of keywords which can be specified:

Widget Specific Options:
These include the "aspect:" and "command:" keywords in the above example. Their precise effects depend upon the definition of the widget.
Standard Options:
These include the "relief:" keyword in the above example. They are accepted by most (if not all) widgets, and have pretty much the same effect for all of them. (Some keywords, like "text:", are not accepted by all widgets, but are common and predictable enough that users may be left to infer their effects.)
Packing Options:
These include the "side:" and "expand:" keywords in the above example. They are used to control the positioning and resizing of the new widgets within their "parent" windows. They are, for the most part, passed on directly to the "pack" function. In very rare cases, you will wish to create a window without (yet) packing it into another window. In this case, you can specify "pack: #f" to defer calling "pack" until you are ready to do so.

Parameter and Return Values

The values which are passed into "make" or into many other Tcl/Tk functions are passed into to Tcl/Tk as strings, and thus it is sometimes most convenient to write them as strings in Dylan. Most Dylan/Tk functions therefore accept strings as parameter values. However, you will usually find it easier to work with a richer set of types within Dylan's type system. Parameters values can commonly include instances of the following native Dylan classes:

and will occasionally include others on a case-by-case basis. In addition, the following new classes are also frequently accepted as parameter values:

Those Dylan/Tk functions which return values typically cast the results back into meaningful Dylan types before returning them. However, there are some occasions where this proves impractical, and result values remain as <string>s which must be explicitly cast into appropriate types. For example, the "configuration" function returns the values of all the configuration options for a widget. Since it can't guess what the types of these values ought to be, they simply remain as <string>s.

In some cases you must specify "callback" functions for Dylan/Tk widgets. These are typically specified via the "command:" keyword to make. On the few occasions where these functions take parameters, they will be passed in as <string>, and may include "\" quotation characters. You may need to call "tk-unquote" to strip out backslashes or to call "tk-as" to convert this to another type. For example:

  tk-as(<boolean>, "1")
returns #t.

The "Standard" Options

The following options seem to apply universally to all widgets. They are mostly concerned with presentation details of the widget's window, as opposed to the widget's function.

background:
This specifies the background color to be used within the widget. Typically it is a color name, like "Black" or "LightBrown", or as hexadecimal integers, like #x3a7 or #x3000a0007000.
borderwidth:
This specifies the size of the border to be placed around the window. Typically this will be an integer which specifies the number of pixels, but you may also use string values like "1c", "1m", "1i", "1p", which specify centimeters, millimeters, inches, and "points" respectively. This keyword is typically combined with the "relief:" keyword.
cursor:
This specifies a bitmap which will be displayed when the user's cursor is in this widget. The most useful forms will be "name" to specify a name from the standard cursor font, or "@Sourcefile fgColor" to load the cursor from a file.
foreground:
This specifies the background color to be used within the widget. Typically it is a color name, like "Black" or "LightBrown", or as hexadecimal integers, like #x3a7 or #x3000a0007000.
relief:
This specifies what sort of 3D border should be displayed around the widget. This must be one of: "flat" (default), "groove", "raised", "ridge", or "sunken".

Other options are by no means universal, but tend to mean the same thing whenever they occur:

bitmap:
This specifies a bitmap which will be displayed in the given widget. The most useful forms will be "name" to specify a name from the standard cursor font, or "@Sourcefile fgColor" to load the cursor from a file.
command:
This specifies an action to be taken when a particular widget is "invoked" or when values associated with the widget change. Typically this will be a Dylan function, but it may also be a bit of Tcl code which can be executed directly by the interpreter. The number of parameters which might be passed to the function vary depending on the widget, but they will all be passed in as <string>s.
font:
This is a string which specifies the font to be used for any text which is displayed within the widget.
state:
This specifies whether the given widget is in an "active" state. Typically, any widget which accepts a "command:" keyword will accept this keyword as well. Acceptable values are "normal", "disabled", and sometimes "active" (for buttons only).
text:
This is a string which will be displayed in the widget in an implementation dependent way. A few widgets also take a "label:" keyword which acts much the same way.
textvariable:
This specifies a "variable" containing a value which will be displayed in the widget. If the variables value changes, the widget is updated. If the widget allows editing, the variable will be updated after each change. Dylan/Tk programs will typically supply a <tk-variable> object for this keyword. (These will be explained in a later section.)
variable:
This functions much like the "textvariable:" keyword, but tends to be displayed somewhat differently.

Windows

The abstract type <window> represents any one of a wide variety of widgets, although it doesn't include pseudo-widgets like menu entries, text tags, or canvas items (which will be discussed later). They are almost always created via "make", which accepts all of the standard options described above, as well as the "packer" options "name:", "in:", "after:", "before:", "side", "expand:", "padx:", "pady:", and "pack:". (Packing will be described briefly in the next section.) All windows except <toplevel>s must specify their parents by some means -- either directly via "in:" or indirectly via "after:" or "before:".

You also have the right to specify a "debug name" for the window via the "name:" option. If you refuse this right, one will be created for you automatically.

One window is automatically created as a result of importing the "tk" library. This is the top level window "*root-window*".

There are a small set of methods which operate upon all windows:

function configure (widget :: <window>, #all-keys);
Configure accepts pretty much the same options as initialize, and uses them to change the state of the object.

function configuration (widget :: <window>) => (result :: <sequence>);
Returns a complete list of options for the given widget. Each option consists of a sequence of the switch name, rdb name, rdb class, default, and value. The "value" will be a string, which may be converted to a more pleasing form via either "tk-as" or "tk-unquote".

function map-window (window :: <window>) => ();
Make sure that a window is displayed on the screen. *root-window* is unmapped initially, so nothing will appear until you call "map-window(*root-window*)".

function unmap-window (window :: <window>) => ();
Remove a window from the display. This is handy to keep from showing intermediate states of newly defined windows.

function destroy-window (window :: <window>) => ();
Completely obliterate a window, removing it from the screen and all knowledge of mankind.

function pack (window :: <window>, #all-keys) => (window :: <window>);
This is explained in the next section. You should never need to call pack unless you either specified "pack: #f" when making the window or you used the "unpack" function to remove it from the display.

function unpack (window :: <window>) => (window :: <window>);
This hides a window which has previously been packed into another window. The window still exists, and may be updated or repacked into its parent. You may not pack the window into a different parent -- this will result in errors.

Packing

Unless they are some sort of top level window (i.e. *root-window* or instances of <toplevel> or <menu>), windows must be "packed" into some containing window before they will appear on the screen. The Dylan/Tk "pack" function closely follows the Tcl/Tk function described in "Tcl and the Tk Toolkit", and you should look there for more complete information. However, we will provide a brief overview here.

Each "slave" (i.e. non-top-level) window must be packed into some other window. You specify this window either directly, via "in: parent" or indirectly via "before: sibling" or "after: sibling". (The latter options specify that the window should have the same parent as the specified "sibling", and that they should be placed just before or after the sibling in the parent's "packing order".) If no parent is specified explicitly, Dylan/Tk implicitly assumes "in: *root-window*".

The "side:" option specifies where the widget should be placed relative to the ones which follow it in the "packing order". If you specify "side: "left"", then all widgets which are packed into the parent after this one (either because they were created later or because of a "before:" or "after:" option) will be placed somewhere to the right of this widget. Note that it will not necessarily be placed on the parent's absolute left side, because all windows which precede this one in the packing order have "first dibs" on prime real estate. Possible values for this option are "left", "right", "top", and "bottom" -- the default is "left".

If you were to execute:

  let w1 = make(<listbox>, in: *root-window*, side: "left");
  let w2 = make(<text>, before: w1, side: "top");
  let w3 = make(<scrollbar>, after: w1);
you would end up with something like:
  +++++++++++++++++
  |       w2      |
  +++++++++++++++++
  |      w1     |w|
  |             |3|
  +++++++++++++++++
Window W2 comes first in the packing order (because of the "before:" option) and preempts the top of the root window. W1 comes second, and grabs the left side of the remaining space, leaving a small chunk on the right for W3 (and any windows which might be packed after W3).

The other options available are "anchor:", "expand:", "fill:", "padx:", and "pady:", which are explained in the book. "Expand:" typically takes a boolean value, while "fill:" takes one of "x", "y", "both", or "none".

The "unpack" function will remove a window from the control of the "packer" and, as a side effect, remove it from the display. The window will still exist and can be updated or repacked via the "pack" function. You will, however, get errors if you try to specify a different parent when you repack it.

Event bindings

Each Dylan/Tk widget has a default set of "bindings" which determine how they react to X events like button or key presses. These may be changed or extended via the "bind" function.
  function bind (window, event :: <string>, command) => (window);
This function specifies an action to be performed when an X event matching "event" occurs. Typically this is a nil-adic Dylan function, but it may also be an arbitrary line of Tcl code. A typical binding might be the following:
  bind(l1, "<Double-Button-1>",
       method () do(print-config, current-selection(l1)) end method);
This would cause Dylan/Tk to call "print-config" on each of the current selections in the <listbox> "l1" whenever the leftmost mouse button is double-clicked.

There are two other utility functions which allow you to determine the bindings for a particular window:

function get-binding (window :: <window>, event) => (result :: <string>);
Returns the window's binding for a single event. Unless you have specified an explicit binding, this will be an empty string.

function get-bindings (window :: <window>) => (result :: <sequence>);
Returns a complete list of explicit bindings for a window. Each element of the sequence will be a <pair> of event-name and binding.

Active Variables

Some widgets have the ability to take an obsessive interest in the value of some "variable". They may update their contents or appearance when the variable's value changes, or they might update the variable in response to some user action. Because this level of monitoring is not supported by ordinary Dylan variables, Dylan/Tk users must instead use instances of <active-variable>.

"Make" on "<active-variable>" requires a "value:" keyword and accepts an optional "class:" keyword. If specified, the class defines the logical type of the variable's value. The "value" slot will always contain a value of that type, although "value-setter" will accept arbitrary types (especially <string>) and attempt to convert them to the given type.

You may also specify a Dylan function to be executed when an active variable's value changes (i.e. becomes \~= to the old value). This method is specified via the "command:" keyword to "make". It will be invoked with both the new and the old value at some point after the value is changed. Note that the value is updated immediately (i.e. asynchronously), while the calls to the given command are executed sequentially along with event callbacks.

Active variables will typically be passed as values of "variable:" or "text-variable:" keywords.

The widget types

Dylan/Tk provides types corresponding to all of the standard Tcl/Tk widgets:

<button>:
Options: #"activebackground", #"activeforeground", #"bitmap", #"command", #"disabledforeground", #"font", #"height", #"state", #"text", #"textvariable", #"width".
  function activate (button) => button;
  function deactivate (button) => button;
  function invoke (button) => button;
<checkbutton>:
Options: #"activebackground", #"activeforeground", #"bitmap", #"command", #"disabledforeground", #"font", #"height", #"offvalue", #"onvalue", #"selector", #"state", #"text", #"textvariable", #"variable", #"width".
  function activate (button) => button;
  function deactivate (button) => button;
  function select-value (button) => button;
  function deselect-value (button) => button;
  function toggle-value (button) => button;
<menubutton>:
Options: #"activebackground", #"activeforeground", #"bitmap", #"disabledforeground", #"font", #"height", #"menu", #"state", #"text", #"textvariable", #"underline", #"width".
  function activate (button) => button;
  function deactivate (button) => button;
<radiobutton>:
Options: #"activebackground", #"activeforeground", #"bitmap", #"command", #"disabledforeground", #"font", #"height", #"selector", #"state", #"text", #"textvariable", #"value", #"variable", #"width".
  function activate (button) => button;
  function deactivate (button) => button;
  function select-value (button) => button;
  function deselect-value (button) => button;
<canvas>:
Options: #"closeenough", #"confine", #"height", #"insertbackground", #"insertborderwidth", #"insertofftime", #"insertontime", #"insertwidth", #"scrollincrement", #"scrollregion", #"selectbackground", #"selectborderwidth", #"selectforeground", #"width", #"xscrollcommand", #"yscrollcommand".
  function xview (canvas :: <canvas>, index) => canvas;
  function yview (canvas :: <canvas>, index) => canvas;
  function focus (canvas :: <canvas>) => result :: <canvas-item>;
  function focus-setter (value :: <canvas-item>, canvas :: <canvas>) => ();
  function scan-mark (window :: <canvas>, #rest coords) => window;
  function scan-dragto (window :: <canvas>, #rest coords) => window;
  function select-item (window :: <canvas>, index) => window;
  function create-arc (canvas, x1, y1, x2, y2, #rest opts) => item;
    Options include: #"extent", #"fill", #"outline", #"start", #"stipple",
    #"style", #"width".
  function create-bitmap (canvas, x1, y1, #rest opts) => item;
    Options include: #"anchor", #"bitmap".
  function create-line (canvas, points :: <sequence>, #rest opts) => item;
    Options include: #"arrow", #"arrowshape", #"capstyle", #"fill",
    #"joinstyle", #"smooth", #"splinesteps", #"stipple", #"width".
  function create-oval (canvas, x1, y1, x2, y2, #rest opts) => item;
    Options include: #"fill", #"outline", #"stipple", #"width".
  function create-polygon (canvas, pnts :: <sequence>, #rest opts) => item;
    Options include: #"fill", #"smooth", #"splinesteps", #"stipple".
  function create-rectangle (canvas, x1, y1, x2, y2, #rest opts) => item;
    Options include: #"fill", #"outline", #"stipple", #"width".
  function create-text (canvas, x1, y, #rest opts) => item;
    Options include: #"anchor", #"fill", #"font", #"justify", #"stipple",
    #"text", #"width".
  function create-window (canvas, x1, y1, #rest opts) => item;
    Options include: #"anchor", #"height", #"width", #"window".
  function postscript (canvas, #rest opts) => result :: <string>;
    Options include: #"colormap", #"colormode", #"fontmap", #"height",
    #"pageanchor", #"pageheight", #"pagewidth", #"pagex", #"pagey",
    #"rotate", #"width", #"x", #"y".
<entry>:
Options: #"exportselection", #"font", #"insertbackground", #"insertborderwidth", #"insertofftime", #"insertontime", #"insertwidth", #"scrollcommand", #"selectbackground", #"selectborderwidth", #"selectforeground", #"state", #"textvariable", #"width".
  function icursor (entry, index) => entry;
  function view (entry, index) => entry;
  function delete (entry, index, #key end: last) => entry;
  function insert (entry, index, #rest elements) => entry;
  function get-all (entry) => result :: <string>;
  function get-elements (entry, index, #key end) => result :: <string>;
  function scan-mark (entry, #rest coords) => entry;
  function scan-dragto (entry, #rest coords) => entry;
  function select-adjust (entry, index) => entry;
  function select-clear (entry) => entry;
  function select-from (entry, index) => entry;
  function select-to (entry, index) => entry;
<frame>:
Options: #"geometry", #"height", #"width".

Supports no operations.

<label>:
Options: #"bitmap", #"font", #"height", #"text", #"textvariable", #"width".

Supports no operations.

<listbox>:
Options: #"exportselection", #"font", #"geometry", #"selectbackground", #"selectborderwidth", #"selectforeground", #"setgrid", #"xscrollcommand", #"yscrollcommand".
  function current-selection(listbox, #rest rest) => indices :: <sequence>;
  function nearest(listbox, y-coord) => index :: <integer>;
  function size(listbox) => result :: <integer>;
  function xview(listbox, index) => listbox;
  function yview(listbox, index) => listbox;
  function delete (listbox, index, #key end) => listbox;
  function insert (listbox, index, #rest elements) => listbox;
  function get-all (listbox) => result :: <string>;
  function get-elements (listbox, index, #key end) => result :: <string>;
  function scan-mark (listbox, #rest coords) => listbox;
  function scan-dragto (listbox, #rest coords) => listbox;
  function select-adjust (listbox, index) => listbox;
  function select-clear (listbox) => listbox;
  function select-from (listbox, index) => listbox;
  function select-to (listbox, index) => listbox;
<menu>:
Options: #"activebackground", #"activeborderwidth", #"activeforeground", #"disabledforeground", #"font", #"postcommand", #"selector"
  function activate-entry (menu, index) => menu;
  function delete (menu, index, #key end) => menu;
  function disable-entry (menu, index) => menu;
  function enable-entry (menu, index) => menu;
  function configure-entry (menu, index, #rest options) => menu;
  function entry-configuration (menu, index) => result :: <sequence>;
  function invoke-entry (menu, index) => result;
  function post (menu, x, y) => ();
  function unpost (menu) => ();
  function yposition-entry (menu, index) => result :: <integer>;  
  function add-command (menu, #key state, command, label) => ();
  function add-checkbutton (menu, #key variable, label) => ();
  function add-radiobutton (menu, #key variable, value, label) => ();
  function add-cascade (menu, #key menu, label) => ();
  function add-separator (menu) => ();
Note that if you wish to assocaiate a menu with a menubutton, you must create it "in:" that button.

<message>:
Options: #"aspect", #"font", #"justify", #"text", #"textvariable", #"width".

Supports no operations.

<scale>:
Options: #"activeforeground", #"command", #"font", #"from", #"label", #"length", #"orient", #"showvalue", #"sliderforeground", #"sliderlength", #"state", #"tickinterval", #"to", #"width".
  function get-value (scale) => result :: <integer>;
  function set-value (scale, value) => scale;
<scrollbar>:
Options: #"activeforeground", #"command", #"orient", #"repeatdelay", #"repeatinterval", #"width".
  function scroll (widget, #key, #all-keys) => scrollbar;
  function get-units (scrollbar) => (#rest units :: <integer>);
  function set-units (scrollbar, #rest Units) => scrollbar;
The "scroll" function exists to reduce the inefficiency inherent in "standard" method of connecting widgets to scrollbars. This function creates a new scrollbar with the given orientation (i.e. "vertical" or "horizontal") and connects it via lower level mechanisms to the given widget. All of the applicable creation and packing options for scrollbars are accepted.

<text>:
Options: #"exportselection", #"font", #"height", #"insertbackground", #"insertborderwidth", #"insertofftime", #"insertontime", #"insertwidth", #"selectbackground", #"selectborderwidth", #"selectforeground", #"setgrid", #"state", #"textvariable", #"width", #"wrap", #"yscrollcommand".
  function yview (text, index) => text;
  function delete (text, index, #key end) => text;
  function insert (text, index, #rest elements) => text;
  function get-all (text) => result :: <string>;
  function get-elements (text, index, #key end) => result :: <string>;
  function scan-mark (text, #rest coords) => text;
  function scan-dragto (text, #rest coords) => text;
  function select-adjust (text, index) => text;
  function select-clear (text) => text;
  function select-from (text, index) => text;
  function select-to (text, index) => text;
<toplevel>:
Options: #"geometry", #"screen".

Supports no operations.

These operations will hopefully be described at greater length in future editions of this document, but for now you are encouraged to check the Tcl/Tk manual for further information.

Text indices

The types <text-index>, <text-mark>, and <text-tag> all represent references to the contents of a <text> window. Functions are provided to let you create and manipulate instances of these types.

<Text-index>s represent an "absolute" index within a <text> object. They have two slots -- "line" and "character". "Make" on <text-index> requires a "line:" keyword and accepts an optional "character:" keyword (which defaults to 0). Lines numbers start at 1, while characters start at 0, so you could

specify the first character of a <text> by calling 
  make(<text-index>, line: 1, character: 0)
The following functions create and manipulate <text-index>s:
  function text-at (line, character) => result :: <text-index>;
  function line-end (line) => result :: <string>;
  method as (cls == <text-index>, value :: <string>) => string
  method as (cls == <string>, value :: <text-index>) => text-index
The "text-at" function provides a convenient shorthand for making <text-index>s. You might, for example replace the above "make" call with
  text-at(1, 0);
"Line-end" creates a reference to the last character of the named line.

Text marks

<Text-mark> objects represent more abstract character positions. It will continue to refer to the same character even if other text is added or deleted. They have two slots -- "value" and "name", and are associated with a particular <text> widget via the required "make" keyword "in:".

If you make a <text-mark> with the name of a "standard" Tcl/Tk mark, such as "end" or "insert", then the new object will pick up the values already associated with the Tcl/Tk mark.

The following functions create or manipulate <text-mark>s:

  method as (cls == <string>, object :: <text-mark>) => string
  method as (cls == <text-index>, mark :: <text-mark>) => text-index
  method marks (text :: <text>) => result :: <sequence>;
The "marks" function returns a sequence containing all of the marks within a given <text>. This will include both the "standard" Tcl/Tk marks and any that you have created. These will not necessarily be "==" to the original objects.

Text tags

<Text-tag>s represent attributes which may be associated with 0 or more sequences of characters in a specific <text>. You may specify different display properties or event bindings for the characters that have been associated with a particular <text-tag>.

<Text-tag>s are created within a particular <text> via "make", which requires an "in:" keyword. Optional keywords include "name:", "bgstipple:", "fgstipple:", "font:", and "underline:". Although tags are not widgets, they support the "configure", "configuration", and "bind" functions. You may add ranges of characters to <text-tag>s via "add-tag". A complete list of functions upon <text-tag>s follows:

  function configure (tag, #rest options) => tag;
  function configuration (tag, index) => result :: <sequence>;
  function bind (tag, event :: <string>, command) => tag;
  method as (cls == <string>, object :: <text-tag>) => string;
  function add-tag (tag, #key start, end: last) => tag;
  function remove-tag (tag, #key start, end: last) => tag;
  function tags (text :: <text>) => result :: <sequence>;
  function delete-tag (tag) => ();
  function raise-tag (tag, #key past :: <text-tag>) => ();
  function lower-tag (tag, #key past :: <text-tag>) => ();
  function next-range (tag, #key start, end) => result :: false-or(<pair>);

Canvas items

<canvas-item>s represent different graphical entity within a <canvas>. Each "create-shape" function returns a <canvas-item> corresponding to the newly created shape. These may be used to delete, resize, move, or respecify the shape. Although canvas items are not widgets, they support the "configure", "configuration" and "bind" functions. The configuration options which are accepted for a given item depend upon its type. You should consult the list of "create-..." functions for a list of optoins.

The following functions operate upon canvas items:

  function configure (item, #rest options) => item; 
  function configuration (item, index) => result :: <sequence>;
  function bind (item, event :: <string>, command) => item;
  function delete-item (item) => ();
  function raise-item (item, #key past :: <canvas-item>) => ();
  function lower-item (item, #key past :: <canvas-item>) => ();
  function move-item (item, x :: <object>, y :: <object>) => ();
  function scale-item (item, x-origin, y-origin, x-scale, y-scale) => ();
  function item-coords (item) => result :: <sequence>;
  function item-coords-setter (value :: <sequence>, item) => ();
  function item-type (item) => result :: <string>;

Requested Enhancements

The Extension Protocol

Because early versions of Dylan/Tk don't support the entire functionality of Tcl/Tk, users may wish to interface directly with the underlying Tcl/Tk interpreter. This capability is provided by the Tk-extension module. (Note: if any user decides to use these capabilities to provide a firm interface to a Tcl/Tk subsystem (e.g. the "wm" command), we would willingly incorporate such code into future versions of this library.)

The following functions are exported:

generic funtion tk-as (cls :: <class>, value) => result :: <object>;
Performs conversions comparable to "as", but adds some which do not generalize outside tk -- for example, tk-as(<boolean>, "1") == #t.

You may wish to add methods to this function for any new dylan types you add.

function put-tk-line (#rest pieces) => ();
Converts all of the input arguments into strings (using "tk-as"), concatentates them into a line of tk input, and sends then to the running tk process. This routine expects no results and therefore does not wait around for them.

You should be careful to include spaces between distinct tokens in the input, since "put-tk-line" will cheerfully combine several strings into a single Tk token.

function call-tk-function (#rest pieces) => result :: <string>;
Concatenates the input into a tk command, executes it, and returns the result as a string. Pauses execution of the current thread until the tk function returns.

You may need to call "tk-as", "tk-unquote", or "parse-tk-list" on the result depending upon the expected result type.

function join-tk-args (#rest object) => result :: <string>;
Creates a string containing the tk representation (via "tk-as") of each argument, separated by spaces.

function make-option (option, value) => option :: <string>;
Creates a tk "option" switch out of option name and an arbitrary value. The value may be an <integer>, <string>, <symbol>, or <true>. If the option is #f, then an empty string (i.e. no option) will be returned.

function parse-tk-list (string, #key depth, unquote, start, end) => sequence;
Takes a string returned by a tk function and converts it into a sequence of values. Values will either be 'words' or subsequences, depending upon whether the string contains list grouping (i.e. '{' and '}') characters. If depth is not #f, all groupings below the given depth will be returned as simple strings. If "unquote" is true, then result strings will be filtered through "unquote".

function tk-quote (object :: <object>) => result :: <string>;
Performs whatever quotation is necessary to get the data to WISH intact. For most objects, this is the same as "tk-as(<string>, ...)", but it does extra quotation on strings to make sure brackets and such like come out OK.

function tk-unquote (string :: <string>) => result :: <string>;
Strips away all quotation from a string. This will undo the various escapes required to imbed brackets and such like inside a tk list. This should be called for any user level string, just as "tk-as" might be called for any other data type.

function anonymous-name (#key prefix = "w") => result :: <string>;
Generates a unique new (but unexciting) name for a window.