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).