Writing Tcl-Based Applications In C

advertisement
Programming Using Tcl/Tk
Week 5
Seree Chinodom
seree@buu.ac.th
http://lecture.compsci.buu.ac.th/TclTk
First Things First

Homework #2
– Some good work

Project: Hand them in, if you've got 'em

To have your project returned, send SASE to:
Ernest Friedman-Hill
1206 Caribou Place
Davis, CA 95616

Grading method choice
– CLG: Credit, letter grade
– CP/NP: Credit / No Credit (Pass / Fail)
– NC: Not for credit
What We'll Do Today

Extending Tcl with C

A look at some third-party Tcl extensions I use
– Expect
– BLT
– TclDii

The future of Tcl/Tk
Extending Tcl: Outline

Philosophy: focus on primitives.

Basics: interpreters, executing scripts.

Implementing new commands.

Managing packages; dynamic loading.

Managing the result string.

Useful library procedures: parsing, variables, lists,
hash tables.
Philosophy

Usually better to write Tcl scripts than C code:
– Faster development (higher level, no compilation).
– More flexible.

Why write C?
– Need access to low-level facilities (hardware).
– Efficiency concerns (iterative calculations).
– Need more structure (code is complex).

Implement new Tcl commands that provide a few simple
orthogonal primitives:
– Low-level to provide independent access to all key features.
– High-level to hide unimportant details, allow efficient
implementation.
Example: Weather Reports

Goal: retrieve weather reports over network from servers.

Tcl command set #1:
– Retrieve report, format, print on
standard output.


Tcl command set #2:
– Open socket to weather server.
– Select station.
– Retrieve first line of report....
Tcl command set #3:
– Return list of available stations.
– Given station name, retrieve report.
Too high-level (inflexible)
Too low-level (tedious)
Just right
Designing New Commands

Choose textual names for objects:
.dlg.bottom.ok
file3 or stdin
– Use hash tables to map to C structures.

Object-oriented commands:
.dlg.bottom.ok configure -fg red
– Good for small numbers of well-defined objects.
– Doesn't pollute name space.
– Allows similar commands for different objects.

Action-oriented commands:
string compare $x $y
– Good if many objects or short-lived objects.
Designing New Commands, cont'd

Formatting command results:
– Make them easy to parse with Tcl scripts:
tmp 53 hi 68 lo 37 precip .02 sky part
– Make them symbolic wherever possible, e.g. not
53 68 37 .02 7

Use package prefixes in command names and global
variables:
wthr_stations
wthr_report
midi_play
– Allows packages to coexist without name clashes.
Interpreters

Tcl_Interp structure encapsulates execution state:
– Variables.
– Commands implemented in C.
– Tcl procedures.
– Execution stack.

Can have many interpreters in a single application
(but usually just one).

Creating and deleting interpreters:
Tcl_Interp *interp;
interp = Tcl_CreateInterp();
Tcl_DeleteInterp(interp);
Executing Tcl Scripts
int code;
code = Tcl_Eval(interp, "set a 1");
code = Tcl_EvalFile(interp, "init.tcl");
code = Tcl_VarEval(interp, "set ", a, " 1");

code indicates success or failure:
– TCL_OK: normal completion.
– TCL_ERROR: error occurred.

interp->result points to string: result or error message.

Application should display result or message for user.
Where Do Scripts Come From?

Read from standard input (see tclMain.c).

Read from script file (see tclMain.c).

Associate with X events, wait for events, invoke
associated scripts (see tkMain.c).

Embeddded in C code (fixed or private scripts)
Creating New Tcl Commands

Write command procedure in C:
int EqCmd(ClientData clientData, Tcl_Interp
*interp, int argc, char **argv) {
if (argc != 3) {
interp->result = "wrong # args";
return TCL_ERROR;
}
if (!strcmp(argv[1], argv[2]))
interp->result = "1";
else
interp->result = "0";
return TCL_OK;
}
Creating New Tcl Commands, cont'd

Register with interpreter:
Tcl_CreateCommand(interp, "eq", EqCmd,
(ClientData) NULL, ...);

Once registered, EqCmd will be called whenever eq
command is invoked in interp.

Command can be deleted later
Tcl_DeleteCommand(interp, "eq");
–This is most useful for 'object' commands
ClientData
Tcl_CreateCommand(interp, "eq", EqCmd,
clientData, ...);
int EqCmd(ClientData clientData, ...) {...}

Used to pass any one-word value to command
procedures and other callbacks.

clientData is usually a pointer to data structure
manipulated by procedure.

Cast pointers in and out of ClientData type:
– Tcl_CreateCommand(... (ClientData) gizmoPtr,
...);
– gizmoPtr = (Gizmo *) clientData;

Lets many object commands share one command proc
Conventions For Packages
Goal: make it easy to develop and use Tcl extensions.
1. Use package prefixes to prevent name conflicts:
– Pick short prefix for package, e.g. rdb.
– Use in all global names:
 C procedure: Rdb_Open
 C variable: rdb_NumRecords
 Tcl command: rdb_query
– See Tcl book and Tcl/Tk Engineering Manual for more
details.
Packages, cont'd
2. Create package initialization procedure:
– Named after package: Rdb_Init.
– Creates package's commands.
– Evaluates startup script, if any.
int Rdb_Init(Tcl_Interp *interp) {
Tcl_CreateCommand(...);
Tcl_CreateCommand(...);
...
return Tcl_EvalFile(interp,
"/usr/local/lib/rdb/init.tcl");
}
Packages, cont'd
3. To use package:
– Compile as shared library, e.g. on Solaris:
cc -K pic -c rdb.c
ld -G -z text rdb.o -o rdb.so
– Dynamically load into tclsh or wish:
load rdb.so Rdb
– Tcl will call Rdb_Init to initialize the package.

Note: this is new since the text was published; much nicer
than old static mechanism!
-Kpic: position independent code. -G: produce a shared object. undefined symbols allowed. -z text: force library to be read-only
The load command

Several forms
load filename
load filename packageName
load "" packageName
where
filename is libXYZ.so
packageName is XYZ

You can use info sharedlibextension
to decide between .dll, .so, etc.

You can use info loaded
to see what packages have been loaded
Packages, cont'd

You can also load packages statically:
Tcl_StaticPackage(Tcl_Interp interp,
char * pkgName,
Tcl_PkgInitProc *InitProc,
Tcl_PkgInitProc *safeInitProc)
…
then
load "" XYZ

Dynamic loading is to be preferred
Managing The Result String

Need conventions for interp->result:
– Permit results of any length.
– Avoid malloc overheads if possible.
– Avoid storage reclamation problems (leaks).
– Keep as simple as possible.

Normal state of interpreter (e.g., whenever command
procedure is invoked):
interp
result
freeProc
""

Default: command returns empty string.
Result String, cont'd

Option 1: (semi-) static result.
interp->result = "0";
interp
result
freeProc
"0"
""

Option 2: use pre-allocated space in interp.
sprintf(interp->result, "Value is %d", i);
interp
result
freeProc
"Value is 2"
~200 bytes
Result String, cont'd

Option 3: allocate new space for result.
interp->result = malloc(2000);
...
interp->freeProc = free;
interp
result
freeProc
free
""

Tcl will call freeProc (if not NULL) to dispose of
result.

Mechanism supports storage allocators other than
malloc/free (if they have the same signatures!)
Procedures For Managing Result

Option 4: use library procedures.
Tcl_SetResult(interp, string, ...);
replaces old value
Tcl_AppendResult(interp, string,
string, ... string, (char *) NULL);
extends old value
Tcl_AppendElement(interp, string);
extends old value, as a list
Tcl_ResetResult(interp);
clears old value
Utility Procedures: Parsing

Used by command procedures to parse arguments:
int value, code;
code = Tcl_GetInt(interp, argv[1], &value);

Stores integer in value.

Returns TCL_OK or TCL_ERROR.

If parse error, returns TCL_ERROR and leaves message
in interp->result.
Parsing (cont'd)

Other procedures:
Tcl_GetDouble Tcl_ExprDouble
Tcl_GetBoolean Tcl_ExprBoolean
Tcl_ExprLong
Tcl_ExprString

Tcl_GetBoolean accepts 'yes', 'false', 1, 0…

Expr- variations interpret argument as an expression
Utility Procedures: Variables

Read, write, and unset:
char *value;
value = Tcl_GetVar(interp, "a", ...);
Tcl_SetVar(interp, "a", "new", ...);
Tcl_UnsetVar(interp, "a", ...);

Set traces:
Tcl_TraceVar(interp, "a",
TCL_TRACE_READS|TCL_TRACE_WRITES,
traceProc, clientData);

traceProc will be called during each read or write of a:
– Can monitor accesses.
– Can override value read or written.
Other Utility Procedures

Parsing, assembling proper lists:
Tcl_SplitList(...)
Tcl_Merge(...)

Flexible hash tables:
Tcl_InitHashTable(...)
Tcl_CreateHashEntry(...)
Tcl_FindHashEntry(...)
Tcl_DeleteHashEntry(...)
Tcl_DeleteHashTable(...)
–Like having Tcl associative arrays in C
–Excellent way to store clientData records for 'objectoriented' command model
Other Utilities (cont'd)

Dynamic strings:
Tcl_DStringInit(...)
Tcl_DStringAppend(...)
Tcl_DStringAppendElement(...)
Tcl_DStringValue(...)
Tcl_DStringFree(...)
– Dynamic strings grow efficiently without bounds
– Like a good String class in C++
– Used internally by Tcl for result management, etc
Extending Tcl Summary

Interfaces to C are simple: Tcl was designed to make
this true.

Focus on primitives, use Tcl scripts to compose fancy
features.
Writing a Widget for Tk

Good News: One widget can work on UNIX, Win32,
Mac
– Tk port uses port of Xlib to support graphics

Bad News: You still have to write to Xlib!
Widgets
Tk
Xlib
Tk only uses Xlib, the
lowest-level part of the
X libraries. It doesn't
use Xt, Athena, Motif,
etc. To draw, you have
to use Xlib, too.
Writing a Widget for Tk

Tk supplies utilities
– 3D outline drawing
– Font utilities
– Event notification, timers, idle procs

You supply
– Tcl commands
– Initialization procedure
– Configuration table
– Event handlers
– Deletion callbacks

Use object-oriented command paradigm
– One Tcl command per widget class
– One Tcl command per widget instance
Expect

Terrific for creating GUIs for interactive commandline apps

Terrific for reacting to prompts from command-line
apps

Expect can
– Automate tedious interactive processes
– Glue GUI actions to simulated keyboard outputs
– Reattach subprocess to keyboard

Works only on true POSIX (UNIX) systems
– Uses pseudottys (ptys), a UNIX construct
Expect (cont'd)

Commands:
spawn: like background exec, but opens two-way pipe
spawn telnet herzberg 80
expect: responds to patterns from pipe
expect {
"connection refused"
exit
"unknown host"
complain DNS
login:
login_proc
default
interact
}
interact: reconnect two-way pipe to console
BLT

Several 'megawidgets'

blt_graph is most useful

Other commands:
blt_htext
blt_drag&drop
blt_barchart
blt_table
BLT Graph Widget
blt_graph .g
-height 175 -width 200 -font \
"-*-times-medium-r-normal-*-*-80-*-*-*-*-*-*"
.g
legend configure -font \
"-*-times-medium-r-normal-*-*-80-*-*-*-*-*-*" \
-anchor nw -position "@65,10" -relief ridge
.g
yaxis configure -title ""
.g
xaxis configure -title "Time (sec)"
.g element create "Series1" -xdata $x1 -ydata $y1 \
-linewidth 1 -fg red -bg blue -label "Velocity"
.g element create "Series2" -xdata $x2 -ydata $y2 \
-linewidth 1 -fg green -bg blue -label "Stroke"
TclDii

Lets Tcl/Tk talks to CORBA objects

CORBA lets objects written in different languages
communicate

Tk GUIs for remote objects are easy and powerful

TclDii test scripts faster and easier than C++ code
A Look Ahead
Tcl 7.5p1
Tk 4.1p1
Tcl 7.6
Tk 4.2
Grid manager revisions for
use by SpecTcl.
Tcl 7.5
Tk 4.1
Tcl 7.6plug
Tk 4.2plug
Embedding, other features
for Netscape plugin.
Tcl 7.7
Tk 4.3
Native look and feel
for PC and Mac.
Tcl 8.0
Tk ?.?
Bytecode compiler.
Tcl 7.6 and Tk 4.2

Major revision of gridder, needed for SpecTcl FCS.

C API change for channel drivers (eliminate
Tcl_File usage).

No other changes except bug fixes.

Now in Beta, final release any day now.
Tcl 7.6plug and Tk 4.2plug

Features needed for Netscape plugin:
– Application embedding:
frame .x.y -container 1
toplevel .t -use [winfo id .x.y]
wish -use 0x6800004
– Focus revision.
– Safe-Tk, security models.

Already released in plugin binaries.

No source release until Tcl 7.7/Tk4.3.
Tcl 7.7 and Tk 4.3



Native widgets on PC and Mac.
New menu mechanism:
– Menu bars implemented with menu widgets.
– New -menu option for toplevel windows.
– Tear-off menus are clones (consistency guaranteed).
– Can use same menu in several places.
– Hide tear-off entries?
New font mechanism: (nice for non-UNIX!)
label .l -font {times 12 bold}
font create title -family Helvetica \
-size 24 -weight bold
label .l2 -font title
font configure title -size 36
Tcl 7.7 and Tk 4.3, cont'd




Virtual bindings, event generation:
event add <<Paste>> <Control-v>
bind Entry <<Paste>> {...}
event generate .t ButtonPress \
-button 1 -x 47 -y 18
Standard dialogs.
Portable file commands: rm, mv, mkdir, rmdir, ...
Binary I/O support:
– Read/write blocks of data.
– Insert/extract fields of blocks.
– Copy between channels.
Tcl 7.7 and Tk 4.3, cont'd

Other possible additions:
– Megawidgets.
– Image revisions:




No dithering.
Better memory usage.
Channel support.
...
– More text improvements (smooth scrolling, etc.).

Schedule estimate:
– First alpha release October-November 1996.
– Final release Q2 1997.
Tcl 8.0

Byte-code compiler (see ftp.neosoft.com for 3rd-party
attempts)

New object system for data representation.

New C APIs for command procedures, many other Tcl
library procedures (old interfaces still supported, but
slow).

Namespaces?
Tcl 8.0, cont'd


Possible changes in semantics:
– More aggressive list syntax checking, conversion:
lappend, lindex.
– Changes to error messages, errorInfo (line numbers?).
Schedule estimate:
– First alpha release October 1996.
– Final release Q2 1997?
– Additional speedups in later releases.
Other Projects

SpecTcl and SpecJava.

WebEdit, microscripting:
– Edit Web pages WYSIWYG with WebEdit.
– Insert Tcl/Tk scripts at various places.
– Scripts fire at interesting times:
 Site configuration time.
 Page fetch (CGI).
 On client machine (via plugin).

Safe-Tcl:
– Better Tk support, socket communication.
– Develop interesting security policies.
– Add authentication, MD5 (signatures).
Download